Repository navigation
Commit d0c56a2
fix(cli,create-objectstack): scaffolds override uuid below 12.0.0 to ^11.1.1, out of GHSA-w5hq-g745-h8pq (#25197)
Fixes #25174
Clause-②: no
Metadata-protocol: none
## What this changes
Every standalone scaffold's npm `overrides` block gains a `uuid` entry
that routes every `uuid` edge below 12.0.0 to `^11.1.1`, out of
GHSA-w5hq-g745-h8pq (`uuid` below 11.1.1). The scaffolds are the
`create-objectstack` blank template, `os init -t app`, `-t plugin` and
`-t empty`, and `os create plugin` (standalone).
- `packages/cli/src/commands/init.ts`: two new constants,
`SCAFFOLD_UUID_OVERRIDE_SELECTOR` (the npm override key: `uuid@`, a
less-than sign, `12.0.0`) and `SCAFFOLD_UUID_OVERRIDE_RANGE`
(`^11.1.1`). `SCAFFOLD_NPM_OVERRIDES` is built from them and from
`SCAFFOLD_VITEST_RANGE`, so `os init` and `os create` emit it with no
other code change.
- `scripts/sync-scaffold-emission-policy.mjs`: one new `POLICY_STAMPS`
row stamps the value into the bundled template from the constant (the
same path PR #25173 used for vitest). The field regex now escapes the
key, because the selector contains `.`. The self-test fixtures declare
the new constant and key, with one new assertion (61 assertions in
total).
- `packages/create-objectstack/src/templates/blank/package.json`: the
stamped entry, written by the script's write run.
- Tests: `packages/cli/test/scaffold-npm-overrides.test.ts` pins the
map, the target's floor (at or above the fix for its major) and the
selector's bound (the major right above the target).
`packages/cli/test/scaffold-emission-policy.e2e.test.ts` (nightly tier)
adds a check that the on-ramp, `os init` and `os create` emit the SAME
whole `overrides` block. The sync script's `--check` compares values
only and cannot see a key spelled differently from the constant.
- Changeset: `@objectstack/cli` and `create-objectstack`, patch. It
includes the one-line fix for existing projects and for other npm
consumers.
`@objectstack/rest` is not changed: no route at its level reaches npm
consumers (measured below).
## Route measurements (npm 10.9.9 and npm 11.21.1)
Method: each manifest in its own empty directory under the scratchpad,
with a fresh `--cache` per leg, and one install tree at a time. The
`@objectstack/*` ranges `^18.0.0` resolve to the published 18.0.0. The
`os init` / `os create` manifests were emitted by this tree's source CLI
(`bin/run-dev.js`), and the blank template was taken from the tree.
`uuid` was read from `node_modules`, then `npm audit --json` was run.
Two npm 10 audits and one npm 11 audit hit a registry endpoint error
(400 on `audits/quick` after the bulk endpoint failed, and a 503 on
`advisories/bulk`). Those were re-run, and only completed audits are
counted.
| Route | Manifests | npm 10.9.9 | npm 11.21.1 |
|---|---|---|---|
| Baseline (main at b6e8707, with #25173) | blank, init -t app, -t
plugin, -t empty | exit 0, `uuid` 8.3.2, audit 9 moderate, all
GHSA-w5hq-g745-h8pq | same |
| Baseline | os create plugin (no `@objectstack/rest` in its closure) |
exit 0, no `uuid`, audit 0 | same |
| A: newer exceljs | n/a | no such release: `latest` is 4.4.0
(2023-10-19), and `4.4.1-prerelease.0` also declares `uuid ^8.3.0` |
same |
| B: `overrides` inside the published `@objectstack/rest` (18.0.0
repacked with `overrides.uuid ^11.1.1`, installed as a dependency) |
bare consumer | exit 0, `uuid` 8.3.2, audit 3 moderate (rest, exceljs,
uuid): **ignored** | same, **ignored** |
| C: scaffold `overrides`, selector `uuid@` + less-than `12.0.0` →
`^11.1.1` (**chosen**) | blank, init -t app, -t plugin, -t empty | exit
0, `uuid` 11.1.1, audit 0 | same |
| C | os create plugin | exit 0, no `uuid`, audit 0 (the entry is inert)
| same |
| C': the same with a bare `uuid` key | bare `@objectstack/rest`
consumer | exit 0, `uuid` 11.1.1, audit 0 | same |
| D: replace the dependency (`exceljs` aliased to the
`exceljs-community@5.2.1` fork, as a published swap would resolve) |
blank; bare consumer | exit 0, no `uuid`, audit 0 | same |
Follow-up on a consumer of the published `@objectstack/rest` 18.0.0,
root override as in C or C'. A later `npm install uuid@14` exits 0 under
C (declares `^14.0.3`, exceljs keeps 11.1.1), and exits 1 with
`EOVERRIDE` under C'. A spec-less `npm install uuid` exits 1 with
`EOVERRIDE: Override for uuid@* conflicts with direct dependency` under
both. Same on both npm versions. This is why the key is a selector and
not bare `uuid`.
**Runtime under route C (H3), measured rather than read from a
changelog.** On the installed blank and `os init -t app` trees, on both
npm versions, exceljs resolved `uuid` 11.1.1. A buffered write with a
`dataBar` rule ran: `CfRuleExtXform.prepare` calls `uuid.v4()`, and the
sheet carried 2 generated ids. The streaming `WorkbookWriter` ran, the
shape `createXlsxStream` bridges onto HTTP. A buffered `load` ran.
`@objectstack/rest`'s own `parseXlsxToRows` read both workbooks back to
the expected rows. exceljs calls no other `uuid` export. In the
monorepo, the same pairing (exceljs 4.4.0 on `uuid` 11.1.1, through the
workspace override) runs the 16 rest test files that touch xlsx: 16
passed, 737 tests.
## Why C, by the four axes
- **Actual business need:** the measured reach is every fresh
npm-installed scaffold (9 entries). C closes exactly that reach, and B
does not reach it at all. D also closes it for every npm consumer, not
only scaffolds.
- **Long-term soundness:** C uses the mechanism this repo already uses
for the same advisory (the workspace's pnpm override, same selector and
range), so a scaffold resolves the `uuid` the monorepo's exceljs suites
run on. It has a written retirement re-check on the constant. D would
move a published runtime dependency onto a fork. The measured candidate
is maintained by one person, was created 2026-08-09 and declares
`engines.node >=22.12.0`, narrower than `@objectstack/rest`'s
`>=22.0.0`. That is a supply-chain decision, not a dependency bump.
- **Hard for AI to get wrong:** one constant feeds all three
scaffolders. The value is stamped, and the key is held equal by the
nightly e2e check. Floor and selector pins red when the advisory
boundary is left. Both ablations are below.
- **Startup focus:** no new dependency, and no change to any published
package's runtime. It adds one map entry and one stamp row.
## Acceptance notes
- **Residual reach, by design and stated in the changeset:** an npm
project that installs `@objectstack/rest` other than through a fresh
scaffold still audits GHSA-w5hq-g745-h8pq. This covers projects made
before this change and hand-written projects, and the bare consumer
measured 3 moderate entries on npm 10.9.9 and 11.21.1. npm reads
`overrides` only from the root manifest (route B), so only replacing
`exceljs` (route D) reaches them. That choice is raised for the
maintainer in the report on #25174, and is not made here.
- Severity, as each source states it (H4): the `pnpm-workspace.yaml`
comment says "high". The npm registry's bulk advisory endpoint says
`moderate`, with a CVSS 3.1 vector scoring 7.5. `npm audit` in the
scaffolds says moderate. GitHub's global advisory API was not readable
from this session (403, session-bound).
- `scripts/sync-scaffold-emission-policy.mjs`'s `devDependencies.vitest`
row comment still says the constant holds why the range is `~4.0.0`. The
range has been `~4.1.12` since PR #25173. The comment is stale, has no
runtime effect and is outside this change. Carrier: none.
## Verification
All readings are at head `50d7f9c25`, which is this branch with
`origin/main` `f7d740376` merged in. The two incoming commits touch
`cli` migrate code and `platform-objects` translations, and none of
these 6 paths. After the merge: `pnpm install` was unchanged (no
lockfile or manifest moved), the non-docs build was 76/76, and
`packages/runtime/.objectstack` was cleared.
- `pnpm --filter @objectstack/cli exec vitest run --project unit
--maxWorkers=2` under the verify lock: VERDICT command-exit 0, 323 test
files passed, 4772 tests passed. The CLI integration tier is left to CI.
The one integration-tier file in this diff is in the nightly tier, so it
was run on its own: `OS_TEST_TIERS=nightly … vitest run --project
integration test/scaffold-emission-policy.e2e.test.ts`, 1 file, 11 tests
passed.
- `pnpm --filter create-objectstack test`: 18 files, 278 tests passed.
`pnpm --filter @objectstack/cli typecheck && pnpm --filter
create-objectstack typecheck`: VERDICT command-exit 0. The cli `tsc -p
tsconfig.test.json --listFiles` lists both edited cli test files.
- `@objectstack/rest` xlsx export and import tests (the 16 files under
`packages/rest/src` that touch xlsx or exceljs): 16 files, 737 tests
passed. `@objectstack/rest` is unchanged; these run exceljs on `uuid`
11.1.1 through the workspace override.
- `node scripts/sync-scaffold-emission-policy.mjs --self-test`: exit 0,
61 assertions. Its `--check` exited 1 on an unstamped key
(`overrides.uuid@`… is `"0.0.0-unstamped"` but the policy declares
`"^11.1.1"`). The write run then stamped it (1 value rewritten), and
`--check` exited 0.
- eslint `--no-inline-config` over the 4 changed code files: exit 0.
Control-byte scan over the 6 changed files: no hits.
- **Ablations**, run at `e7cf02f09` with the fix committed first,
through `scripts/ablation-replace.mjs` in wrap mode. The later commits
change only comments in `init.ts`. The tests import `../src`, so no
`dist/` is on their path.
1. `SCAFFOLD_UUID_OVERRIDE_RANGE` changed from `^11.1.1` to `^11.1.0`
(anchor 1 to 0, blob `827cf1e96177` to `33bed46cc4ef`). Result: `Tests 1
failed | 13 passed (14)`, failing on `admits 11.1.0, below the fix
11.1.1`. Restored: blob equals HEAD, `git diff HEAD` empty.
2. `SCAFFOLD_UUID_OVERRIDE_SELECTOR` with its bound moved to `13.0.0`
(anchor 1 to 0, blob `827cf1e96177` to `98fd9e5105ca`). Result: unit `1
failed | 13 passed (14)` (`expected 13 to be 12`), and nightly e2e `1
failed | 10 passed (11)` (`npx create-objectstack must emit the shared
npm overrides block`). That second failure is the key-spelling drift the
stamp script cannot see. Restored: blob equals HEAD, `git diff HEAD`
empty.
- Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 103 commands over 6 paths, against
merge base `f7d740376`. Each ran byte for byte, with its exit code
recorded before any pipe: all 103 exited 0. `--ran`: `103 derived, 103
run, 0 NOT-MEASURED, 0 UNRUN`, exit 0. This includes
`check:scaffold-emission-policy`, `check:template-version-sync`,
`check:cross-package-test-inputs` and `check:pm-dispatch-gates`.
`check-governed-merges --branch`: 0 of 6 paths governed, 220 changed
lines.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01AFrKwndmrCLtF5V9hNtrw3)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2919ddb commit d0c56a2
6 files changed
Lines changed: 208 additions & 12 deletions
File tree
- .changeset
- packages
- cli
- src/commands
- test
- create-objectstack/src/templates/blank
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
434 | 434 | | |
435 | 435 | | |
436 | 436 | | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
437 | 508 | | |
438 | 509 | | |
439 | 510 | | |
440 | | - | |
441 | | - | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
442 | 518 | | |
443 | | - | |
| 519 | + | |
444 | 520 | | |
445 | 521 | | |
446 | 522 | | |
| |||
487 | 563 | | |
488 | 564 | | |
489 | 565 | | |
| 566 | + | |
490 | 567 | | |
491 | 568 | | |
492 | 569 | | |
| |||
570 | 647 | | |
571 | 648 | | |
572 | 649 | | |
573 | | - | |
| 650 | + | |
| 651 | + | |
574 | 652 | | |
575 | 653 | | |
576 | 654 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| 81 | + | |
81 | 82 | | |
82 | 83 | | |
83 | 84 | | |
| |||
421 | 422 | | |
422 | 423 | | |
423 | 424 | | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
424 | 444 | | |
425 | 445 | | |
426 | 446 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
29 | | - | |
30 | | - | |
| 30 | + | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| 51 | + | |
| 52 | + | |
50 | 53 | | |
51 | 54 | | |
52 | 55 | | |
| |||
84 | 87 | | |
85 | 88 | | |
86 | 89 | | |
87 | | - | |
| 90 | + | |
88 | 91 | | |
| 92 | + | |
| 93 | + | |
89 | 94 | | |
90 | 95 | | |
91 | 96 | | |
| |||
143 | 148 | | |
144 | 149 | | |
145 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
167 | 181 | | |
168 | 182 | | |
169 | 183 | | |
| |||
332 | 346 | | |
333 | 347 | | |
334 | 348 | | |
335 | | - | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
336 | 352 | | |
337 | 353 | | |
338 | 354 | | |
| |||
630 | 646 | | |
631 | 647 | | |
632 | 648 | | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
633 | 656 | | |
634 | | - | |
635 | 657 | | |
636 | 658 | | |
637 | 659 | | |
| |||
642 | 664 | | |
643 | 665 | | |
644 | 666 | | |
| 667 | + | |
| 668 | + | |
645 | 669 | | |
646 | 670 | | |
647 | 671 | | |
| |||
661 | 685 | | |
662 | 686 | | |
663 | 687 | | |
664 | | - | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
665 | 692 | | |
666 | 693 | | |
667 | 694 | | |
| |||
813 | 840 | | |
814 | 841 | | |
815 | 842 | | |
| 843 | + | |
| 844 | + | |
| 845 | + | |
| 846 | + | |
| 847 | + | |
| 848 | + | |
816 | 849 | | |
817 | 850 | | |
818 | 851 | | |
| |||
0 commit comments