Reject path-traversal entries in binlog embedded files#14208
Reject path-traversal entries in binlog embedded files#14208jankratochvilcz wants to merge 6 commits into
Conversation
Guard against zip-slip when reading binlog embedded project imports: reject any archive entry whose path contains a '..' traversal segment. Legitimate binlogs store rooted, colon-stripped paths and never contain '..', so this fails fast on maliciously crafted logs without false positives. Adds unit tests for the IsPathTraversal helper. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR hardens MSBuild binary log replay by rejecting embedded archive entries that contain directory-traversal (..) path segments, preventing “zip-slip” style attacks when consuming embedded files from a potentially malicious .binlog.
Changes:
- Add a path traversal guard when reading embedded archive entries in
BuildEventArgsReader. - Add a new localized resource string for the rejection error message.
- Add unit tests for the traversal detection helper.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Build/Resources/xlf/Strings.zh-Hant.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.zh-Hans.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.tr.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.ru.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.pt-BR.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.pl.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.ko.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.ja.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.it.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.fr.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.es.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.de.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/xlf/Strings.cs.xlf | Adds localized resource entry for the new path-traversal rejection message. |
| src/Build/Resources/Strings.resx | Adds the new Binlog_PathTraversalInEmbeddedFile resource string. |
| src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs | Rejects embedded archive entries containing traversal segments; adds IsPathTraversal helper. |
| src/Build.UnitTests/BinaryLogger_PathTraversal_Tests.cs | Adds unit coverage for traversal detection logic. |
|
Caution agentic threat detected MSBuild Target Authoring Conventions — LGTM This PR modifies
|
|
Caution agentic threat detected Design-level notesMissing issue reference / PR description — For a security fix addressing a zip-slip attack vector, the PR has no description, no linked issue, and no reference to a CVE or GHSA. This makes it impossible to assess whether backporting to servicing branches is needed and prevents coordinating a security advisory if one is warranted. Please link or open a tracking issue.
// EmbeddedContentRead receives raw ZIP bytes; subscribers must not
// extract entries by FullName without validating against IsPathTraversal.
|
There was a problem hiding this comment.
Caution
agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.
Error Message Quality
Error Message Quality — ISSUE
SEVERITY: MODERATE
FILE: src/Build/Resources/Strings.resx
LINES: Binlog_PathTraversalInEmbeddedFile entry
SCENARIO: A user or tooling encounters this exception — e.g. a build system parses
MSBuild diagnostic output expecting MSBxxxx codes to look up documentation,
file issues, or configure error suppression. This message has no code,
while the adjacent binlog messages (MSB4279, MSB4280, MSB4281) all do.
Additionally, the call site uses FormatResourceStringStripCodeAndKeyword,
which is the right helper for resources *with* a code — using it on a
codeless string is a harmless mismatch but signals the code was intended
to follow the code convention.
FINDING: The resource string is missing an MSBxxxx error code. This breaks
consistency with peer binlog errors, prevents code-based documentation
lookup, and makes the error untargetable by tooling (e.g. /WarnAsError:MSBxxxx,
suppression lists, telemetry grouping by code).
RECOMMENDATION: Assign the next available MSB4xxx code (check the bottom of
Strings.resx for the next-available comment), prefix the value string
with it, e.g.:
<value>MSB4282: Embedded file path "{0}" contains a directory
traversal segment and was rejected as potentially malicious.</value>
No change needed at the call site — FormatResourceStringStripCodeAndKeyword
will then correctly strip and return the code alongside the message.
The message text itself is clear and actionable (states what happened and why). The only gap is the missing error code per the repository convention.
Generated by Expert Code Review (on open) for #14208 · 1.9K AIC · ⊞ 30.3K
Replace string.Split (which allocates a string[] plus a string per segment on every archive entry) with a ReadOnlySpan<char> scan that detects ".." segments without allocations on the binlog replay path. Also drops the redundant null-forgiving operator now that the scan no longer dereferences the string directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The user-facing exception for a rejected path-traversal entry was the
only binlog-reader error string without an MSBxxxx code. Prefix it with
MSB4282 (next available code) and mark the localized {StrBegin}. The
call site already uses FormatResourceStringStripCodeAndKeyword, which
strips the code automatically, so no call-site change is needed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
"Rooted paths with the drive colon stripped" is Windows-centric; on Linux/macOS CalculateArchivePath performs no colon removal. Broaden both the inline comment and the XML doc to describe the normalization in a platform-neutral way. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
For an Is* predicate, action-verb result segments ("Rejects"/"Allows")
are ambiguous about whether they describe the return value or a thrown
exception. Use the strict MethodUnderTest_Scenario_ExpectedResult form.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The existing tests validate IsPathTraversal in isolation; none exercised the actual guard in the embedded-content read loop. Add a test that crafts a binlog stream whose embedded archive contains a "../../escape.txt" entry, replays it through BuildEventArgsReader with ArchiveFileEncountered subscribed, and asserts an InvalidDataException is thrown before the entry is surfaced. This catches accidental removal of the security check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No description provided.