@bongos/core 1.19.862 → 1.19.864
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +20 -15
- package/docs/adr/0010-pms-architecture.md +1 -1
- package/docs/adr/0020-security-model-public-stub.md +1 -1
- package/docs/adr/0022-secrets-policy.md +1 -1
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +4 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/docs_scrubber_damage.mjs +111 -0
package/.bongos-core.json
CHANGED
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.864",
|
|
6
|
+
"core_contract": "1.19.864",
|
|
7
|
+
"source_commit": "c073f3cd57b680341105e1e2f06e3a5c0ebb3358",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-18T01:11:02.555Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 500,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2281,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2807,
|
|
20
|
+
"tree_sha256": "605295e9a15f1948dd81353eab191ae7c28bbf4c6859146311c02ffc16a3e235",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -467,7 +467,7 @@
|
|
|
467
467
|
{
|
|
468
468
|
"path": "docs/adr/0010-pms-architecture.md",
|
|
469
469
|
"mode": "0000644",
|
|
470
|
-
"sha256": "
|
|
470
|
+
"sha256": "30193a4833d15edc09bf75b41451e1ac25d48543dcbdae5c3c210e2cb9493f40"
|
|
471
471
|
},
|
|
472
472
|
{
|
|
473
473
|
"path": "docs/adr/0011-terrain-id-rendering-model.md",
|
|
@@ -517,7 +517,7 @@
|
|
|
517
517
|
{
|
|
518
518
|
"path": "docs/adr/0020-security-model-public-stub.md",
|
|
519
519
|
"mode": "0000644",
|
|
520
|
-
"sha256": "
|
|
520
|
+
"sha256": "6b90acde1797bec054e17dd343883fc4dd2a6d8d1a9902f53da0d7fb21f94b2a"
|
|
521
521
|
},
|
|
522
522
|
{
|
|
523
523
|
"path": "docs/adr/0021-per-builder-skill-model-preferences.md",
|
|
@@ -527,7 +527,7 @@
|
|
|
527
527
|
{
|
|
528
528
|
"path": "docs/adr/0022-secrets-policy.md",
|
|
529
529
|
"mode": "0000644",
|
|
530
|
-
"sha256": "
|
|
530
|
+
"sha256": "3d63f192c4c596f1840f057ddd1686f2ed4504eb7712965d57ee2173580d3c58"
|
|
531
531
|
},
|
|
532
532
|
{
|
|
533
533
|
"path": "docs/adr/0023-kind-multipliers-and-idea-bonus.md",
|
|
@@ -2592,7 +2592,7 @@
|
|
|
2592
2592
|
{
|
|
2593
2593
|
"path": "docs/file-map.md",
|
|
2594
2594
|
"mode": "0000644",
|
|
2595
|
-
"sha256": "
|
|
2595
|
+
"sha256": "38c851145d7b4066a378b1af75747b6cc28529b7f824bd6b7e30e0502bc6346c"
|
|
2596
2596
|
},
|
|
2597
2597
|
{
|
|
2598
2598
|
"path": "docs/handoff-template.md",
|
|
@@ -2602,7 +2602,7 @@
|
|
|
2602
2602
|
{
|
|
2603
2603
|
"path": "docs/module-api-changelog.md",
|
|
2604
2604
|
"mode": "0000644",
|
|
2605
|
-
"sha256": "
|
|
2605
|
+
"sha256": "efb0a55093a321e95fbbbe4d5a0d074ef2cd3bd0148dc820fe338027465d2aed"
|
|
2606
2606
|
},
|
|
2607
2607
|
{
|
|
2608
2608
|
"path": "docs/modules-contract.md",
|
|
@@ -7927,12 +7927,12 @@
|
|
|
7927
7927
|
{
|
|
7928
7928
|
"path": "package-lock.json",
|
|
7929
7929
|
"mode": "0000644",
|
|
7930
|
-
"sha256": "
|
|
7930
|
+
"sha256": "ea96e5b57ed6cd9e4a043090b7ab48ac78b8f058b2c9767d77633a20e528ff6b"
|
|
7931
7931
|
},
|
|
7932
7932
|
{
|
|
7933
7933
|
"path": "package.json",
|
|
7934
7934
|
"mode": "0000644",
|
|
7935
|
-
"sha256": "
|
|
7935
|
+
"sha256": "feed56a36e8348a1790db370c1ea4a6896a6e120a490116212625d8317ff4cf6"
|
|
7936
7936
|
},
|
|
7937
7937
|
{
|
|
7938
7938
|
"path": "public-docs/index.html",
|
|
@@ -9877,7 +9877,7 @@
|
|
|
9877
9877
|
{
|
|
9878
9878
|
"path": "src/module-api.js",
|
|
9879
9879
|
"mode": "0000644",
|
|
9880
|
-
"sha256": "
|
|
9880
|
+
"sha256": "3ee60c835967094a7b5c983e651e0ada54b2127b2b38a329ecf8d6a9237cb0d2"
|
|
9881
9881
|
},
|
|
9882
9882
|
{
|
|
9883
9883
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11034,6 +11034,11 @@
|
|
|
11034
11034
|
"mode": "0000644",
|
|
11035
11035
|
"sha256": "acc6c86bfb9edaee192e9c363f4b578a01e1bba69422d618987d8ff75e883a63"
|
|
11036
11036
|
},
|
|
11037
|
+
{
|
|
11038
|
+
"path": "tests/docs_scrubber_damage.mjs",
|
|
11039
|
+
"mode": "0000644",
|
|
11040
|
+
"sha256": "2d815aa719c9b83af094405b02dcb50c031267fd38cdd9306c81d592597966dd"
|
|
11041
|
+
},
|
|
11037
11042
|
{
|
|
11038
11043
|
"path": "tests/doctor_hooks.mjs",
|
|
11039
11044
|
"mode": "0000644",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
- **Status:** Accepted
|
|
4
4
|
- **Date:** 2026-05-09
|
|
5
5
|
- **Deciders:** Lars
|
|
6
|
-
- **Addendum (V3.R01, 2026-05-13):** "PMS" was renamed to "GDS" (Game Development System) when V3 opened the system to multiple builders. Folder/route/env-var renames: `src/pms/` → `src/bongos/`, `scripts/pms/` → `scripts/gds/`, `/api/pms/*` → `/api/gds/*`, `PMS_GITHUB_*` → `GDS_GITHUB_*`, `PMS_API_BASE` → `GDS_API_BASE`, `pms_session` cookie → `gds_session`, `~/.config/otb/pms-session.json` → `~/.config/otb/gds-session.json`. The version ID `PMS-V3` was also renamed to `GDS-V3` (migration
|
|
6
|
+
- **Addendum (V3.R01, 2026-05-13):** "PMS" was renamed to "GDS" (Game Development System) when V3 opened the system to multiple builders. Folder/route/env-var renames: `src/pms/` → `src/bongos/`, `scripts/pms/` → `scripts/gds/`, `/api/pms/*` → `/api/gds/*`, `PMS_GITHUB_*` → `GDS_GITHUB_*`, `PMS_API_BASE` → `GDS_API_BASE`, `pms_session` cookie → `gds_session`, `~/.config/otb/pms-session.json` → `~/.config/otb/gds-session.json`. The version ID `PMS-V3` was also renamed to `GDS-V3` (migration `019_rename_pms_v3_to_gds_v3.sql` rewrites `versions`, `tasks`, `done_when_criteria`, `idea_inbox`, plus the `pms-shipper` achievement → `gds-shipper`). Future internal-track versions ship as `GDS-V4`, `GDS-V5`, etc. The version IDs `PMS-V1` and `PMS-V2` are NOT renamed — those versions shipped under those names and their archives in `limitations/pms-v*-shipped.md` are immutable historical records.
|
|
7
7
|
|
|
8
8
|
V3.R01 also introduced a transitional `/api/pms/* → /api/gds/*` redirect surface (301 for GET/HEAD, 308 for POST/PATCH/DELETE) so old skill files and cached browser clients would keep working through the rollout. **V3.R01-followup ([#308](https://example.com/builders#/task/308), 2026-05-13) removed that redirect surface** after a repo audit confirmed no caller still hits the legacy path; `/api/pms/*` is now a 404. The read-side back-compat for the `pms_session` cookie, `PMS_GITHUB_*` env vars, and the `~/.config/otb/pms-session.json` file path **continues** (in `src/bongos/auth.js` and `scripts/gds/cli-lib.js`) — those are independent of the URL surface and protect existing local sessions.
|
|
9
9
|
|
|
@@ -66,5 +66,5 @@ The defense doesn't depend on the ADR's secrecy. Every protection mentioned in t
|
|
|
66
66
|
- [ADR 0016](0016-trust-boundary-server-enforced-permissions.md) — the server-enforced trust boundary the security model rests on.
|
|
67
67
|
- [ADR 0018](0018-three-rank-model-goes-live.md) — the three-rank model whose gates the security ADR documents.
|
|
68
68
|
- [ADR 0019](0019-red-team-bounty-payout-table.md) — the payout table a confirmed vulnerability_report maps onto.
|
|
69
|
-
- Migration [
|
|
69
|
+
- Migration [030_vulnerability_reports.sql](../../migrations/030_vulnerability_reports.sql) — the report intake table (R63).
|
|
70
70
|
- Migration [033_audit_log.sql](../../migrations/033_audit_log.sql) — the audit ledger every privileged write lands in (R35).
|
|
@@ -86,7 +86,7 @@ When a credential is rotated — either because we found a leak or because the c
|
|
|
86
86
|
4. **Revoke the old credential** at the issuing service. Do this AFTER step 2; the brief overlap is intentional so a slow-rolling service catches both keys.
|
|
87
87
|
5. **Log the rotation** — a one-line entry to [`docs/recipes/ops-gotchas.md`](../recipes/ops-gotchas.md) (date + which credential + why), so the next builder spinning up knows what changed when.
|
|
88
88
|
|
|
89
|
-
GDS session tokens rotate automatically — they're 30-day-TTL, refresh-on-use, revocable per session or per builder (migration
|
|
89
|
+
GDS session tokens rotate automatically — they're 30-day-TTL, refresh-on-use, revocable per session or per builder (migration `035_auth_token_hygiene.sql`, ADR-aligned with [ADR 0016](0016-trust-boundary-server-enforced-permissions.md)). No manual rotation needed for those except in a leak scenario, which falls under "What to do on a hit" below.
|
|
90
90
|
|
|
91
91
|
## What to do when the scan finds a hit
|
|
92
92
|
|
package/docs/file-map.md
CHANGED
|
@@ -366,6 +366,7 @@ tests/
|
|
|
366
366
|
├── session_506_regression.mjs ← (6D.5): the #506 regression gate — runs the engine over tests/fixtures/session-506-digest.json, asserts the hand-labelled findings (→#513/#514/#468) reproduce.
|
|
367
367
|
├── grade_attempts.mjs ← (6D.6): logGradeAttempt swallow-safety + param coercion via an injected queryFn (no DB).
|
|
368
368
|
├── linkify_refs.mjs ← (#604): the doc-linkifier classifier — pins which `#NNN` rewrite (tasks) vs stay bare (heading anchors, link targets, idea/blocker/session/PR ids, code spans), incl. comma-list entity inheritance + idempotency.
|
|
369
|
+
├── docs_scrubber_damage.mjs ← (task 1004027, part 4 of the scrubber-damage family after 1004021/1004022): docs/recipes/managed-settings-remote-control.md had TWO pieces of real prose replaced by the literal `<redacted>` — an in-page anchor target and an `infra/*.sh` script name — while FOUR other `<redacted>` in the same file are deliberate stand-ins for real secrets. Pins both halves: every in-page anchor resolves through docs-entropy's own parseFile (so the slug asserted is the slug the lint computes), the key-sync cron names the script `scripts/gds/box-infra.js` actually bakes into the cloud-init, and the placeholder count stays at EXACTLY four, matched by context rather than line number. The last assertion is the point — the anchor lint only reads link targets, so a global sweep of the literal would leave doc_broken_anchors at 0 while destroying four correct lines.
|
|
369
370
|
├── sandbox_stage.mjs ← (#927, ADR 0046): sandbox-first review gate — box detection, BOX_PREVIEW_HOSTNAME parsing, previewable-surface matching, decideShipGate table, tree-stamp freshness (temp git repo), runShipGate composition with injected fs/exec/prompt.
|
|
370
371
|
├── session_start_freshness.mjs ← (#1277, ADR 0067): the SessionStart freshness decision (decideFreshness) — pull ONLY on a clean `main` full clone strictly behind origin/main; warn in every other behind case; skip when up to date.
|
|
371
372
|
├── own_scope_guard.mjs ← (task 1003343, goal 1000045 criterion own-scope-held-by-a-test): the OWN-SCOPE structural guard, generalised out of memory_isolation.mjs. An explicit inventory of own-scoped route files; inside them the route's declared PATH decides — a path naming no builder must never read a subject from the request (direct, optional-chained, destructured, aliased, computed-key or header), a path that DOES name one needs a per-route exemption whose gate the guard verifies is present. Repo-wide backstop: any file mounting a `/me` route must be in the inventory. Source-grepping, so it runs in the DB-free `unit` lane.
|
|
@@ -2198,5 +2198,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2198
2198
|
landed since 1.19.860 with no explicit bump. run 35290990147. (task 1002620)
|
|
2199
2199
|
1.19.862 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2200
2200
|
landed since 1.19.861 with no explicit bump. run 35292190396. (task 1002620)
|
|
2201
|
+
1.19.863 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2202
|
+
landed since 1.19.862 with no explicit bump. run 35292877780. (task 1002620)
|
|
2203
|
+
1.19.864 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2204
|
+
landed since 1.19.863 with no explicit bump. run 35294233113. (task 1002620)
|
|
2201
2205
|
---------------------------------------------------------------------------
|
|
2202
2206
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.864",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.864",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.864",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.864'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// tests/docs_scrubber_damage.mjs — the secret-scrubber ate real prose in
|
|
2
|
+
// docs/recipes/managed-settings-remote-control.md (task 1004027, part 4 of the
|
|
3
|
+
// damage family after 1004021 / 1004022).
|
|
4
|
+
//
|
|
5
|
+
// Two distinct repairs, and the SECOND is the trap. A scrubber pass replaced
|
|
6
|
+
// two pieces of ordinary content with the literal `<redacted>`:
|
|
7
|
+
// - an in-page anchor target, so the link went nowhere;
|
|
8
|
+
// - an `infra/*.sh` script name, so the doc pointed at no script at all.
|
|
9
|
+
// But four OTHER `<redacted>` occurrences in the same file are DELIBERATE — they
|
|
10
|
+
// stand in for real secrets (a tunnel credential, two tokens, a trust seed).
|
|
11
|
+
// A global sweep "fixes" the two and destroys the four, and nothing in the repo
|
|
12
|
+
// would notice, because the anchor lint only ever looks at link targets.
|
|
13
|
+
//
|
|
14
|
+
// So this file pins BOTH halves: the anchor and the script path resolve, AND the
|
|
15
|
+
// placeholder count stays at exactly four. The second assertion is the one that
|
|
16
|
+
// makes a future sweep fail loudly instead of quietly.
|
|
17
|
+
import assert from 'node:assert/strict';
|
|
18
|
+
import { test } from 'node:test';
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
|
|
24
|
+
const require = createRequire(import.meta.url);
|
|
25
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
|
+
const { parseFile } = require(path.join(ROOT, 'scripts', 'gds', 'docs-entropy.js'));
|
|
27
|
+
|
|
28
|
+
const REL = 'docs/recipes/managed-settings-remote-control.md';
|
|
29
|
+
const ABS = path.join(ROOT, REL);
|
|
30
|
+
const raw = () => fs.readFileSync(ABS, 'utf8');
|
|
31
|
+
|
|
32
|
+
// The doc is only worth asserting against while it exists at this path.
|
|
33
|
+
test('the recipe under repair is still tracked at its documented path', () => {
|
|
34
|
+
assert.ok(fs.existsSync(ABS), `${REL} is missing — move this test with it`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('every in-page anchor in the recipe resolves to a heading it actually has', () => {
|
|
38
|
+
// The damaged link read `[named-tunnel automation](#<redacted>)`. Resolve
|
|
39
|
+
// through the SAME parser docs-entropy.js uses, so the slug this asserts is
|
|
40
|
+
// the slug the lint computes — a test with its own slug rule would let the
|
|
41
|
+
// doc pass here and still red the ratchet.
|
|
42
|
+
const { headings, links } = parseFile(ABS);
|
|
43
|
+
const slugs = new Set(headings.map((h) => h.slug));
|
|
44
|
+
const inPage = links.filter((l) => l.href.startsWith('#'));
|
|
45
|
+
|
|
46
|
+
assert.ok(inPage.length > 0, 'fixture sanity: the recipe does link to its own sections');
|
|
47
|
+
const broken = inPage.filter((l) => !slugs.has(l.href.slice(1)));
|
|
48
|
+
assert.deepEqual(
|
|
49
|
+
broken.map((l) => `${l.line}: ${l.href}`), [],
|
|
50
|
+
'an in-page anchor points at no heading in this file — this is what doc_broken_anchors counts',
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('the key-sync cron names a script the cloud-init renderer actually installs', () => {
|
|
55
|
+
// The damaged line read `infra/<redacted>.sh`. The authority for the real name
|
|
56
|
+
// is not prose anywhere: it is the renderer that bakes the script into the
|
|
57
|
+
// cloud-init, so assert the doc against THAT rather than against a literal.
|
|
58
|
+
const line = raw().split(/\r?\n/).find((l) => l.includes('GET /box/authorized-keys'))
|
|
59
|
+
?? raw().split(/\r?\n/).find((l) => l.includes('keeps `authorized_keys` in sync'));
|
|
60
|
+
assert.ok(line, 'fixture sanity: the recipe still describes the authorized_keys sync cron');
|
|
61
|
+
|
|
62
|
+
const named = [...raw().matchAll(/`infra\/([a-z0-9-]+\.sh)`/g)].map((m) => m[1]);
|
|
63
|
+
assert.ok(
|
|
64
|
+
named.includes('box-authorized-keys-fetch.sh'),
|
|
65
|
+
`the recipe names no authorized-keys script; found ${JSON.stringify(named)}`,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Scoped to the repaired name on purpose. Other `infra/*.sh` the recipe cites
|
|
69
|
+
// (box-connect.sh) are builder-side scripts absent from this checkout for an
|
|
70
|
+
// unrelated, pre-existing reason (idea 1000895) — asserting over all of them
|
|
71
|
+
// would red this test on a gap it is not about.
|
|
72
|
+
const renderer = fs.readFileSync(path.join(ROOT, 'scripts', 'gds', 'box-infra.js'), 'utf8');
|
|
73
|
+
assert.ok(
|
|
74
|
+
renderer.includes('box-authorized-keys-fetch.sh'),
|
|
75
|
+
'box-infra.js no longer bakes box-authorized-keys-fetch.sh into the cloud-init — '
|
|
76
|
+
+ 'the recipe now names a script nothing installs, so fix the doc to match the renderer',
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('the four deliberate <redacted> secret placeholders survive — a global sweep must fail here', () => {
|
|
81
|
+
// This is the anti-fix assertion. Each of these stands in for a real secret and
|
|
82
|
+
// is correct as written; "repairing" them is the damage, not the cure.
|
|
83
|
+
const lines = raw().split(/\r?\n/);
|
|
84
|
+
const placeholders = lines
|
|
85
|
+
.map((text, i) => ({ line: i + 1, text }))
|
|
86
|
+
.filter((l) => l.text.includes('<redacted>'));
|
|
87
|
+
|
|
88
|
+
assert.equal(
|
|
89
|
+
placeholders.length, 4,
|
|
90
|
+
'expected exactly the four secret placeholders, got:\n'
|
|
91
|
+
+ placeholders.map((p) => ` ${p.line}: ${p.text.trim().slice(0, 110)}`).join('\n'),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Named by CONTEXT, not by line number, so ordinary edits above them don't red this.
|
|
95
|
+
const contexts = ['"credential"', 'BOX_TUNNEL_TOKEN', 'BOX_SOURCE_FULL_TOKEN', '/root/.claude.json'];
|
|
96
|
+
for (const ctx of contexts) {
|
|
97
|
+
assert.ok(
|
|
98
|
+
placeholders.some((p) => p.text.includes(ctx)),
|
|
99
|
+
`the deliberate placeholder for ${ctx} is gone — a <redacted> sweep took a real secret stand-in with it`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// And no placeholder may sit in a link target or a path again: those two shapes
|
|
104
|
+
// are exactly what the scrubber damaged.
|
|
105
|
+
for (const p of placeholders) {
|
|
106
|
+
assert.ok(
|
|
107
|
+
!/\(#<redacted>\)/.test(p.text) && !/infra\/<redacted>/.test(p.text),
|
|
108
|
+
`line ${p.line} carries scrubber damage, not a secret placeholder: ${p.text.trim().slice(0, 110)}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
});
|