Skip to content

chore(deps): clear all high-severity npm advisories + enforce 3-day minimum release age - #61

Merged
JosephSamirL merged 2 commits into
mainfrom
chore/deps-security-npm-high
Aug 9, 2026
Merged

chore(deps): clear all high-severity npm advisories + enforce 3-day minimum release age#61
JosephSamirL merged 2 commits into
mainfrom
chore/deps-security-npm-high

Conversation

@abbaseya

@abbaseya abbaseya commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Two commits, reviewable independently:

  1. Clear every open Dependabot alert — all 4 high plus the 5 moderate that fall out of the same resolution pass. No critical was open.
  2. Enforce the Convert 3-day minimum-release-age gate on npm installs.

No Kotlin/Java source is touched by either.


1 — Clear the Dependabot alerts

All 9 alerts sit in the root yarn.lock — the semantic-release release-tooling tree. That tree is dev-only and is installed by exactly one thing: the Install Node dependencies step of .github/workflows/release.yml. ci.yml is pure Gradle and never installs it. Nothing here reaches the published AAR/JAR, and tools/yarn.lock (parity-vector tooling) is untouched and carries no advisories.

One line of package.json, plus the lockfile it re-resolves.

Three of the four highs were already reachable through the existing semver ranges and only needed their resolutions floated (yarn up -R):

Package Before After Advisory
ip-address 10.1.0 10.4.0 GHSA-mwp4-54f8-5fhr — high, leading-zero octet SSRF / trust-boundary bypass
js-yaml 4.1.1 4.3.1 GHSA-52cp-r559-cp3m — high, quadratic CPU via merge-key chains
brace-expansion 2.1.0 5.0.9 GHSA-3jxr-9vmj-r5cp — high, exponential-time expansion DoS

The fourth high needed a range bump, not a pin

sigstore had to reach ≥ 4.1.1 (GHSA-52v5-jr5w-gjxr — high; certificateOIDs verification constraints are silently dropped and never enforced). Every dependent pinned it below that major: pacote@19.0.2, pacote@20.0.1 and libnpmpublish@10.0.2 all declare sigstore ^3.0.0, and they arrive via npm@10.9.8@semantic-release/npm@12.0.2semantic-release@24.2.9.

A resolutions override would have forced the major while leaving the declared range asserting something untrue, and it would rot at the next advisory. Instead this bumps the range that actually gates the chain:

semantic-release  ^24.0.0 → ^25.0.0   (resolves 25.0.9)

which walks entirely within semver to the patched version:

semantic-release@25.0.9
  └─ @semantic-release/npm@13.1.5   (declares npm ^11.6.2)
       └─ npm@11.19.0               (declares pacote ^21.5.1, libnpmpublish ^11.2.0)
            ├─ pacote@21.5.1        (declares sigstore ^4.0.0)  ─┐
            └─ libnpmpublish@11.2.0 (declares sigstore ^4.0.0)  ─┴─→ sigstore@4.1.1 ✅

Why the semantic-release major is safe here

semantic-release@25.0.0's only documented BREAKING CHANGES are Node minimums — ^22.14.0 || >= 24.10.0 (v20/v21/v23 dropped). No configuration or plugin-API surface changed.

  • release.yml pins node-version: 'lts/*', which resolves to the current LTS line — Krypton, 24.19.0 — and satisfies that range. The workflow needs no change, and it stays satisfied when Node 26 becomes LTS.
  • Every declared plugin remains compatible: @semantic-release/exec@7 declares semantic-release >=24.1.0; commit-analyzer, release-notes-generator and github are hoisted by semantic-release itself (v13/v14/v12 respectively).
  • @semantic-release/github@12 also drops its GitHub Search API consumption. That only affected the PR/issue success comments, which release.config.mjs already disables (successComment: false, releasedLabels: false, …) — so no behavioural change for us.

Moderates that close for free

No extra pinning — these are the same resolution pass:

Why chore(deps): and not fix:

release.config.mjs maps chore to no release and hides it from the notes. A dev-only release-tooling bump must not cut a Maven Central version of the SDK. Confirmed in the dry-run below: "The commit should not trigger a release."


2 — Minimum release age gate

Adds the Convert house gate to .yarnrc.yml, matching what already ships in javascript-sdk and backend tracking:

npmMinimalAgeGate: 4320          # 3 days, in minutes
npmPreapprovedPackages:
  - "@convertcom/*"

Yarn refuses to resolve any dependency version published less than three days ago. That is the window smash-and-grab npm malware lives in — a compromised publish is typically yanked within hours, so simply not being first to install it removes most of the exposure. This repo pins packageManager: yarn@4.15.0, well past the 4.10 that introduced the setting, so the gate lands natively with no Yarn version bump.

Why the @convertcom/* exemption is real here, unlike php-sdk / python-sdk

Those trees hold zero internal npm packages, so the exemption there would be decoration. This repo has two Berry projects:

  • the root release tooling — 0 @convertcom/ hits in yarn.lock
  • the nested tools/ parity-vector project — depends on @convertcom/js-sdk-bucketing

Yarn resolves rcfiles by walking up from the cwd, so tools/ inherits this file even though it has no .yarnrc.yml of its own. Verified by running yarn config --json from tools/ and confirming source points back at the repo root. Without the exemption, regenerating the cross-SDK parity vectors against a freshly published bucketing package would stall for three days.

The gate is enforcing, and it doesn't block us — both checked

  • Effective in both projects: npmMinimalAgeGate reports effective 4320, source .yarnrc.yml, from the repo root and from tools/ — confirming inheritance for the new keys, not just for the pre-existing nodeLinker.
  • Doesn't block this tree: a full fresh resolution from no lockfile at all, with the gate active, resolved 432 packages with zero quarantine rejections. Every version pinned here is at least 8 days old.
  • Not inert: installing a version published 0.86 days ago fails with YN0016: eslint@npm:10.8.1: All versions satisfying "10.8.1" are quarantined, and the same version installs cleanly with --no-time-gate — so the gate, not a missing version, is what refused it.

One honest gap: the @convertcom/* exemption path itself is not exercised. No internal package currently has a release inside the 3-day window to test against. The setting is confirmed recognized (it reports as effective config rather than an unknown key), not confirmed to admit a fresh internal publish end to end.


Verification

Run under the repo's own pinned toolchain — packageManager: yarn@4.15.0 via corepack, not the ambient yarn.

  1. Baseline controlyarn install --immutable on unmodified main: passes, no migration, .yarnrc.yml clean. Establishes that the starting lock was in sync.
  2. yarn install --immutable after both commits: passes at the root (the exact command release.yml runs) and in tools/ → the frozen install stays in sync. The only tools/ warnings are the pre-existing unmet @convertcom/js-sdk-bucketing peer ranges, unrelated to either change.
  3. Lockfile audited against all 9 advisories — every resolved version parsed out of the pushed yarn.lock and checked against each vulnerable range: 9/9 resolved, 0 still in range.
  4. End-to-end release dry-run under semantic-release@25.0.9 — all five configured plugins loaded across all nine lifecycle steps; verifyConditions completed for both @semantic-release/exec instances and for @semantic-release/github against the live API; commit analysis ran over real history and concluded "no release".
    Difference from the real path, stated explicitly: the dry-run used --no-ci --branches <this branch> because release.config.mjs restricts releases to main and semantic-release requires the branch to exist on the remote. Only the branch-name check differs; plugin resolution, GitHub auth and commit analysis all ran for real. ./gradlew publishAllPublicationsToMavenCentralRepository correctly never executed — dry-run skips prepare/publish, and no release was warranted anyway.

On .yarnrc.yml moving in a dependency PR

Normally that file changing inside a dependency bump is a red flag: a Yarn lockfile migration rewrites it with hardening opt-outs, including npmMinimalAgeGate: 0 against a built-in default of 1d, which would silently disable the gate. So the two concerns were deliberately split across commits:

  • Commit 1 (the dependency bump) leaves .yarnrc.yml byte-identical to main — verified with git diff --exit-code, and no install emitted YN0087: Migrated your project.
  • Commit 2 is the only one that touches it, and it moves the gate in the safe direction: 1440 (built-in default) → 4320 (explicit). Never to 0.

Worth reviewing commit 1's diff on its own to confirm that split.

Note for the reviewer: CI green here does not exercise either change. ci.yml never runs yarn, so the npm tree is only installed by release.yml on main. That is precisely why the dry-run and gate checks above were run by hand.

Supersedes

Dependabot PR #42 (ip-address 10.1.0 → 10.2.0) — that target is still inside GHSA-mwp4-54f8-5fhr's vulnerable range (<= 10.3.0). This PR takes ip-address to 10.4.0 instead.

🤖 Generated with Claude Code

Closes every open Dependabot alert on the root `yarn.lock` (the
semantic-release release-tooling tree — dev-only, installed solely by
`.github/workflows/release.yml`). No Kotlin/Java source is touched, so
this is deliberately a `chore` commit: release.config.mjs maps `chore`
to "no release", and a dependency bump in release tooling must not cut
a Maven Central version of the SDK.

Three of the four HIGH advisories were reachable through the existing
semver ranges and only needed the resolutions floated:

  ip-address       10.1.0 -> 10.4.0   GHSA-mwp4-54f8-5fhr (high, SSRF)
  js-yaml          4.1.1  -> 4.3.1    GHSA-52cp-r559-cp3m (high, DoS)
  brace-expansion  2.1.0  -> 5.0.9    GHSA-3jxr-9vmj-r5cp (high, DoS)

The fourth could not be: `sigstore` needed >= 4.1.1
(GHSA-52v5-jr5w-gjxr, high — `certificateOIDs` verification constraints
silently dropped), but its dependents pinned it below that major.
`pacote@19/20` and `libnpmpublish@10` all declare `sigstore ^3.0.0`, and
they arrive via `npm@10.9.8` <- `@semantic-release/npm@12` <-
`semantic-release@24.2.9`. Rather than force a major with a
`resolutions` override — which would leave the declared range lying
about compatibility and rot on the next advisory — bump the range that
actually gates the chain:

  semantic-release  ^24.0.0 -> ^25.0.0  (resolves 25.0.9)

That pulls @semantic-release/npm@13.1.5 -> npm@11.19.0 ->
pacote@21.5.1 + libnpmpublish@11.2.0, both of which declare
`sigstore ^4.0.0` -> 4.1.1, entirely within semver.

semantic-release 25.0.0's only BREAKING CHANGES are Node minimums
(`^22.14.0 || >=24.10.0`). release.yml pins `node-version: lts/*`, which
resolves to the Krypton line (24.19.0) and satisfies that, so the
workflow needs no change. Every declared plugin stays compatible:
@semantic-release/exec@7 declares `semantic-release >=24.1.0`, and
commit-analyzer/release-notes-generator/github are hoisted by
semantic-release itself.

Five MEDIUM advisories close as a side effect of the same resolution
pass, with no extra pinning: tar 7.5.13 -> 7.5.22 (GHSA-w8wr-v893-vjvp,
GHSA-vmf3-w455-68vh) and @sigstore/core 2.0.0 -> 3.2.1
(GHSA-jfc7-64v2-mr8c) via the chain above, plus the lower-severity
duplicates on js-yaml (GHSA-h67p-54hq-rp68) and ip-address
(GHSA-v2v4-37r5-5v8g).

`.yarnrc.yml` is intentionally byte-identical to main. A Yarn lockfile
migration rewrites it with hardening opt-outs — `npmMinimalAgeGate: 0`
against a built-in default of 1d — and that would silently disable the
minimum-release-age gate. Verified after every install step: no
`YN0087: Migrated`, `git diff --exit-code .yarnrc.yml` clean, and
`yarn config --json` still reports `npmMinimalAgeGate` effective 1440
from `<default>`. Every version resolved here is at least 8 days old, so
the gate admits them all.

Ran under the repo's own pinned toolchain (`packageManager: yarn@4.15.0`
via corepack), not the ambient yarn. `yarn install --immutable` passes,
so CI's frozen install stays in sync. `tools/yarn.lock` is untouched and
carries no advisories.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@abbaseya abbaseya self-assigned this Aug 8, 2026
@abbaseya
abbaseya requested a review from JosephSamirL August 8, 2026 16:56
Adds the Convert house supply-chain gate to the repo's `.yarnrc.yml`,
matching what already ships in javascript-sdk and backend tracking:

    npmMinimalAgeGate: 4320          # 3 days, in minutes
    npmPreapprovedPackages: ["@convertcom/*"]

Yarn refuses to resolve any dependency version published less than three
days ago. That is the window smash-and-grab npm malware lives in — a
compromised publish is typically yanked within hours, so simply not being
first to install it removes most of the exposure. android-sdk pins
`packageManager: yarn@4.15.0`, well past the 4.10 that introduced the
setting, so the gate lands natively with no version bump.

`npmPreapprovedPackages` is NOT dead config here, which is what makes
this repo differ from the php-sdk and python-sdk gates. Those trees hold
zero internal npm packages, so the exemption there would have been
decoration. This repo has TWO Berry projects: the root release tooling
(0 `@convertcom/` hits in yarn.lock) and the nested parity-vector
project under tools/, which depends on `@convertcom/js-sdk-bucketing`.
Yarn resolves rcfiles by walking up from the cwd, so tools/ inherits
this file despite having no `.yarnrc.yml` of its own — verified by
running `yarn config --json` from tools/ and confirming `source` points
back at the repo root. Without the exemption, regenerating the
cross-SDK parity vectors against a freshly published bucketing package
would stall for three days.

Verified, not assumed — four checks:

  1. `npmMinimalAgeGate` reports effective 4320 (source `.yarnrc.yml`,
     against a built-in default of 1d) from BOTH the repo root AND
     tools/, confirming inheritance for the new keys and not just for
     the pre-existing `nodeLinker`.
  2. `yarn install --immutable` still passes at the root (the exact
     command release.yml runs) and in tools/. The only tools/ warnings
     are the pre-existing unmet `@convertcom/js-sdk-bucketing` peer
     ranges, unrelated to the gate.
  3. The gate does not block this tree: a full fresh resolution from no
     lockfile at all, with the gate active, resolved 432 packages with
     zero quarantine rejections. Every version currently pinned is at
     least 8 days old.
  4. The gate is not inert. Installing a version published 0.86 days
     ago fails with `YN0016: eslint@npm:10.8.1: All versions satisfying
     "10.8.1" are quarantined`, and the SAME version installs cleanly
     with `--no-time-gate` — so the gate, not a missing version, is
     what refused it.

The `@convertcom/*` exemption path itself is not exercised by these
checks: no internal package currently has a release inside the 3-day
window to test against. The setting is confirmed recognized (it reports
as effective config rather than an unknown key), not confirmed to admit
a fresh internal publish end to end.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@abbaseya abbaseya changed the title chore(deps): resolve all 4 high-severity npm advisories in release tooling chore(deps): clear all high-severity npm advisories + enforce 3-day minimum release age Aug 8, 2026
@JosephSamirL
JosephSamirL merged commit 81f9194 into main Aug 9, 2026
10 checks passed
@JosephSamirL
JosephSamirL deleted the chore/deps-security-npm-high branch August 9, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants