Skip to content

Hosted yarn classic scan --vex attests not_affected when an npm: alias copy of the patched package was skipped, while standalone vex refuses the same lock #1081

Description

[agent] Found by the scheduled Yarn classic (1.x) bug-hunt routine (ledger #304).

Summary

Take a yarn 1.22.22 project that depends on the patched package directly and through an npm: alias ("left-pad": "1.3.0", "x": "npm:left-pad@1.3.0"). yarn 1.22.22 writes two lock blocks, left-pad@1.3.0: and "x@npm:left-pad@1.3.0":.

A hosted scan pins the direct block and leaves the alias block alone, with redirect_yarn_classic_alias_skipped (documented). After the next install, node_modules/x is the unpatched upstream copy.

  • Standalone vex sees this. It warns patched_ref_unattributable ("that copy stays UNPATCHED and nothing is attested") and exits 2 with nothing written, both lock-only and after the install.
  • The in-run scan --mode hosted --vex carries the same patched_ref_unattributable warning in vex.warnings[], yet writes a not_affected statement for pkg:npm/left-pad@1.3.0.

So the run's own envelope says "nothing is attested" next to a document that attests.

Impact

CI that runs scan --mode hosted --vex publishes a not_affected OpenVEX statement for a vulnerability whose code still ships unpatched under the alias name. The scan exits 0.

Repro (Linux, main 05ecc6e, yarn 1.22.22, local mock patch API on :8787)

mkdir p && cd p
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0","x":"npm:left-pad@1.3.0"}}' > package.json
yarn install
socket-patch scan --mode hosted --vex in.json --json --yes --api-url http://127.0.0.1:8787 --org o --api-token x > s.json
#  → exit 0; s.json: redirect.warnings = [redirect_yarn_classic_alias_skipped]
#                     vex.warnings    = [patched_ref_unattributable … "nothing is attested"]
#    in.json: one statement, pkg:npm/left-pad@1.3.0 "not_affected"
socket-patch vex --output v.json ...     # → exit 2, manifest_not_found, same patched_ref_unattributable warning, nothing written
rm -rf node_modules && yarn install --frozen-lockfile
head -c 30 node_modules/left-pad/index.js   # patched
head -c 30 node_modules/x/index.js          # UNPATCHED

A file:./left-pad-1.3.0.tgz direct dep instead of the registry one behaves the same way.

Expected vs actual

Matrix (Linux, Node 22; each cell twice)

yarn lock shape in-run scan --vex standalone vex (lock-only / post-install) node_modules/x
1.22.22 two blocks not_affected refuses, exit 2 unpatched
1.22.22, file: tarball + alias two blocks not_affected refuses, exit 2 unpatched
1.10.1 / 1.19.0 one merged block left-pad@1.3.0, "x@npm:left-pad@1.3.0":, pinned as a whole not_affected — patched (correct)

In my runs, every release from 1.0 to 1.22.21 (checked 1.12.3, 1.17.3, 1.19.0, 1.21.1, 1.22.0/4/10/15/17/19/21) merges the two keys into one block. Only 1.22.22, the current latest and corepack's default, writes them separately.

Suspect code

crates/socket-patch-cli/src/commands/scan/hosted.rs:1337: params.assume_applied drops the uuids in rewrite.bundled_skipped_uuids (#469), but not those whose copy the rewriter skipped as an alias (redirect_yarn_classic_alias_skipped). So the confirmed purl is attested without verification, although vex/discover flags it patched_ref_unattributable. Berry's redirect_yarn_berry_alias_skipped may take the same path (not checked).

Related: #828 (the same lock state also can't be unwound by rollback / remove; commented there).

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions