Skip to content

Vendored NuGet doesn't recognise a close tag with whitespace (</packageSources >, </packageSourceMapping >), so it appends a second section that NuGet ignores and every restore fails NU1100 / NU1403 while scan reports success and VEX attests #685

Description

[agent] Found by the scheduled NuGet / dotnet bug-hunt routine (ledger #320).

Summary

In vendored mode, the nuget.config writer (build_config_edit) and its key reader (parse_config_source_keys) look for section ends with literal find("</packageSources>") / contains("</packageSourceMapping>"). XML allows whitespace before the > of an end tag, and NuGet parses </packageSources > and </packageSourceMapping > fine. When the user's config uses either spelling, vendored decides the section doesn't exist and appends a second <packageSources> or <packageSourceMapping> element before </configuration>. NuGet reads only the first one, so:

  • </packageSources >: the Socket source sits in the ignored second section. The mapping routes Newtonsoft.Json exclusively to a source that doesn't exist, so every restore fails NU1100.
  • </packageSourceMapping >: the Socket mapping sits in the ignored second section. The id resolves from nuget.org, so with the re-pinned lock every restore (locked or plain) fails NU1403. Without a lock the unpatched package would install silently.

In both cases scan --mode vendored exits 0 with status: success and no warning, and the in-run --vex attests not_affected / inline_mitigations_already_exist.

Hosted mode already tolerates both spellings (patch/redirect/mod.rs builds </{section}\s*> and </configuration\s*> regexes, and the open tag <packageSources >), and its restores pass on the same fixtures. Only the vendored twin is affected.

Impact

A config that dotnet accepts is turned into one that can't restore, and every signal socket-patch gives says it worked: exit 0, success, and a VEX not_affected for a patch that never installs. The spelling is uncommon (hand-edited or tool-generated configs), so the reach is narrow, but the failure is silent at scan time.

Repro

This uses the wiremock stand-in from crates/socket-patch-cli/tests/e2e_nuget_dotnet_build.rs: fixture restore of Newtonsoft.Json 13.0.3, then scan --mode vendored --vendor-source service. Only nuget.config differs from the suite's REGISTRY_CONFIG:

<!-- A -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources >
</configuration>
<!-- B -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
  </packageSourceMapping >
</configuration>
dotnet restore --locked-mode            # baseline: Restored (both A and B)
socket-patch scan --mode vendored --vendor-source service --json --yes \
  --api-url $URI --org test-org --api-token fake --vex vex.json --vex-product pkg:nuget/app@1.0.0
# exit 0, status success, no warnings; vex.json: not_affected / inline_mitigations_already_exist
rm -rf obj; NUGET_PACKAGES=$(mktemp -d) dotnet restore --locked-mode
# A: error NU1100: Unable to resolve 'Newtonsoft.Json (>= 13.0.3)' for 'net8.0'. PackageSourceMapping is enabled, the following source(s) were not considered: nuget.org.
# B: error NU1403: Package content hash validation failed for Newtonsoft.Json.13.0.3.

Resulting config for A (the Socket source lands in a second, ignored section):

  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources >
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="socket-patch-<uuid>" value=".socket/vendor/nuget/<uuid>" />
  </packageSources>
  <packageSourceMapping> … nuget.org → * ; socket-patch-<uuid> → Newtonsoft.Json … </packageSourceMapping>

For B, a second <packageSourceMapping> is appended after the user's </packageSourceMapping >.

vendor --revert restores both configs byte-for-byte, so the unwind is fine.

Expected vs actual

  • Expected: per CLI_CONTRACT.md (vendored table, nuget row), vendored adds the nuget.config source plus the packageSourceMapping for the id, verified by dotnet restore --locked-mode on a cold cache. The Socket entries should land in the user's existing sections, as hosted mode does for the same files. If the config can't be edited, it should be refused rather than reported as success.
  • Actual: a duplicate section NuGet ignores, exit 0 success, VEX attests, and every restore fails (NU1100 / NU1403).

Matrix

OS SDK binary A </packageSources > B </packageSourceMapping > hosted (A and B)
Linux 8.0.131 main 045d7ec fail NU1100 (2/2) fail NU1403 (2/2) pass
Linux 8.0.131 v4.0.0 (npm) fail NU1100 fail NU1403 n/a

Not a regression; v4.0.0 behaves the same. macOS and Windows weren't probed (the writer is pure string handling, with no OS dependence).

Suspect code

  • crates/socket-patch-core/src/vendor/nuget_feed.rs:936: creating_mapping = !visible.contains("</packageSourceMapping>")
  • crates/socket-patch-core/src/vendor/nuget_feed.rs:974: visible.find("</packageSources>") falls through to the "create the section" branch
  • crates/socket-patch-core/src/vendor/nuget_feed.rs:987: same literal for the mapping anchor
  • crates/socket-patch-core/src/vendor/nuget_feed.rs:1069: parse_config_source_keys returns no keys, so nuget.org gets re-seeded as a duplicate <add>

The tokenizer refactor proposed in #594 would subsume this. This issue records the user-visible symptom, which #594 doesn't list.

No probe branch was used. Stale bughunt/nuget/* branches can't be deleted from the sandbox, so new probe branches are on hold; see ledger #320.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:nugetNuGet / dotnetpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions