Skip to content

chore(osv): exempt sprintf-js GHSA-hp3w-g68c-fv3c until 2026-11-05 (no fixed release exists) - #21952

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21945-osv-exemption
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21945-osv-exemption

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #21945

Clause-②: no

What this does

This PR adds one [[IgnoredVulns]] entry to osv-scanner.toml, for GHSA-hp3w-g68c-fv3c (sprintf-js 1.1.3, DoS through unbounded precision specifiers, CVSS 6.9). It is the only one of main's four OSV advisories with no fixed release. The ledger header's convention 3 says an exemption lands in its own osv-exemption-labelled PR, so this PR carries that entry and nothing else. The other three advisories are fixed in #21951 (lockfile re-lock plus a katex override).

Which half this leaves: the three fixable advisories, which are #21951's. This PR alone does not turn the OSV step green, and neither does #21951 alone. The card stays open when this PR merges, and the PM finishes it after both have landed.

[[IgnoredVulns]]
id = "GHSA-hp3w-g68c-fv3c"
ignoreUntil = 2026-11-05
reason = "https://github.com/advisories/GHSA-hp3w-g68c-fv3c — no fixed sprintf-js exists (1.1.3, the latest release, is the last affected version), and it arrives only transitively through tedious 18.6.2 (driver-sql's optional mssql peer) and fengari 0.1.5 (under ioredis-mock, a service-cluster-redis devDependency), whose latest releases (tedious 20.3.3, fengari 0.1.5) still require ^1.1.3; remove when sprintf-js publishes a fix or both parents drop it."

The entry follows the header's conventions:

  • Convention 1: ignoreUntil is a bare TOML date, 30 days out, which is the default and well under the 90-day ceiling.
  • Convention 2: reason is the advisory URL, an em dash, then one sentence on why the advisory cannot be fixed right now.
  • One id only: the scanner applies an exemption to the advisory's aliases too, so the CVE alias (CVE-2026-97058) gets no entry of its own.

Exemption decision

There is no fix to take

Fact Reading
Advisory range (OSV offline npm DB, downloaded 2026-10-06) introduced: 0 up to last_affected: 1.1.3, so there is no fixed event
npm view sprintf-js version 1.1.3 (published 2023-09-11, the latest)
tedious (latest 20.3.3, next 20.3.4) every major checked (18.6.2, 19.0.0, 19.2.1, 20.0.0, 20.3.3, 20.3.4) declares sprintf-js ^1.1.3
fengari (latest 0.1.5) declares sprintf-js ^1.1.3
pnpm why -r sprintf-js one copy, 1.1.3; the only parents are fengari@0.1.5 and tedious@18.6.2

No override can help, because there is no version to override to. Moving either parent to a newer release changes nothing either.

Where each parent is used in this workspace

  • tedious 18.6.2 is the MSSQL driver. @objectstack/driver-sql declares it as an optional peer (packages/drivers/driver-sql/package.json:35, with optional: true at :44). The workspace installs a project's own peers (.npmrc auto-install-peers=true), and knex@3.3.0 picks it up as its optional mssql peer, both under driver-sql and under driver-sqlite-wasm.
    • In this repository it is reached only by driver-sql tests that build a client: 'mssql' driver without a server: sql-driver-date-bucket.test.ts:163, sql-driver-text-case-conformance.test.ts:366, sql-driver-20446-empty-flip.test.ts:168 and sql-driver-20987-json-membership-move.test.ts:163.
    • The CLI's bundler keeps it external (packages/cli/src/utils/config.ts:95).
    • A downstream install that uses mssql brings its own tedious, and so its own sprintf-js. This entry does not reach that install, and nothing here can.
  • fengari 0.1.5 is a Lua VM in JavaScript. It sits under ioredis-mock 8.13.1, directly and through fengari-interop, and ioredis-mock is a devDependency of @objectstack/service-cluster-redis (package.json:33).
    • It runs only in that package's tests, where ioredis-mock executes the package's two Lua scripts. Both are constants: RELEASE_SCRIPT at src/lock.ts:18 and RENEW_SCRIPT at src/lock.ts:30, sent by the eval calls at src/lock.ts:163 and :186.
    • In production, ioredis sends EVAL to a real Redis, and fengari is never loaded. The published package depends on ioredis only.

Does either path pass an untrusted format string to sprintf?

The advisory's precondition is an attacker who controls the format string, so that a precision specifier outside what toFixed / toExponential / toPrecision accept throws a RangeError.

  • tedious: no. All 12 sprintf calls in tedious@18.6.2/lib take a string-literal format, and data only ever enters as an argument:
    • value-parser.js:409 and :485;
    • metadata-parser.js:104 and :355;
    • prelogin-payload.js:204;
    • login7-payload.js:397 (four calls);
    • packet.js:131, :144 and :155.
  • fengari: only from Lua source, and only partly. The Lua script supplies the format.
    • string.format (fengari/src/lstrlib.js:334) passes the script's own format to sprintf at :361, :373 and :391.
    • It does so only after scanformat (:301) caps width and precision at two digits and raises invalid format (width or precision too long) (:314). So the advisory's over-100 precision never reaches sprintf. Measured: string.format("%.100f", 1.5) is a Lua error, and "%.99f" formats normally.
    • The lower edge, "%.0g" (toPrecision(0)), does still reach sprintf. It fails the Lua call (measured: non-zero status).
    • The other three sprintf calls in that file (:172, :188 and :285) use constant formats.
    • Exposure therefore needs an attacker who can write the Lua source that ioredis-mock runs. In this workspace the only Lua source is the two constant scripts above, in tests.

Net: this repository passes no untrusted format string to sprintf on any path.

Routes considered and not taken

  • Drop tedious from driver-sql's optional peers. This removes a published peer declaration, which is a contract change for MSSQL users. The four tests above build an mssql client, and their own comments say knex resolves tedious from this workspace, so they would need a different stand-in client (not measured here). It would also not clear fengari. That is outside this card.
  • Replace ioredis-mock. That would mean a test-double migration for service-cluster-redis, whose version pair is pinned in src/ioredis-pair.pin.test.ts. It would not clear tedious. Also outside this card.

Both parents would have to go for the advisory to leave the lockfile, so neither route is a fix on its own.

Renewal trigger

ignoreUntil = 2026-11-05. After that date the scanner stops filtering the advisory and the step goes red on its own. Remove the entry when sprintf-js publishes a fix, or when both tedious and fengari drop it. Removing it can ride along with that fix, while a renewal is a new decision and needs its own osv-exemption PR.

OSV reading at this head (e6a3636f)

Measured locally with OSV-Scanner v2.3.8, the version validate-deps.yml pins. api.osv.dev answers 403 from this container, so the scan ran in offline mode against the OSV npm database the scanner downloaded on 2026-10-06. Exit 1, with sprintf-js filtered and three rows left, which #21951 fixes:

GHSA-hp3w-g68c-fv3c and 1 alias have been filtered out because: https://github.com/advisories/GHSA-hp3w-g68c-fv3c — no fixed sprintf-js exists …
Filtered 1 vulnerability from output
| https://osv.dev/GHSA-238p-pmpm-9mq7 | 2.1  | npm       | katex         | 0.16.47 | 0.18.2        | pnpm-lock.yaml |
| https://osv.dev/GHSA-jqcg-44mw-7w3h | 9.1  | npm       | proxy-addr    | 2.0.7   | 2.0.8         | pnpm-lock.yaml |
| https://osv.dev/GHSA-68fv-2mgg-jv7q | 8.7  | npm       | source-map-js | 1.2.1   | 1.2.2         | pnpm-lock.yaml |

This ledger scanned with #21951's lockfile: exit 0, No issues found.

Changeset

skip-changeset. osv-scanner.toml is repo-root configuration and ships in no package's files[].

Local verification, at e6a3636f

The gates come from node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at this head: 13 commands. The --ran reconciliation, with exit codes recorded before any pipe, is 13 derived, 13 run, 0 NOT-MEASURED, 0 UNRUN:

  • node scripts/check-changeset-fixed.mjs: 0.
  • node scripts/check-closing-keyword-parity.mjs (+ --self-test): 0 / 0.
  • node scripts/check-comment-mask-corpus.mjs: 0.
  • node scripts/check-osv-exemptions.mjs (+ --self-test): 0 / 0. The verdict line reads ✓ 1 OSV exemption(s) in osv-scanner.toml: all carry an unexpired ignoreUntil within 90 days and a reason with an advisory link.
  • pnpm check:driver-memory-census · check:gitlink-declared · check:nul-bytes · check:override-consistency · check:refd-timer-probe · check:vendor-export-contract-resolve · check:watch-hint-literal: 0 each.

Acceptance notes

  • The ledger's header still says "This ledger currently holds ZERO exemptions." Once this entry lands, that sentence is no longer true. This PR leaves the header byte-identical so that its diff is the entry alone. If the reviewer wants it, the one-line count edit can go into this same PR.
  • The osv-exemption label did not exist in this repository before this PR: GET /labels/osv-exemption answered 404. No exemption has landed since the convention was written. The additive label write on this PR creates it.

Generated by Claude Code

No fixed sprintf-js exists: 1.1.3, the latest release, is the last
affected version. It reaches the lockfile only through tedious 18.6.2
(driver-sql's optional mssql peer) and fengari 0.1.5 (under
ioredis-mock, a service-cluster-redis devDependency), and the latest
release of each still declares ^1.1.3. The entry carries a bare-date
ignoreUntil 30 days out and an advisory-linked reason, per the
ledger's header conventions.

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added skip-changeset PR has no user-facing published change; bypasses the changeset gate osv-exemption labels Oct 6, 2026
…ntry stands

The header said the ledger "currently holds ZERO exemptions", which
stops being true the moment the sprintf-js entry lands. It now states
zero as the intended steady state and says every entry is a dated
exception with its own renewal date. The rest of the header is
byte-identical.

Claude-Session: https://claude.ai/code/session_01VF48aw8RPG6wzDnMgp6rtw
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Correction to this PR's body, from the domain:devx seat 2 reviewer (session_01VF48aw8RPG6wzDnMgp6rtw), 2026-10-06T05:13Z. The dev's PR bodies are write-once, so the correction is recorded here.

The body says the local scan used "OSV-Scanner v2.3.8, the version validate-deps.yml pins". That is not what CI runs. validate-deps.yml:142 pins the action by sha with the comment # v2.3.8, but the job pulls the image ghcr.io/google/osv-scanner-action:v2.5.0. Scheduled run 37407261685, job 112087469784, step "Run google/osv-scanner-action/…" shows that pull.

Corrected reading: the local scans were measured with OSV-Scanner v2.3.8 and v2.5.0 (the image the CI job pulls). Both give identical rows on every tree. The verdicts in the body are unchanged.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 — 接受 sprintf-js 的 OSV 豁免(#21945)

席位 domain:devx#2 · session_01VF48aw8RPG6wzDnMgp6rtw · 2026-10-06T05:16Z · 对照 head ba17ca5b 的 diff 写成。

改了什么

  • osv-scanner.toml 加 1 条 [[IgnoredVulns]]:GHSA-hp3w-g68c-fv3c(sprintf-js 1.1.3,格式串精度说明符过大导致抛错的 DoS,中危 6.9)。
  • 到期日 2026-11-05(30 天),到期后扫描自动恢复报红。
  • 表头那句「目前零豁免」顺手改成「目标是零豁免,每条都是带到期日的例外」。这是本仓 OSV 账本的第一条豁免。

为什么改

风险与代价(含回滚)

  • 实际可触达面很小:
    • tedious 是 driver-sql 的可选 MSSQL 依赖,本仓只在测试里用到。它 12 处 sprintf 调用的格式串全是写死的字面量。
    • fengari 只在 service-cluster-redis 的测试 mock 里跑,生产环境不加载。它的 string.format 已把精度限制在两位数以内,触发不到这个漏洞。
  • 下游用户自己装 MSSQL 驱动时会带上自己的 sprintf-js,本豁免管不到,也不影响他们。
  • 回滚:删掉这条豁免即可。到期日一过,扫描也会自己重新报红。

席位意见

你要做的(一个动作)

  • 在本 PR 回复「接受」:即批准这条豁免,并授权席位把本 PR 带着预期的 Validate Package Dependencies 红入队。
  • 不接受就回复理由,席位改走别的路。

Governing text: osv-scanner.toml header, conventions 1–3 (decided on #4965); .claude/skills/pm-dispatch/SKILL.md 〈路径分叉〉 enqueue eligibility (every check green, or the three-condition by-design red).


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Maintainer ruling: the sprintf-js exemption is accepted, and this PR lands first with its expected red

Recorded by domain:devx seat 2 (session_01VF48aw8RPG6wzDnMgp6rtw) at 2026-10-06T06:17Z.

Ruling (maintainer, os-justin, answering the seat's question in that session's chat), verbatim and untranslated:

Accept exemption, #21952 first (Recommended)

The option the maintainer chose read: "Approve the sprintf-js exemption (expires 2026-11-05, only reachable from tests and test mocks). The seat queues #21952 with the expected red. After it lands, #21951 picks up main, goes fully green and lands through the queue normally."

The red this PR is enqueued with:

  • Which check: Validate Package Dependencies (job 112119410758 at ba17ca5b).
  • Cause: OSV still reports katex 0.16.47, proxy-addr 2.0.7 and source-map-js 1.2.1. All three are fixed by fix(deps): take the fixes for proxy-addr, source-map-js and katex that turn main's OSV scan red #21951, which lands next, and sprintf-js is filtered by this PR's entry.
  • Not a required context, and it does not run on merge_group.
  • Every other check on ba17ca5b is green, including Lint & Repo Gates and TypeScript Type Check.
  • Governed-surface audit: 0 of 1 path governed. The body carries no closing keyword (Part of #21945).

Next: pr_ready + automerge_enable now. Once this is on main, #21951 is synced (update-branch), its OSV step is confirmed green, and it lands through the queue. Then #21945 closes.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 06:19
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 06:19
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit 6209964 Oct 6, 2026
35 of 36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21945-osv-exemption branch October 6, 2026 06:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

osv-exemption size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants