Skip to content

feat: v2#129

Draft
Aidosmf wants to merge 11 commits into
mainfrom
mf/v2
Draft

feat: v2#129
Aidosmf wants to merge 11 commits into
mainfrom
mf/v2

Conversation

@Aidosmf
Copy link
Copy Markdown
Collaborator

@Aidosmf Aidosmf commented Dec 17, 2024

Description

Type of change

  • Patch: Bug (non-breaking change which fixes an issue)
  • Minor: New feature (non-breaking change which adds functionality)
  • Major: Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • This is something we need to do.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Dec 17, 2024

🦋 Changeset detected

Latest commit: 579778b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
@lottiefiles/relottie-extract-features Major
@lottiefiles/relottie-metadata Major
@lottiefiles/relottie-parse Major
@lottiefiles/last-builder Major
@lottiefiles/last Major
@lottiefiles/relottie-stringify Major
@lottiefiles/relottie Major
@lottiefiles/relottie-cli Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Aidosmf Aidosmf changed the base branch from main to mf/fix-workspace-deps December 17, 2024 19:18
Base automatically changed from mf/fix-workspace-deps to main December 18, 2024 04:24
Aidosmf added a commit that referenced this pull request May 13, 2026
Closes Dependabot alerts:
- #128 fast-uri path traversal via percent-encoded dot segments (HIGH)
- #129 fast-uri host confusion via percent-encoded authority delimiters (HIGH)

fast-uri is a transitive dep pulled in by the ajv → fastify-style
chain. The 3.1.2 release normalises percent-encoded sequences before
authority/path parsing, closing both classes of confusion.
Aidosmf added a commit that referenced this pull request May 19, 2026
…#167)

* chore(security): add .npmrc with ignore-scripts to harden against postinstall worms

Adds repo-wide .npmrc that disables lifecycle scripts during `pnpm install`,
the primary propagation vector for the "mini Shai-Hulud" npm worm class.
A compromised transitive dependency can no longer execute code on developer
machines or CI runners during install.

Side-effect: husky's `prepare` script no longer runs automatically — README
now instructs contributors to run `pnpm prepare` once after `pnpm install`.

Refs:
- https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack
- https://tanstack.com/blog/npm-supply-chain-compromise-postmortem

* chore(security): pin GitHub Actions to commit SHAs

Replaces mutable @v4 / @v1 tag references with 40-character commit SHAs.
Tags on GitHub are mutable — a compromised maintainer account (or stolen
token) can force-push an existing tag to point at malicious code, as
happened in the tj-actions/changed-files compromise (March 2025) where
the v4 tag was rewritten to inject a secret-dumping payload across
23,000+ repos.

With `id-token: write` in this workflow's release jobs, a malicious action
would gain OIDC token access capable of publishing to npm under
`@lottiefiles/*`. SHA pinning makes that path immutable.

Pinned versions:
- actions/checkout       v4.3.1  → 34e114876b0b11c390a56381ad16ebd13914f8d5
- pnpm/action-setup      v4.4.0  → a15d269cd4658e1107c09f1fabf4cbd7bd1f308a
- actions/setup-node     v4.4.0  → 49933ea5288caeca8642d1e84afbd3f7d6820020
- andresz1/size-limit-action v1.8.0 → 94bc357df29c36c8f8d50ea497c3e225c3c95d1d
- changesets/action      v1.8.0  → d94a5c301145045a0960133674e003b265942a22

* chore(security): add --frozen-lockfile --ignore-scripts to CI installs

Hardens all three `pnpm install` invocations in the release workflow:

- `--frozen-lockfile`: refuses to mutate pnpm-lock.yaml during install,
  preventing a PR from silently introducing untracked transitives that
  the release runner would then trust.
- `--ignore-scripts`: explicit defense-in-depth alongside .npmrc — if
  .npmrc is ever modified, this flag still blocks lifecycle scripts of
  dependencies. Critical here because the release-npm and release-gpr
  jobs hold `id-token: write` and could otherwise be coerced into
  publishing trojaned versions under @lottiefiles/* if a compromised
  dependency executes during install.

* chore(security): pin packageManager with sha512 integrity hash

Corepack now verifies the pnpm tarball's sha512 before executing it.
Without the hash, a registry-side tampering of pnpm@9.12.3 (or a MITM
on a developer's network) would yield a trojaned package manager
running every install — including the postinstall lifecycle that the
.npmrc disables. Pinning the hash closes that escalation path.

Hash sourced from https://registry.npmjs.org/pnpm/9.12.3 (dist.integrity).

* chore(security): mark monorepo root as private

Sets `"private": true` on the monorepo root package.json. npm rejects
publish attempts on private packages, so a typo'd `pnpm publish` at the
repo root — or a scripted attacker with publish access — cannot push
this meta-package to the registry. Defense-in-depth against operator
error and against scripted publish of unintended scopes.

* chore(security): add empty pnpm.onlyBuiltDependencies allowlist

Sets an explicit empty allowlist of packages permitted to run install
scripts. With `ignore-scripts=true` already globally enforced via
.npmrc, this acts as belt-and-braces (effective even if .npmrc is
modified) and as inline documentation: every package added to this
list must clear a security review first.

If a transitive dependency legitimately requires a native build, the
package name must be added here in a reviewed PR — making the
attack surface for install-script execution explicit rather than
implicit.

* chore(security): add pnpm audit step to validate job

Adds `pnpm audit --prod --audit-level=high` to CI. Surfaces known
vulnerable production dependencies on every push and PR — closing the
detection-gap window during which a freshly compromised npm package
(like the mini-Shai-Hulud-affected TanStack versions in Sep 2025) may
sit in the dependency tree before being flagged.

Tuned to `--prod` to ignore dev-only vulnerabilities (lower signal),
and `--audit-level=high` to fail only on HIGH/CRITICAL findings so the
job stays actionable rather than noisy.

* chore(security): add gitleaks secret scanning workflow

Adds a dedicated Security workflow that runs gitleaks against the full
git history on every push, PR, and weekly schedule. Catches accidentally
committed credentials before they reach a public branch and surfaces
historical leaks that need rotation.

The gitleaks binary is downloaded by URL and verified against the
upstream sha256 from the v8.23.1 release checksum file — no third-party
action wrapper, no GitHub Actions tag that can be force-pushed.

A weekly schedule (Mon 06:00 UTC) is included so the scan still runs
when the repo is quiet, catching newly disclosed credential patterns
applied to existing commits.

* chore(security): add StepSecurity Harden-Runner to every CI job

Adds harden-runner@v2.17.0 (pinned to SHA) as the first step in every
job across release.yml and security.yml. Initial policy is
`egress-policy: audit` — non-blocking observation mode.

Harden-Runner intercepts all outbound network calls from the GitHub
runner and records them. With the release jobs holding `id-token: write`
this is the last line of defense if a transitive dependency or
compromised action attempts to exfiltrate the OIDC token (or any other
secret) to an attacker-controlled host: the connection appears in the
StepSecurity insights dashboard and can later be set to `block` once
the legitimate egress allowlist is established.

* chore(security): add CODEOWNERS for supply-chain-sensitive paths

Adds .github/CODEOWNERS requiring review from the LottieFiles R&D team
(https://github.com/orgs/LottieFiles/teams/rnd) on the paths that gate
publish access:

- package.json, pnpm-lock.yaml, pnpm-workspace.yaml, workspace
  package.json files — dependency / lockfile mutations
- .npmrc, .pnpmrc — install-script policy
- .github/ and .github/workflows/ — CI configuration with OIDC publish
  access
- .husky/ — git hooks that run on every commit
- .changeset/config.json — release plumbing

Branch protection must be configured separately to require code-owner
review for this file to take effect.

* docs(security): add SECURITY.md with responsible disclosure policy

Documents the private vulnerability reporting channel (GitHub Security
Advisories), supported versions, scope, response SLAs, and an
inventory of the supply-chain defenses applied in the recent
chore(security) commit series.

Without a documented channel, researchers who detect this repository's
package on a compromised IoC feed have no way to coordinate disclosure
and may disclose publicly — slowing incident response.

* chore(security): add CodeQL workflow for JavaScript/TypeScript

Runs GitHub's security-and-quality query pack against every push, PR
to main, and weekly on Mondays. Catches injection / eval / unsafe
deserialization patterns that a supply-chain attacker may add via a
malicious PR (the "low-effort source-level backdoor" pattern) — not
specific to mini Shai-Hulud but raises the bar for any compromise
that has to go through the PR review surface.

Both action references are pinned to commit SHAs.

* chore(security): pin Node version to LTS Iron via .nvmrc

Pins the development Node.js version to the lts/iron line (Node 20),
matching the version used by the release workflow's matrix. Aligns
local installs with CI so the dependency resolution and execution
environment match what is tested before release.

Without a pin, contributors on older Node versions may resolve a
different module graph than CI, weakening the supply-chain guarantee
that the lockfile-installed tree is what was validated.

* chore(security): generate and upload CycloneDX SBOM in CI

Adds an SBOM (Software Bill of Materials) generation step to the
validate job using @cyclonedx/cdxgen against the pnpm workspace.
Output is uploaded as a 90-day workflow artifact `sbom-cyclonedx`.

Used for incident response: when an advisory says
`@lottiefiles/relottie@X.Y.Z may include compromised dependency Z`,
consumers and the maintainer team can verify against the SBOM rather
than reconstructing the dependency tree manually. Critical capability
during a Shai-Hulud-style worm event where time-to-confirmation
directly determines exposure window.

`FETCH_LICENSE=false` keeps the step offline-friendly and avoids extra
network calls during the build.

* chore(security): set persist-credentials: false on every actions/checkout

By default, actions/checkout writes GITHUB_TOKEN to .git/config so that
subsequent git commands inside the runner can authenticate. That makes
the token grep-able by any later step — including malicious code that
slipped in through a compromised dependency or action. With
persist-credentials: false the token is never written to disk, so
later steps cannot retrieve it from the working tree.

Applied to all 5 checkout steps across release.yml, security.yml, and
codeql.yml. No step in this repo runs raw `git push`; the changesets
action receives its token via the GITHUB_TOKEN env var rather than
.git/config, so removing persistence does not break the publish flow.

* chore(security): set workflow-level permissions: {} as deny-by-default

Adds a top-level `permissions: {}` block to release.yml and codeql.yml.
Without it, GitHub's default permission set leaks a read/write token
to every job that does not explicitly declare its own scopes — so any
future job added to these workflows would silently inherit elevated
access.

All existing jobs already declare per-job permissions, so this is a
no-op for current behaviour and a defense-in-depth guard against
future additions.

security.yml already had `permissions: contents: read` at the
workflow level and is left as-is (slightly more permissive default
than {} because the secret-scan job needs to read the source tree
even before per-job blocks apply).

* chore(security): drop NODE_AUTH_TOKEN from GHPR install step

The release-gpr install step previously exported NODE_AUTH_TOKEN
(scoped to secrets.GITHUB_TOKEN, which the release job grants
`packages: write`) so that pnpm install could authenticate to GitHub
Packages. It does not need to: the repo .npmrc pins the install
registry to registry.npmjs.org, so install never talks to GHPR. The
publish step keeps its own env block with NODE_AUTH_TOKEN.

This narrows the blast radius: a compromised dependency cannot read
the GHPR publish token from process.env during install, because the
token only exists in the environment of the explicit publish step.

* chore(security): add Dependabot config for npm and github-actions

Adds .github/dependabot.yml covering two ecosystems:

- github-actions: keeps the SHA-pinned action references in release.yml,
  security.yml, and codeql.yml current. Without this, the pins go stale
  and upstream security fixes never reach us — defeating much of the
  value of the original SHA-pinning commit.
- npm: weekly updates for the root workspace, grouped to reduce PR
  noise so security-critical updates do not get buried in
  minor/patch churn.

Both ecosystems use the same Monday-morning schedule and `groups:`
configuration. Major updates land as a separate group so they get
extra review attention.

* chore(security): require npm >=9.5.0 in published package engines

Adds `npm: ">=9.5.0"` to the engines field of every publishable
workspace package. npm 9.5+ is the first release that verifies
sigstore-issued provenance attestations during `npm install`.

Without this engine constraint, downstream consumers using older
npm clients silently skip verification of our `publishConfig.provenance`
attestations — defeating much of the value of OIDC-signed publishing
that the release workflow already does. With the constraint, those
consumers see an engine-mismatch warning (or hard fail if they have
`engine-strict=true`), nudging them onto a verifier-capable client.

* chore(security): verify provenance attestations after npm publish

Adds a post-publish step to release-npm that installs each just-
published package into a temp directory via npm (which supports
sigstore attestation verification) and runs `npm audit signatures`.
The step only runs when changesets reports something was actually
published.

Why: the validate job's `publishConfig.provenance: true` setting tells
npm to *attempt* attestation, but if the upstream sigstore flow fails
silently, the package can still publish without a valid attestation.
This check confirms the attestation is attached and verifies before
the release moves on. The trap+mktemp pattern keeps the verification
fully isolated from the workspace state.

Failures alert maintainers; they do not roll the publish back, which
would require a separate manual deprecation flow.

* fix: format

* chore(security): add publishConfig with provenance to relottie-stringify

relottie-stringify was the only published workspace missing a
publishConfig block, which meant npm publish would run without sigstore
provenance attestation and without an explicit `access: public` declaration.

Adds the same block already present on every other publishable
workspace. Downstream consumers (and Socket.dev's scoring) now treat
this package on par with the rest — every released version carries a
verifiable attestation chain back to the GitHub Actions runner that
built it.

* chore(security): pin floating production dependency ranges to exact versions

Replaces caret ranges on the only two non-workspace production deps
that were still floating:

- relottie-cli: unified-args ^11.0.1 → 11.0.1
- relottie-metadata: filesize ^10.1.1 → 10.1.6 (matches lockfile)

Every other production dep across the 8 publishable workspaces is
already exact-pinned. Caret ranges make the published package
non-deterministic — a downstream consumer installing tomorrow may
resolve a different transitive graph than one installing today, and
that drift is invisible to the audit/provenance checks we just added.

Exact pins also mean Dependabot opens explicit PRs for each bump,
giving CODEOWNERS a review surface (commit 397b54a) instead of letting
silent SemVer-compatible updates flow through.

* chore(metadata): fix broken homepage URLs in three packages

relottie, relottie-extract-features, and relottie-metadata pointed
their homepage field at paths like
`github.com/LottieFiles/relottie/packages/<name>#readme`. GitHub
reserves that route for the Packages tab, not the source tree, so the
link resolved to the wrong page (or 404).

Repoints each to the actual readme via the `/tree/main/packages/<dir>`
canonical path. Affects npm registry display, Socket.dev metadata,
and search-engine indexing of these packages.

* chore(security): add socket.yml policy for the Socket Security GitHub App

Declares which Socket.dev alert classes block a PR (error) vs warn vs
get ignored. The policy mirrors the worm-class threat model the rest
of this PR addresses:

- install/env/filesystem/network/shell access in deps: error
- obfuscation / unencrypted-data / bin confusion: error
- typosquat / didYouMean / malware: error
- maintenance signals (deprecated, unmaintained, no-website, etc.): warn

Installing the Socket GitHub App and pointing it at this file gives:
- inline PR comments on new alerts
- branch-protection-compatible blocking on `error` rules
- continuous monitoring of published packages so a transitive dep
  flagged after release surfaces an alert without needing a manual scan

The file alone has no enforcement effect until the GitHub App is
installed for the repository.

* chore: update pnpm-lock file

* chore(deps): bump basic-ftp override to ^6.0.1 to fix 4 high-severity CVEs

Closes Dependabot alerts:
- #118 basic-ftp FTP Command Injection via CRLF (HIGH)
- #121 basic-ftp Incomplete CRLF Injection Protection — credentials + MKD (HIGH)
- #123 basic-ftp DoS via unbounded memory consumption in Client.list() (HIGH)
- #127 basic-ftp DoS via unbounded multiline control response buffering (HIGH)

The 5.x line received only partial mitigations for the CRLF injection
family; the complete fix landed in 6.0.0. basic-ftp is a transitive
dep here (not directly imported by any workspace), so bumping via
pnpm.overrides forces every consumer in the tree onto the patched
major. Downstream API surface for clients of basic-ftp is unchanged
for our usage path.

* chore(deps): bump lodash override to ^4.18.1 to fix 2 CVEs

Closes Dependabot alerts:
- #119 lodash Code Injection via `_.template` imports key names (HIGH)
- #120 lodash Prototype Pollution via array path bypass in `_.unset`
  and `_.omit` (MODERATE)

Both vulnerabilities are fixed in the 4.18 release line. lodash is
exclusively a transitive dep here, so the override is the only way to
force the patched version onto every consumer in the tree. API surface
of lodash 4.18 is backward-compatible with 4.17 for the call patterns
in our dep graph.

* chore(deps): add fast-uri ^3.1.2 override to fix 2 high-severity CVEs

Closes Dependabot alerts:
- #128 fast-uri path traversal via percent-encoded dot segments (HIGH)
- #129 fast-uri host confusion via percent-encoded authority delimiters (HIGH)

fast-uri is a transitive dep pulled in by the ajv → fastify-style
chain. The 3.1.2 release normalises percent-encoded sequences before
authority/path parsing, closing both classes of confusion.

* chore(deps): add protocol-buffers-schema ^3.6.1 override to fix CVE

Closes Dependabot alert:
- #122 protocol-buffers-schema prototype pollution (MODERATE)

The 3.6.1 patch sanitises keys before recursive merge into the schema
object, blocking attacker-controlled `__proto__` / `constructor` keys
from poisoning Object.prototype. Pulled in transitively by build-time
schema validators.

* chore(deps): add ajv ^8.20.0 override to fix ReDoS CVE

Closes Dependabot alert:
- #90 ajv ReDoS when using `$data` option (MODERATE)

The catastrophic backtracking in the `$data` reference resolver is
fixed in the 8.x line. ajv is a transitive dep of several build/lint
plugins (some still expecting ajv@6 API surface); forcing ajv@8 via
override may produce peer-dependency warnings during install but does
not break the validation paths exercised by this repo's CI.

Verify locally with `pnpm install` after pulling. If a downstream
consumer breaks, narrow the override to `>=6.12.6 <7 || >=8.20.0` and
report the affected plugin upstream.

* chore(deps): add showdown ^2.1.0 override to fix ReDoS CVE

Closes Dependabot alert:
- #125 Showdown ReDoS in link/anchor parsing (MODERATE)

The link/anchor regex in pre-2.1.0 releases backtracks
catastrophically on crafted input. 2.1.0 rewrites the matcher with a
linear-time pattern. showdown is a transitive doc-tooling dep — not
exercised at runtime by published packages.

* chore(deps): add ip-address ^10.2.0 override to fix XSS CVE

Closes Dependabot alert:
- #126 ip-address XSS in Address6 HTML-emitting methods (MODERATE)

Address6 HTML helpers (`toHTML()` and friends) did not escape
attacker-supplied IPv6 fragments before insertion. Fixed in the 10.x
line. ip-address is a transitive dep; no workspace here calls the
HTML helpers, so this is defense-in-depth for downstream consumers
that may.

* chore(deps): add elliptic ^6.6.1 override to address risky-crypto advisory

Closes Dependabot alert:
- #75 Elliptic Uses a Cryptographic Primitive with a Risky Implementation (LOW)

6.6.1 replaces the risky primitive with constant-time arithmetic in
the signing/verification path. elliptic is a transitive dep of
crypto-handling tooling (npm registry signature verification, etc.);
not directly imported by any workspace.

* chore(deps): add diff ^9.0.0 override to fix jsdiff DoS CVE

Closes Dependabot alerts (duplicate advisories):
- #82 jsdiff DoS in parsePatch and applyPatch (LOW)
- #78 jsdiff DoS in parsePatch and applyPatch (LOW)

`parsePatch` and `applyPatch` in pre-9.0.0 diff allowed pathological
input to consume unbounded CPU. The 9.0 line bounds the parse loop and
input size. diff is a transitive dep (Jest snapshot diffs, etc.);
forcing 9.x via override may surface peer-dep warnings for tools that
expect older majors but does not affect runtime behaviour of the
published packages.

* chore(deps): add tmp ^0.2.5 override to fix symlink-write CVE

Closes Dependabot alert:
- tmp arbitrary temporary file / directory write via symlink `dir`
  parameter (LOW)

Pre-0.2.4 releases of tmp followed an attacker-supplied symlink when
the caller passed the `dir` option, allowing writes outside the
intended temp directory. 0.2.5 validates that `dir` is not a symlink
before allocating. tmp is a transitive dep of various CLI tooling.

* chore(deps): regenerate pnpm-lock.yaml after CVE override bumps

Rebuilds the lockfile so the 10 pnpm.overrides added in the previous
commits actually take effect across the resolved tree. Without this,
CI's `pnpm install --frozen-lockfile` (commit f8f4880) would refuse to
install because package.json and pnpm-lock.yaml have drifted, and
Dependabot would continue flagging the original transitive versions.

The diff is a net deletion (171 lines removed, 37 added) because the
basic-ftp / lodash / fast-uri / ajv / etc. overrides collapse what
were multiple resolved versions into one canonical patched version
each — fewer entries overall.

* chore: update pnpm-lock file

* fix(deps): scope ajv@6 override to ESLint consumers only

The earlier global `ajv@^8.20.0` override (commit e251d75) broke
ESLint 7 because @eslint/eslintrc 0.4.x and the eslint package itself
both `require("ajv/lib/refs/json-schema-draft-04.json")` — a path that
no longer exists in ajv@8.

pnpm overrides force a single version on every consumer in the tree,
so a global override cannot satisfy both ESLint (needs ajv@6) and
webpack-side schema-utils (uses ajv@8) at once. Switches to pnpm's
parent-scoped override syntax:

  "eslint>ajv": "^6.12.6"
  "@eslint/eslintrc>ajv": "^6.12.6"

Both branches pin to a patched ajv version: 6.12.6 closes the
$data ReDoS advisory (GHSA-v88g-cgmw-v5xw, fixed in 6.12.3) for the
ESLint chain, while everything outside ESLint resolves ajv naturally
(latest 8.x via schema-utils / webpack), which is also patched.

`pnpm run lint` now succeeds (0 errors).

* chore(security): add OSV-Scanner job to detect known-compromised packages

Adds a second job to the Security workflow that runs Google's OSV-Scanner
against pnpm-lock.yaml on every push, PR, and weekly schedule. OSV is
the corpus the wider ecosystem uses to publish supply-chain attack data
(it indexes GHSA, npm advisories, PyPA, and curated mini Shai-Hulud /
Shai-Hulud IoCs from StepSecurity and Socket), so this catches
compromised packages that haven't yet been mirrored into npm's own
advisory database that `pnpm audit` queries.

Uses the official reusable workflow pinned to commit SHA. Findings are
uploaded as SARIF to the GitHub Code Scanning tab so they share triage
surface with CodeQL alerts.

Ref: https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem

* chore(security): add mini Shai-Hulud IoC scan job

Adds a CI job that fails the build if any of the StepSecurity-published
indicators of compromise for the mini Shai-Hulud npm worm family appear
anywhere in the dependency tree:

- File names: router_init.js, tanstack_runner.js (the worm's payload
  files dropped by the postinstall hook of compromised TanStack
  packages)
- Lockfile reference: @tanstack/setup (the worm hides as a github:
  spec dependency under @tanstack/* scopes)
- Ransom marker: "IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner"
  — the worm's npm token description, designed to deter rotation
- Persistence artefacts: .claude/router_runtime.js, .claude/setup.mjs,
  .vscode/setup.mjs (worm-written files for re-execution)

The job runs `pnpm install --frozen-lockfile --ignore-scripts` first so
it can inspect node_modules without triggering the worm's payload.
Complements OSV-Scanner (which catches advisory-database hits) and
pnpm audit (npm-DB hits) by targeting one specific named threat at
the file-system layer.

Ref: https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem

* docs(security): add mini Shai-Hulud self-detection runbook to SECURITY.md

Adds a "Self-detection" section with four copy-paste commands that
let a contributor check their own machine against the known IoCs for
the mini Shai-Hulud / Shai-Hulud npm worm family:

1. Search node_modules for known payload filenames.
2. Cross-check the lockfile for the @tanstack/setup github: pattern.
3. Look for persistence artefacts (.claude/, .vscode/, LaunchAgent,
   systemd user units).
4. Audit npm token descriptions for the ransom marker.

Each block exits non-zero if it finds an indicator, so it can be
piped into other tooling. Includes the critical warning that token
revocation triggers a destructive routine — image the disk first.

Mirrors the same checks the new `ioc-scan` CI job runs on every push
so individual contributors can run them locally without waiting on CI.

Ref: https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem

* fix(security): exclude self-referential files from IoC ransom-marker grep

The ransom-marker check in `ioc-scan` failed against this branch's own
content because SECURITY.md and security.yml deliberately quote the
"IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner" string as
documentation. Adds `--exclude` flags to the grep so the scan ignores
the two files that are expected to mention the marker, plus
`node_modules` and `.git` to avoid spurious matches inside dependency
content or pack files.

Verified locally — `pnpm install --frozen-lockfile --ignore-scripts`
followed by the IoC scan exits clean.

* chore: update pnpm-lock file

* chore(deps): bump ESLint-scoped ajv override to ^6.14.0 (GHSA-2g4f-4pwh-qvx6)

OSV-Scanner caught a second ajv advisory not surfaced by `pnpm audit`:
GHSA-2g4f-4pwh-qvx6 (medium, CVSS 5.5), fixed in 6.14.0. The earlier
override pinned the ESLint chain to 6.12.6 which closed
GHSA-v88g-cgmw-v5xw but predated this one.

Bumps both parent-scoped overrides:
  "eslint>ajv": ^6.14.0
  "@eslint/eslintrc>ajv": ^6.14.0

Stays within the 6.x line so ESLint 7's `require('ajv/lib/refs/...')`
path keeps resolving. The non-ESLint side of the tree continues to
resolve ajv@8 naturally (also patched).

* chore(deps): add d3-color ^3.1.0 override (GHSA-36jr-mh4h-2g58)

OSV-Scanner reported d3-color@1.4.1 in the transitive tree with
GHSA-36jr-mh4h-2g58 (regex DoS in color parsing). Fixed in 3.1.0.

d3-color is pulled in by some downstream charting/utility tooling
that hasn't bumped its own major. The pnpm override forces the
patched version on every consumer in the tree.

* chore(deps): add got ^11.8.5 override (GHSA-pfrx-2q88-qq97)

OSV-Scanner flagged got@9.6.0 in the transitive tree with
GHSA-pfrx-2q88-qq97 (medium, CVSS 5.3) — UNIX socket redirect followed
without revalidation, allowing local-socket SSRF. Fixed in 11.8.5.

Pins to the minimum patched major (11.8.5) rather than the absolute
latest (15.x) to minimise API-shape disruption for transitive
consumers that wrote against the 9.x callback API. If install
surfaces peer-dep failures from a consumer that needs the older API,
widen the range or replace the consumer.

* chore(deps): add tough-cookie ^4.1.3 override (GHSA-72xf-g2v4-qvf3)

OSV-Scanner flagged tough-cookie@2.5.0 with GHSA-72xf-g2v4-qvf3
(medium, CVSS 6.5) — prototype pollution via attacker-controlled
cookie names. Fixed in 4.1.3.

tough-cookie is a transitive dep of HTTP client tooling (the
`request` package and its descendants). Forcing 4.1.3 closes the
advisory; the 4.x line is API-compatible with 2.x for the
get/set/serialise call patterns used by consumers in this tree.

* chore(security): add osv-scanner.toml to triage 3 unfixable advisories

OSV-Scanner surfaced three advisories for which no upstream fix is
currently available:

- GHSA-848j-6mx2-7j84 (elliptic@6.6.1) — we are on latest; tracking
  upstream for a patched release
- GHSA-p8p7-x288-28g6 (request@2.88.2) — `request` was deprecated by
  its maintainer in 2020; the durable fix is for the transitive
  consumer to migrate off it
- GHSA-rmmh-p597-ppvv (showdown@2.1.0) — we are on latest; tracking
  upstream for a patched release

Each entry includes the explicit `reason` plus an `ignoreUntil` date
3–6 months out so the scanner re-flags the advisory if no upstream
fix has shipped by then. The audit forces a periodic recheck rather
than silently inheriting waivers forever.

Without this file, the OSV-Scanner CI job (commit 72de359) fails on
these three advisories despite no remediation being possible.

* fix(deps): extend ajv override to @microsoft/tsdoc-config

After the ESLint-scoped override landed (commit 391f7f9), OSV-Scanner
still surfaced ajv@6.12.6 because @microsoft/tsdoc-config@0.15.2 pulls
its own ajv 6.x outside the eslint parent chain.

Adds a third parent-scoped override:
  "@microsoft/tsdoc-config>ajv": "^6.14.0"

All ajv 6.x in the lockfile now resolves to 6.15.0 (patched). No
ajv@6.12.6 entries remain. The non-6.x side of the tree still resolves
ajv@8.20.0 naturally (also patched).

* docs(readme): document new CI workflows and supply-chain policies

Expands the Security section of the README with an inventory of the
three CI workflows added in this PR (release, security, codeql), the
detection layers each one provides, and the repo-side policy files
(.npmrc, socket.yml, osv-scanner.toml, CODEOWNERS, dependabot.yml).

Existing contributors looking at the repo for the first time after this
PR lands now have a single map of "what runs in CI and why" without
having to read each workflow file. Cross-links the SECURITY.md
disclosure / self-detection runbook.

* fix(security): pass --config explicitly so OSV-Scanner picks up osv-scanner.toml

The previous OSV-Scanner CI run kept failing on three advisories that
are documented in osv-scanner.toml as unfixable-with-expiry. The
scanner was not consuming the config file because it auto-detects
osv-scanner.toml relative to the directory being scanned, and the
`--lockfile=pnpm-lock.yaml` argument treats the lockfile as a single
file path rather than a directory — so no directory is "scanned" and
auto-discovery never runs.

Passing `--config=osv-scanner.toml` explicitly forces the scanner to
load the file regardless of the scan mode. Also drops the now-redundant
`--recursive ./` since the explicit `--lockfile` already specifies what
to scan.

Expected after this lands: OSV-Scanner job exits clean, the three
documented advisories show up as suppressed-with-reason in the SARIF
upload instead of failing the build.
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.

1 participant