Skip to content

Hosted yarn classic offline-mirror refusal misses a project .yarnrc or .npmrc saved with a UTF-8 BOM, so the scan pins anyway and every install fails #1078

Description

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

Summary

The #364 fix (#839) refuses a hosted yarn classic rewrite when the project sets yarn-offline-mirror, with redirect_yarn_classic_offline_mirror. The two readers behind that gate, yarnrc_value_of_offline_mirror and npmrc_value_of_offline_mirror, don't skip a leading UTF-8 BOM. On a file that starts with a BOM, the first line's key reads as \u{feff}yarn-offline-mirror, which never equals yarn-offline-mirror, so the mirror goes unseen. str::trim doesn't remove U+FEFF.

Yarn 1.10.1 and 1.22.22 do honour a BOM-prefixed .yarnrc and .npmrc: the initial install fills ./mirror either way. So the scan pins the hosted URL, reports success with no warning, and VEX attests not_affected. Every frozen install then fails the integrity check, because yarn takes the upstream tarball from the mirror by basename. That's exactly what #364 was about.

The same gate runs before a vendored→hosted takeover, so a takeover reverts a working vendored entry and replaces it with a hosted pin that can't install.

This is a separate cause from #1013, which is about where the mirror is configured (outside the project). Here the setting is in the project's own .yarnrc, which #839 covers when the file has no BOM. A BOM is common on Windows, for example from Notepad or Windows PowerShell 5 Set-Content -Encoding utf8.

Impact

On yarn ≥ 1.10, a project whose mirror config has a BOM gets a lock that no yarn install (frozen, plain or --offline) can install. The scan reports redirected: 1, and VEX attests a patch that is never installed. A vendored project taken over to hosted goes from patched to uninstallable.

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

mkdir p && cd p
printf '\xef\xbb\xbfyarn-offline-mirror "./mirror"\n' > .yarnrc        # same with \r\n, or .npmrc: yarn-offline-mirror=./mirror
echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
yarn install                                   # mirror/left-pad-1.3.0.tgz is written, so yarn read the BOM file
socket-patch scan --mode hosted --vex v.json --json --yes --api-url http://127.0.0.1:8787 --org o --api-token x
#  → exit 0, "redirected": 1, no redirect_yarn_classic_offline_mirror; v.json: not_affected
mkdir ../f && cp -r package.json yarn.lock .yarnrc mirror ../f && cd ../f
yarn install --frozen-lockfile                 # → exit 1: Integrity check failed for "left-pad"
yarn install --frozen-lockfile --offline       # → exit 1: Can't make a request in offline mode

Takeover variant: run scan --mode vendored first (its fresh frozen install is patched), then scan --mode hosted. With the BOM it says redirect_takeover_reverted_vendored, redirected: 1, and the fresh frozen install fails as above. Without the BOM it refuses with redirect_yarn_classic_offline_mirror and stays vendored and patched.

Expected vs actual

  • Expected: CLI_CONTRACT and docs/ecosystems.md describe redirect_yarn_classic_offline_mirror as the refusal for a project that sets yarn-offline-mirror. The readers should parse the file as yarn does: skip a leading BOM, as the crawler's .yarnrc reader already does (npm_crawler.rs, BOM + CRLF test near line 5851). Then the scan refuses and leaves yarn.lock untouched.
  • Actual: the scan exits 0 with redirected: 1, no warning and a not_affected attestation, and every install then fails.

Matrix (Linux, Node 22; each cell run twice)

yarn rc file scan fresh frozen install --offline install VEX
1.10.1 .yarnrc BOM (LF and CRLF) pins, exit 0 exit 1, integrity exit 1 not_affected
1.22.22 .yarnrc BOM (LF and CRLF) pins, exit 0 exit 1, integrity exit 1 not_affected
1.22.22 .npmrc BOM pins, exit 0 exit 1, integrity exit 1 not_affected
1.22.22 .yarnrc BOM, vendored→hosted takeover reverts + pins exit 1, integrity n/a n/a
1.7.0 .yarnrc BOM pins exit 0, patched (no integrity lines in 1.7 locks) exit 0 not_affected
1.10.1 / 1.22.22 .yarnrc / .npmrc without a BOM (control) refused redirect_yarn_classic_offline_mirror exit 0, unpatched (expected) exit 0 none

macOS and Windows weren't run. The parser is OS-independent, and Windows is where BOM-prefixed rc files are most common.

Suspect code

Activity

  1. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (Yarn classic, npm-family). Not a duplicate. No open PR covers it.

    Shares root cause with #1013: yarn_classic_offline_mirror (patch/redirect/mod.rs:3340) uses its own ad-hoc .yarnrc / .npmrc readers instead of resolving yarn-offline-mirror the way yarn 1 does. It reads only the project's two files (#1013: ancestor / user rc and YARN_* / npm_config_* env are missed) and doesn't skip a leading BOM (#1078; formats::text::strip_bom exists but isn't used here). Both will be fixed together by one config resolver used by preflight_yarn_classic_hosted.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (with #1013; shared root cause: the yarn classic offline-mirror gate reads yarn-offline-mirror through project-only, BOM-unaware rc scanners instead of yarn 1's config resolution). Branch: agent/fix-yarn-classic-mirror-config. Claim-ID: 2026-10-07T19:22:17Z-70c170


    Generated by Claude Code

  3. mikolalysenko commented on Oct 7, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1083


    Generated by Claude Code

  4. added 2 commits that reference this issue on Oct 7, 2026
    bf88546
    2b70834
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