chore(deps): clear all Dependabot advisories in Scripts/release via Yarn 4 - #52
Conversation
…arn 4 Every one of the 16 Dependabot alerts on this repo (1 critical, 6 high, 9 medium) lived in Scripts/release/package-lock.json — the isolated semantic-release manifest. Nothing installs it: no workflow runs setup-node, npm or npx, and release.yml documents that the manifest is deliberately never invoked. Nothing published to SPM or CocoaPods is affected. Fourteen of the sixteen sat inside the npm CLI that arrives via semantic-release -> @semantic-release/npm -> npm. npm declares those as bundleDependencies, so the lockfile carried them as inBundle entries with no resolved URL and no integrity hash — npm never fetches them individually, so "overrides" cannot reach them and the only lever is the npm CLI version itself. That lever is not enough: npm 11.19.0 (the ceiling @semantic-release/npm 13 allows) and npm 12.0.2 both bundle ip-address 10.2.0, while GHSA-mwp4-54f8-5fhr needs 10.3.1. No published npm CLI clears that alert. Yarn Berry does not implement bundleDependencies, so it resolves the same tree as first-class, patchable nodes. Switching Scripts/release to Yarn 4 therefore reaches versions npm structurally cannot: ip-address 10.1.0 -> 10.4.0 (high GHSA-mwp4-54f8-5fhr, med GHSA-v2v4-37r5-5v8g) js-yaml 4.2.0 -> 4.3.1 (high GHSA-52cp-r559-cp3m) brace-expansion 2.0.2 -> 5.0.9 (high GHSA-3jxr-9vmj-r5cp, GHSA-mh99-v99m-4gvg, med GHSA-f886-m6hf-6m8v) sigstore 3.1.0 -> 4.1.1 (high GHSA-52v5-jr5w-gjxr) tar 7.5.11 -> 7.5.22 (crit GHSA-23hp-3jrh-7fpw, high GHSA-8x88-c5mf-7j5w, + 4 med) @sigstore/core 2.0.0 -> 3.2.1 (med GHSA-jfc7-64v2-mr8c) picomatch 4.0.3 -> 4.0.5 (high GHSA-c2c7-rcm5-vvqj, med GHSA-3v7f-55p6-f55p) semantic-release goes 24.2.9 -> 25.0.9 and @semantic-release/github 11.0.6 -> 12.0.9 with it (25.0.9 requires ^12.0.0, so the root pin stays inside core's range and yarn keeps a single copy); release-notes-generator 14.0.3 -> 14.1.1 for the same reason. commit-analyzer 13.0.1 is already current. Also adds the supply-chain hardening this repo lacked: packageManager pins yarn@4.18.0 and .yarnrc.yml sets npmMinimalAgeGate: 4320, refusing any version published under 3 days ago — same gate and same pin as javascript-sdk, php-sdk and python-sdk. No npmPreapprovedPackages entry is needed: this manifest has no internal @convertcom/* dependencies, so our own release chain cannot stall on a fresh publish. The release.yml deviation block was re-verified against 25.0.9 rather than assumed: verifyAuth, EGITNOPERMISSION, makeTag, tag(), addNote(), push(repositoryUrl) and pushNotes() are all still in core run(), at the same lines, so the tag-driven decision is untouched by the bump. Its version reference and the npx dry-run command are updated to match. Verified locally: 414 packages resolve and install cold-cache with the gate active, yarn install --immutable is clean with zero lockfile churn, all 414 resolutions clear the 3-day floor (youngest semantic-release@25.0.9 at 3.5 days), the gate is proven live rather than inert (adding a 0.97-day-old version fails YN0016 "quarantined" and succeeds only with the gate off), and semantic-release --dry-run loads all seven configured plugins. Supersedes Dependabot PR #36, which bumped to 25.0.5 in June and whose npm bundle has since gone vulnerable again (tar 7.5.16). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
On the one red check in this PR's history — it is a pre-existing flake, not this diff. Commit Re-running that job on the same SHA passed. So the identical commit is 2 passes / 1 fail — non-deterministic, and this diff contains no Swift at all (only Looking at the test, the shape fits: 🤖 Generated with Claude Code |
JosephSamirL
left a comment
There was a problem hiding this comment.
Approved via /convert:approve. An independent code review ran through /convert:review, and this issues the B-G4 human marker at d07a7fe.
Code review — #52
- HEAD sha: d07a7fe
- merge-base: 8d448e0 (verified ancestor via
git merge-base --is-ancestor) - Reviewed:
- Full non-lockfile diff (106 lines), plus
Scripts/release/yarn.lock(3933 lines) and the deletedScripts/release/package-lock.json(5705 lines). git ls-tree -r <sha> -- Scripts/releaseto establish which lockfiles actually exist at HEAD..github/workflows/release.ymlin full at the PR sha; all four workflows enumerated;git grepforScripts/release,npm ci,npm install,package-lock,setup-node,corepack,yarnacross the tree at the PR sha.gh api repos/convertcom/ios-sdk/dependabot/alerts --paginate(all states, grouped by manifest path).- OSV.dev
/v1/querybatchaudit of all 414 npm packages resolved by the newyarn.lock, with a positive control run of the identical method against the oldpackage-lock.json. - npm registry metadata for
semantic-release@{24.2.9,25.0.9},@semantic-release/github@{11.0.6,12.0.9},@semantic-release/release-notes-generator@{14.0.3,14.1.1},@yarnpkg/cli-dist@{4.10.3,4.18.0}. - Downloaded and unpacked
semantic-release@25.0.9to check the exact line numbers the workflow comment cites. - Downloaded and unpacked
@yarnpkg/cli-dist@4.18.0to confirm.yarnrc.ymlkeys are real. gh apireads of.yarnrc.yml/package.jsonon the default branches ofconvertcom/{javascript-sdk,php-sdk,python-sdk}(plus full recursive tree listings) to check the cross-repo claim.
- Full non-lockfile diff (106 lines), plus
- Verdict: APPROVED
The PR does what its title claims, and the claim is verifiable rather than asserted. No blocking findings.
Blocking findings
None.
Non-blocking observations
N1 — The cross-repo claim about php-sdk and python-sdk is factually wrong (2 of the 3 repos named)
File: Scripts/release/.yarnrc.yml:5-6 and RELEASE.md:232-233
Both the yarnrc comment and RELEASE.md state the age gate matches "convertcom/javascript-sdk, convertcom/php-sdk and convertcom/python-sdk".
Evidence — read from each repo's default branch, not a local checkout:
$ gh api repos/convertcom/javascript-sdk/contents/.yarnrc.yml --jq .content | base64 -d
nodeLinker: node-modules
...
npmMinimalAgeGate: 4320
npmPreapprovedPackages:
- "@convertcom/*"
$ gh api repos/convertcom/php-sdk/contents/.yarnrc.yml --jq .content | base64 -d
nodeLinker: node-modules
$ gh api repos/convertcom/python-sdk/contents/.yarnrc.yml --jq .content | base64 -d
nodeLinker: node-modules
Confirmed each repo has exactly one .yarnrc.yml in its whole tree (gh api .../git/trees/HEAD?recursive=1 --jq '.tree[]|select(.path|test("yarnrc"))' returned the single root path for both). So javascript-sdk matches; php-sdk and python-sdk have no age gate at all.
Why it does not block: the gate itself is correctly configured and effective; only the prose justifying it is inaccurate.
Fix: narrow both comments to javascript-sdk, or land the same gate in php-sdk/python-sdk in a follow-up.
N2 — Node engine floor rose from >=20.8.1 to ^22.14.0 || >=24.10.0, and nothing in the repo records it
File: Scripts/release/package.json:7,9 (@semantic-release/github 11.0.6→12.0.9, semantic-release 24.2.9→25.0.9); RELEASE.md:233
Evidence (npm registry engines fields):
semantic-release@24.2.9 {'node': '>=20.8.1'}
semantic-release@25.0.9 {'node': '^22.14.0 || >= 24.10.0'}
@semantic-release/github@11.0.6 {'node': '>=20.8.1'}
@semantic-release/github@12.0.9 {'node': '^22.14.0 || >= 24.10.0'}
@semantic-release/release-notes-generator@14.1.1 {'node': '>=20.8.1'}
The new range excludes Node 20.x, 21.x, 23.x and 24.0–24.9. Meanwhile:
$ git ls-tree -r d07a7fe8 --name-only | grep -iE "nvmrc|node-version|\.tool-versions"
(none)
RELEASE.md:233 adds the instruction "Run yarn install from Scripts/release/" without stating any Node requirement.
Why it does not block: no workflow installs this manifest (see "What I verified clean"), so CI cannot break. The blast radius is one developer running the documented command on a too-old/too-new Node.
Unverified: whether Yarn 4 hard-fails or merely warns on a dependency engines.node mismatch — I could not confirm the enforcement path from the @yarnpkg/cli-dist@4.18.0 bundle, so I am not asserting a severity beyond "the install will at minimum complain".
Fix: add .nvmrc (or a one-line note in RELEASE.md) recording ^22.14.0 || >=24.10.0.
N3 — The lockfile has no freshness gate anywhere, because nothing installs it
File: .github/workflows/release.yml (whole file)
Evidence:
$ git grep -nE "npm |npm ci|yarn|setup-node|corepack|node-version" d07a7fe8 -- .github
.github/workflows/release.yml:30:# Local dry-runs (from Scripts/release, after `yarn install`:
.github/workflows/release.yml:31:# GITHUB_TOKEN=dummy yarn semantic-release --dry-run --no-ci):
Both hits are inside a comment block. There is no setup-node, no cache key, no install step in any of the four workflows (ci.yml, generate-config-types.yml, pod-publish.yml, release.yml).
This is pre-existing — it was equally true of package-lock.json — and is consistent with the deliberate "kept but not invoked" design. Worth stating explicitly: Scripts/release/yarn.lock now exists essentially as a Dependabot scanning surface, and no yarn install --immutable will ever catch it drifting from package.json. I checked that drift manually instead and it is clean (see below).
Fix (optional follow-up): a tiny yarn install --immutable job scoped to Scripts/release/** would make lockfile drift loud. Out of scope for this PR.
What I verified clean
The title's claim holds — verified, not taken on trust.
- Every open alert targets the file this PR deletes. Grouping all alerts by manifest path:
$ gh api repos/convertcom/ios-sdk/dependabot/alerts --paginate --jq '...group_by(.path)...' [{"path":"Scripts/release/package-lock.json","states":{"auto_dismissed":7,"open":9}}]Scripts/release/package-lock.jsonis the repo's only manifest path, andgit ls-tree -r d07a7fe8 -- Scripts/releaseconfirms it is gone at HEAD. - Each formerly-vulnerable package now resolves at or above its patched version in the new
yarn.lock:package advisory patched-in now resolves to ip-address(high)10.3.1 10.4.0 tar7.5.18 7.5.22 js-yaml(high)4.3.0 4.3.1 brace-expansion(high)2.1.2 5.0.9 sigstore(high)4.1.1 4.1.1 @sigstore/core3.2.1 3.2.1 picomatch4.0.4 4.0.5 (plus an out-of-range 2.3.2) Checked for all occurrences of each package, not just the first — grep -n '<pkg>@npm'shows single entries exceptpicomatch, whose second entry (2.3.2) falls outside the>=4.0.0, <4.0.4range. - Full-tree advisory audit with a positive control. OSV.dev batch query over all 414 packages resolved by the new
yarn.lock: 0 packages with advisories. To prove the method was not silently returning nothing, I ran the identical query against the oldpackage-lock.json(420 packages) and it returned exactly the 7 distinct vulnerable packages behind the 9 open alerts (@sigstore/core@2.0.0,brace-expansion@2.0.2,ip-address@10.1.0,js-yaml@4.2.0,picomatch@4.0.3,sigstore@3.1.0,tar@7.5.11). Control passed; the zero is real. - Dependabot will actually scan the new lockfile.
gh api repos/convertcom/javascript-sdk/dependabot/alertsreturns alerts withmanifest_pathvalues"yarn.lock"and"packages/demo-nodejs/yarn.lock"— GitHub's dependency graph demonstrably parses Yarn berry lockfiles in this org.
The "two lockfiles in one directory" concern in the review brief does not apply.
$ git ls-tree -r d07a7fe8 --name-only -- Scripts/release
Scripts/release/.gitignore
Scripts/release/.releaserc.json
Scripts/release/.yarnrc.yml
Scripts/release/package.json
Scripts/release/yarn.lock
package-lock.json is deleted (git diff --name-status shows D). Exactly one lockfile at HEAD; no npm/yarn divergent-tree risk.
Yarn version coherence — all four surfaces agree.
yarn.lockheader is__metadata: version: 10,cacheKey: 10c0→ Yarn modern (berry), not classic.package.json:12pinspackageManager: yarn@4.18.0+sha512.fcb8716f…;@yarnpkg/cli-dist@4.18.0exists and is the currentlateston npm..yarnrc.yml:1nodeLinker: node-modulesis consistent with anode_modules/-based.gitignore.- The corepack hash is canonical. My first check compared it against the npm
dist.integrityof@yarnpkg/cli-dist@4.18.0and it did not match — but a control againstconvertcom/javascript-sdk's known-workingyarn@4.10.3+sha512.c38cafb5…pin also did not match, proving that comparison measures the wrong artifact. GitHub code search for the exact stringyarn@4.18.0+sha512.fcb8716f…returns 48 results across unrelated public repos (VKCOM/VKUI, RedHatInsights/notifications-backend, …), which is whatyarn set version 4.18.0emits. Pin is correct; I am explicitly not reporting this as a defect. .yarnrc.ymlkeys are real for this Yarn version:npmMinimalAgeGateappears 4× in@yarnpkg/cli-dist@4.18.0'sbin/yarn.js, andnpmPreapprovedPackagesis also present.4320minutes = 72 h = the 3 days the comment claims.
No breaking major bump, because there are no call sites. semantic-release 24→25 and @semantic-release/github 11→12 are majors, but git grep "Scripts/release" across the entire tree at the PR sha returns only 5 hits, all of them comment/prose lines in release.yml and RELEASE.md. Nothing requires or imports this package, so the CJS→ESM class of break cannot arise here (semantic-release@25.0.9 is "type": "module", consumed only via its own bin).
Peer dependencies are satisfied and the graph is deduped. semantic-release@25.0.9 declares @semantic-release/commit-analyzer: ^13.0.1, @semantic-release/github: ^12.0.0, @semantic-release/release-notes-generator: ^14.1.0; the pinned 13.0.1 / 12.0.9 / 14.1.1 satisfy all three and share single lock entries with the transitive descriptors. Reverse peers: commit-analyzer and release-notes-generator require semantic-release >=20.1.0, github requires >=24.1.0 — all satisfied by 25.0.9.
Lockfile integrity. Parsed all 505 lock entries and all 504 dependencies: descriptors: 0 dangling (referenced-but-unresolved) descriptors. The root convert-ios-sdk-release@workspace:. entry lists exactly the four deps at exactly the versions in package.json — i.e. the lockfile is in sync with the manifest, which is the core of what yarn install --immutable checks.
No supply-chain escape hatches. Every resolution: in yarn.lock uses the @npm: protocol (the sole exception is the root @workspace:. entry) — no git:, http:, patch:, portal: or exec: sources. No resolutions/overrides block in package.json or the lockfile, so nothing pins a vulnerable version back. No npmRegistryServer/npmAuthToken/npmScopes overrides. No secrets or tokens introduced (the only GITHUB_TOKEN occurrence in the diff is GITHUB_TOKEN=dummy inside a comment).
The workflow's own evidence claims are accurate. The comment block asserts it was "re-verified locally on 2026-08-08 against the currently pinned 25.0.9". I downloaded semantic-release@25.0.9 and checked every cited line of its 290-line index.js:
L88 await verifyAuth(options.repositoryUrl, context.branch.name, { cwd, env });
L101 throw getError("EGITNOPERMISSION", context);
L185 nextRelease.version = getNextVersion(context);
L186 nextRelease.gitTag = makeTag(options.tagFormat, nextRelease.version);
L187 nextRelease.name = nextRelease.gitTag;
L204 if (options.dryRun) {
L205 logger.warn(`Skip ${nextRelease.gitTag} tag creation in dry-run mode`);
L208 await tag(nextRelease.gitTag, nextRelease.gitHead, { cwd, env });
L209 await addNote({ channels: [nextRelease.channel] }, nextRelease.gitTag, { cwd, env });
L210 await push(options.repositoryUrl, { cwd, env });
L211 await pushNotes(options.repositoryUrl, nextRelease.gitTag, { cwd, env });
Every cited line number and description matches exactly. The re-verification was genuinely performed.
CI cannot break. No workflow installs Scripts/release, so there is no leftover npm ci against a yarn project, no stale cache: npm in actions/setup-node, and no corepack step to get wrong. release.yml's functional steps (tag extraction, podspec assertion, swift build, temp-consumer smoke, gh release create, pod trunk push) are untouched by this PR — the only change to that file is the comment block at lines 9-31.
RELEASE.md drift. Checked the whole file for node|yarn|npm|corepack — the only hits are lines 224 and 230-235, all added by this PR and all consistent with the new state (yarn.lock + .yarnrc.yml listed as committed artifacts, yarn install as the install command, npm install explicitly warned against). No stale npm ci/package-lock.json instruction survives anywhere. The single inaccuracy is the cross-repo claim in N1.
.gitignore. node_modules/, .yarn/, .pnp.* are correct for a nodeLinker: node-modules berry project with the default global cache; no committed artifact is accidentally ignored (yarn.lock and .yarnrc.yml are both tracked, confirmed by git ls-tree).
Takes the repo from 9 open Dependabot alerts (4 high, 5 moderate) to 0, and clears the 7 auto-dismissed ones (incl. 1 critical) along with them.
Nothing published changes.
Package.swift, both podspecs and everySources/file are untouched — this is one directory of dev-only release tooling.Where the alerts were, and why a version bump alone could not fix them
All 16 alerts lived in
Scripts/release/package-lock.json. Nothing installs it: no workflow runssetup-node,npmornpx, and the deviation block inrelease.ymldocuments that the manifest is deliberately never invoked.Fourteen of the sixteen sat inside the npm CLI, which arrives as
semantic-release → @semantic-release/npm → npm. npm declares its own dependencies asbundleDependencies, so the lockfile carried them asinBundleentries with noresolvedURL and nointegrityhash — npm never fetches them individually, sooverridescannot reach them, and the only lever is the npm CLI version itself.That lever is not enough. I unpacked both candidate tarballs:
ip-address@semantic-release/npm@13allows)ip-address10.3.1 shipped 2026-07-25;socks@2.8.9already asks for^10.1.1, so the range is fine — npm simply has not re-bundled. No published npm CLI clears that high alert.Yarn Berry does not implement
bundleDependencies, so it resolves the same tree as first-class, patchable nodes. Samenpm@11.19.0in the graph, but the leaves move:ip-addressGHSA-mwp4-54f8-5fhr, medGHSA-v2v4-37r5-5v8gjs-yamlGHSA-52cp-r559-cp3mbrace-expansionGHSA-3jxr-9vmj-r5cp, highGHSA-mh99-v99m-4gvg, medGHSA-f886-m6hf-6m8vsigstoreGHSA-52v5-jr5w-gjxrtarGHSA-23hp-3jrh-7fpw, highGHSA-8x88-c5mf-7j5w, + 4 med@sigstore/coreGHSA-jfc7-64v2-mr8cpicomatchGHSA-c2c7-rcm5-vvqj, medGHSA-3v7f-55p6-f55pThis is the same shape as php-sdk#60 and python-sdk#59 —
sigstorebehind a range wall that only the semantic-release major bump reaches. The difference here is that ios-sdk was on npm, so the bundled tree was unreachable by any in-tree bump, not merely a stale one.Three things that want real eyes
1.
Scripts/release/package-lock.jsonis deleted, replaced byyarn.lock. This is the load-bearing change and the reason the highip-addressalert clears at all.RELEASE.mdalready described the kept artifact as ".releaserc.json+ its ownpackage.json" — the lockfile was never part of the documented AC5.3 artifact, it was a byproduct of the one localnpm installthat gathered the deviation-block evidence. It is nowyarn.lock+.yarnrc.yml, andRELEASE.mdsays so.2. semantic-release 24.2.9 → 25.0.9, and
@semantic-release/github11.0.6 → 12.0.9 with it. 25.0.9 requires^12.0.0, so the root pin stays inside core's range and yarn keeps a single copy rather than shadowing it;release-notes-generator14.0.3 → 14.1.1 for the same reason (core wants^14.1.0).commit-analyzer13.0.1 is already current. The repo's exact-pin style is kept — no carets introduced.This is the release path's own tooling, so the deviation block was re-verified against 25.0.9, not assumed:
verifyAuth,EGITNOPERMISSION,makeTag,tag(),addNote(),push(repositoryUrl)andpushNotes()are all still in corerun(), at the same lines, and a dry-run still stops at the branch gate exactly as documented. The tag-driven decision is untouched by the bump. The block's version reference and itsnpxdry-run command are updated to match.3.
npmMinimalAgeGate: 4320is new hardening, not a revert. ios-sdk never had it..yarnrc.ymlnow refuses any dependency version published under 3 days ago, andpackageManagerpinsyarn@4.18.0with the same hash as php-sdk#60 / python-sdk#59 — which is what makes the gate exist at all rather than falling through to a Yarn 1 substitution. NonpmPreapprovedPackagesentry: this manifest has no internal@convertcom/*dependencies, so our own release chain cannot stall on a fresh publish (same call as php-sdk#60, for the same reason).Verified locally on the branch
yarn installcold-cache with the gate active: 414 packages resolve and link, noYN0087, no quarantine rejection.yarn install --immutablecold-cache: clean, zero lockfile churn.semantic-release@25.0.9at 3.5 days (same figure python-sdk#59 reported).YN0016 … are quarantined, and the identical add succeeds only with the gate switched off.yarn configconfirmsnpmMinimalAgeGate = 4320sourced from.yarnrc.yml, withenableScriptsstill at its secure default.GITHUB_TOKEN=dummy yarn semantic-release --dry-run --no-ciloads all seven configured plugins under 25.0.9 and stops at the branch gate as documented.CI here is Swift-only and this PR touches no Swift, so
build-testandparity-suiteshould be unaffected.Follow-ups, not in this PR
tar7.5.16). It can be closed.Scripts/generate-parity-vectors/is still on npm (package-lock.json, 6 deps, zero alerts). Deliberately left alone to keep this PR to the alert surface. It is the only manifest here pulling a published@convertcom/*package, so migrating it would wantnpmPreapprovedPackages: ["@convertcom/*"]like javascript-sdk — worth its own small PR..github/dependabot.yml, so this repo gets security updates but no scheduled version updates. That is why a June PR was still the only open fix.🤖 Generated with Claude Code