@bongos/core 1.19.725 → 1.19.727
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 +179 -494
- package/.devcontainer/Dockerfile +1 -1
- package/.devcontainer/devcontainer.json +2 -2
- package/.devcontainer/healthcheck.sh +1 -1
- package/.devcontainer/init-firewall.sh +1 -1
- package/.devcontainer/post-create.sh +2 -2
- package/.env.local.example +1 -1
- package/Dockerfile +1 -1
- package/docs/copy-inventory.md +1 -1
- package/docs/copy-registry.json +9 -9
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/devbox-app-release.md +4 -4
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +5 -5
- package/modules/dev-box/app/scripts/gen-brand.js +1 -1
- package/modules/dev-box/app/src/brand.js +1 -1
- package/modules/dev-box/app/test/unit.mjs +16 -16
- package/modules/dev-box/box-access.js +1 -1
- package/modules/dev-box/routes/box.js +1 -1
- package/modules/discord/discord-approvals.js +1 -1
- package/modules/grading/grader-model.js +1 -1
- package/modules/grading/grader-prompt.js +1 -1
- package/modules/grading/grader-workers/quality.js +1 -1
- package/modules/hall-ui/module.json +1 -1
- package/modules/hall-ui/public/brand-apply.js +1 -1
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/lifecycle/conflict-resolve.js +1 -1
- package/modules/lifecycle/db-analytics.js +1 -1
- package/modules/lifecycle/routes/analytics.js +1 -1
- package/modules/onboarding/approval-broadcast.js +1 -1
- package/modules/onboarding/onboarding.js +1 -1
- package/modules/provisioning/tests/liveness.mjs +1 -1
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects-floor.states.json +2 -2
- package/modules/public-landing/public/projects.probes.json +4 -4
- package/modules/public-landing/public/projects.states.json +2 -2
- package/modules/status-ui/module.json +1 -1
- package/modules/status-ui/public/status.js +4 -4
- package/modules/status-ui/public/style.css +1 -1
- package/modules/ui-design/kit/fixtures/cost-summary.json +1 -1
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
- package/modules/ui-design/kit/fixtures/featured.json +1 -1
- package/modules/ui-design/kit/fixtures/leaderboard.json +1 -1
- package/modules/ui-design/kit/fixtures/my-projects.json +1 -1
- package/modules/ui-design/kit/fixtures/progress.json +1 -1
- package/modules/ui-design/kit/fixtures/recent-shipped.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/cron/style_guide_audit.sh +1 -1
- package/scripts/discord/lib.js +1 -1
- package/scripts/discord/post.js +1 -1
- package/scripts/gds/audit-authorship.sh +1 -1
- package/scripts/gds/audit-replay.sh +29 -2
- package/scripts/gds/audit-ships.js +1 -1
- package/scripts/gds/backfill-sessions.js +2 -2
- package/scripts/gds/cf-tunnel.js +4 -4
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +6 -6
- package/scripts/gds/cutover-codemod.js +1 -1
- package/scripts/gds/dev-lib.js +2 -2
- package/scripts/gds/diagnose-achievements.js +2 -2
- package/scripts/gds/fitness-checks-identity.js +6 -4
- package/scripts/gds/gate-review.js +14 -6
- package/scripts/gds/gen-diagrams.js +1 -1
- package/scripts/gds/leak-scan-allowlist.js +293 -0
- package/scripts/gds/learning-capture.js +1 -1
- package/scripts/gds/linkify-refs.js +2 -2
- package/scripts/gds/local-preview-lib.js +1 -1
- package/scripts/gds/mirror-lineage-check.js +1 -1
- package/scripts/gds/mirror-redact.js +17 -2
- package/scripts/gds/newcomer-floor.templates.json +1 -1
- package/scripts/gds/package-core.js +18 -1
- package/scripts/gds/paste-token.js +1 -1
- package/scripts/gds/provision-units.js +2 -2
- package/scripts/gds/publish-manifest.js +15 -0
- package/scripts/gds/repo-metrics.js +1 -1
- package/scripts/gds/sandbox-stage.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +1 -1
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +1 -1
- package/scripts/gds/seed-bongos-tasks.js +2 -2
- package/scripts/gds/seed-gds-v4-tasks.js +1 -1
- package/scripts/gds/seed-medusa-tasks.js +10 -10
- package/scripts/gds/seed-module-upstreaming.js +1 -1
- package/scripts/gds/seed-productization-tasks.js +6 -6
- package/scripts/gds/seed-provisioning-tasks.js +2 -2
- package/scripts/gds/seed-staging.sh +19 -6
- package/scripts/gds/ship-deploy-target.js +1 -1
- package/scripts/gds/ship-flow.js +1 -1
- package/scripts/gds/smoke-gds.js +1 -1
- package/scripts/status-probe.mjs +2 -2
- package/src/bongos/auth-discord.js +2 -2
- package/src/bongos/live-channel.js +1 -1
- package/src/bongos/permission-path-check.js +80 -3
- package/src/bongos/pool.js +1 -1
- package/src/bongos/routes/auth.js +11 -11
- package/src/bongos/routes/security.js +1 -1
- package/src/bongos/routes.js +1 -1
- package/src/branding.js +1 -1
- package/src/devbox-downloads.js +1 -1
- package/src/instance-config.js +2 -2
- package/src/module-api.js +1 -1
- package/src/module-loader/provenance.js +2 -2
- package/tests/art_stage.mjs +2 -2
- package/tests/auth_anthropic_identity.mjs +1 -1
- package/tests/auth_duplicate_cookie.mjs +1 -1
- package/tests/auth_error_brand.mjs +1 -1
- package/tests/auth_inactive_builder.mjs +1 -1
- package/tests/backup_cli.mjs +4 -4
- package/tests/bongos_box_connect.mjs +2 -2
- package/tests/bongos_downloads.mjs +2 -2
- package/tests/bongos_login.mjs +5 -5
- package/tests/box_connect_e2e.mjs +2 -2
- package/tests/box_dns_repoint.mjs +10 -10
- package/tests/box_host_keys.mjs +1 -1
- package/tests/box_onboard.mjs +9 -9
- package/tests/box_scope_session.mjs +1 -1
- package/tests/box_terminal_ssrf.mjs +7 -7
- package/tests/boxes.mjs +9 -9
- package/tests/branding.mjs +2 -2
- package/tests/bump_version.mjs +2 -2
- package/tests/cf_tunnel.mjs +40 -40
- package/tests/cli_token_reissue.mjs +7 -7
- package/tests/cloud_bongos_pack.mjs +1 -1
- package/tests/currency_label.mjs +8 -8
- package/tests/dev_lib.mjs +4 -4
- package/tests/devbox_dmg_backdrop.mjs +1 -1
- package/tests/devbox_downloads.mjs +2 -2
- package/tests/devbox_icons.mjs +1 -1
- package/tests/discord_approvals.mjs +1 -1
- package/tests/discord_oauth.mjs +2 -2
- package/tests/executed_closure.mjs +6 -1
- package/tests/first_admin_bootstrap.mjs +1 -1
- package/tests/gate_approvals.mjs +5 -5
- package/tests/government_protected_surfaces.mjs +137 -0
- package/tests/grade_aggregates_unavailable.mjs +1 -1
- package/tests/hook_user_agents.mjs +2 -2
- package/tests/init.mjs +6 -6
- package/tests/instance_config.mjs +1 -1
- package/tests/instance_manifest.mjs +5 -5
- package/tests/instance_mismatch_guard.mjs +13 -13
- package/tests/leak_scan_allowlist.mjs +94 -0
- package/tests/migrate_root_resolution.mjs +4 -4
- package/tests/mirror_redact.mjs +28 -4
- package/tests/module_catalog.mjs +5 -5
- package/tests/module_submissions.mjs +3 -3
- package/tests/package_core.mjs +1 -1
- package/tests/parallel-load.mjs +6 -6
- package/tests/projects_hub.mjs +2 -2
- package/tests/provision.mjs +50 -50
- package/tests/provisioning_github_app_routes.mjs +1 -1
- package/tests/provisioning_onboard_mode.mjs +2 -2
- package/tests/publish_reconciler.mjs +4 -4
- package/tests/sandbox_stage.mjs +1 -1
- package/tests/scrubber_corpus.mjs +3 -3
- package/tests/search_isolation.mjs +1 -1
- package/tests/session_records.mjs +1 -1
- package/tests/ship_card.mjs +6 -6
- package/tests/ship_ci_deploy.mjs +1 -1
- package/tests/single_host_hall.mjs +9 -9
- package/tests/smoke_preflight.mjs +1 -1
- package/tests/smoke_target.mjs +1 -1
- package/tests/status-probe.mjs +1 -1
- package/tests/status_mirror_sync.mjs +1 -1
- package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +0 -45
- package/docs/design/mocks/_schemes.css +0 -604
- package/docs/design/mocks/component-sheet.html +0 -428
- package/docs/design/mocks/hall/_tokens.css +0 -370
- package/docs/design/mocks/hall/board/v1.html +0 -846
- package/docs/design/mocks/hall/board/v2.html +0 -858
- package/docs/design/mocks/hall/board/v3.html +0 -884
- package/docs/design/mocks/hall/goals/v1.html +0 -1212
- package/docs/design/mocks/hall/goals/v2.html +0 -1192
- package/docs/design/mocks/hall/goals/v3.html +0 -1205
- package/docs/design/mocks/hall/hall-sheet.html +0 -244
- package/docs/design/mocks/hall/reading/v1.html +0 -1125
- package/docs/design/mocks/hall/reading/v2.html +0 -1177
- package/docs/design/mocks/hall/reading/v3.html +0 -1150
- package/docs/design/mocks/hall/settings/v1.html +0 -1238
- package/docs/design/mocks/hall/settings/v2.html +0 -1235
- package/docs/design/mocks/hall/settings/v3.html +0 -1299
- package/docs/design/mocks/hall/task/v1.html +0 -633
- package/docs/design/mocks/hall/task/v2.html +0 -646
- package/docs/design/mocks/hall/task/v3.html +0 -647
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/hub-sheet.html +0 -192
- package/docs/design/mocks/hub/mine.html +0 -465
- package/docs/design/mocks/hub/picker.html +0 -421
- package/docs/design/mocks/hub/planets.html +0 -356
- package/docs/design/mocks/hub/v1.html +0 -1209
- package/docs/design/mocks/hub/v2.html +0 -1257
- package/docs/design/mocks/hub/v3.html +0 -1496
- package/docs/design/mocks/hub/wizard.html +0 -421
- package/docs/design/mocks/landing/landing-sheet.html +0 -131
- package/docs/design/mocks/landing/v1.html +0 -956
- package/docs/design/mocks/landing/v2.html +0 -708
- package/docs/design/mocks/landing/v3.html +0 -911
- package/docs/design/mocks/landing-page-mock.html +0 -687
- package/docs/design/mocks/landing-palettes.html +0 -103
- package/docs/design/mocks/projects-hub-mock.html +0 -991
- package/docs/design/mocks/projects-hub-variants.html +0 -146
- package/docs/design/mocks/projects-page-mock.html +0 -167
- package/docs/design/mocks/world/blue-hud/_tokens.css +0 -246
- package/docs/design/mocks/world/blue-hud/hall.html +0 -595
- package/docs/design/mocks/world/blue-hud/landing.html +0 -424
- package/docs/design/mocks/world/blue-hud/status.html +0 -410
- package/docs/design/mocks/world/chrome-blobs/_tokens.css +0 -479
- package/docs/design/mocks/world/chrome-blobs/hall.html +0 -553
- package/docs/design/mocks/world/chrome-blobs/landing-b.html +0 -581
- package/docs/design/mocks/world/chrome-blobs/landing.html +0 -328
- package/docs/design/mocks/world/chrome-blobs/status.html +0 -416
- package/docs/design/mocks/world/natgeo-editorial/_tokens.css +0 -377
- package/docs/design/mocks/world/natgeo-editorial/hall.html +0 -649
- package/docs/design/mocks/world/natgeo-editorial/landing-b.html +0 -743
- package/docs/design/mocks/world/natgeo-editorial/landing.html +0 -475
- package/docs/design/mocks/world/natgeo-editorial/status.html +0 -437
- package/docs/design/mocks/world/organic-cards/_tokens.css +0 -462
- package/docs/design/mocks/world/organic-cards/hall.html +0 -625
- package/docs/design/mocks/world/organic-cards/landing-b.html +0 -736
- package/docs/design/mocks/world/organic-cards/landing.html +0 -420
- package/docs/design/mocks/world/organic-cards/status.html +0 -458
- package/docs/design/mocks/world/world-sheet.html +0 -156
|
@@ -518,3 +518,140 @@ test('the floor decision reads the hit\'s registry floor, so a re-map takes effe
|
|
|
518
518
|
const atFloor = ppc.checkPermissionPaths({ changedFiles: ['migrations/001.sql'], builderRank: 'metic' });
|
|
519
519
|
assert.equal(atFloor.hardFail, false);
|
|
520
520
|
});
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
// task 1002874 — NFC + case-folding, the last piece of R101's ADR 0151 §4 scope.
|
|
525
|
+
//
|
|
526
|
+
// R101 shipped the registry with matching left exact-BYTES, deliberately and
|
|
527
|
+
// declared: its hard constraint was byte-identical behaviour, and normalisation
|
|
528
|
+
// changes what matches. This IS that change, so these come in two halves — the
|
|
529
|
+
// wall must now hold against a re-spelling, and nothing protected before may have
|
|
530
|
+
// stopped being protected.
|
|
531
|
+
// ---------------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
// A real literal surface from the registry, not a fixture: this cannot pass
|
|
534
|
+
// against a glob that no longer exists.
|
|
535
|
+
const LITERAL = ppc.PROTECTED_SURFACES.find((s) => !s.glob.includes('*') && !s.glob.endsWith('/'));
|
|
536
|
+
|
|
537
|
+
test('a CASE variant of a protected path is still protected', () => {
|
|
538
|
+
assert.ok(LITERAL, 'no literal surface in the registry — this test is reading the wrong shape');
|
|
539
|
+
assert.ok(ppc.surfaceFor(LITERAL.glob), 'the exact spelling must match (sanity)');
|
|
540
|
+
// macOS by default and Windows are case-INsensitive: this names the SAME file.
|
|
541
|
+
assert.ok(ppc.surfaceFor(LITERAL.glob.toUpperCase()),
|
|
542
|
+
'an uppercased spelling of a protected file must not step over the wall');
|
|
543
|
+
const mixed = LITERAL.glob.replace(/(^|\/)([a-z])/g, (m, s, c) => s + c.toUpperCase());
|
|
544
|
+
assert.ok(ppc.surfaceFor(mixed), `a mixed-case spelling must not step over the wall: ${mixed}`);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test('the REPORTED path keeps its original case — only matching is folded', () => {
|
|
548
|
+
const shouty = LITERAL.glob.toUpperCase();
|
|
549
|
+
const [hit] = ppc.matchProtected([shouty]);
|
|
550
|
+
assert.ok(hit, 'the case variant must be caught');
|
|
551
|
+
assert.equal(hit.file, shouty,
|
|
552
|
+
'findings, refusals and grader issues all carry this path; a lowercased one names '
|
|
553
|
+
+ 'nothing on a case-SENSITIVE filesystem (Linux, i.e. CI) and would ENOENT for a file that exists');
|
|
554
|
+
assert.equal(hit.glob, LITERAL.glob, 'and the registry glob is reported verbatim, not folded');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test('an NFD-composed path matches its NFC glob', () => {
|
|
558
|
+
// The registry is pure ASCII today, so this exercises the mechanism directly:
|
|
559
|
+
// one filename, two different byte strings, either of which a checkout can hand you.
|
|
560
|
+
const nfc = 'modules/caf\u00e9/secret.js';
|
|
561
|
+
const nfd = 'modules/cafe\u0301/secret.js';
|
|
562
|
+
assert.notEqual(nfc, nfd, 'the two spellings must really differ in bytes, or this proves nothing');
|
|
563
|
+
const fold = (s) => s.normalize('NFC').toLowerCase();
|
|
564
|
+
const re = ppc.globToRegExp(fold(nfc));
|
|
565
|
+
assert.ok(re.test(fold(nfd)), 'NFD and NFC spellings of one filename must match the same glob');
|
|
566
|
+
assert.ok(re.test(fold(nfc.toUpperCase())), 'and case must not separate them either');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
test('STRICTLY WIDENING: every glob still matches a path it matched before', () => {
|
|
570
|
+
// The regression that would matter is a path silently becoming UNprotected, so
|
|
571
|
+
// walk the whole registry and synthesise a concrete path from each glob.
|
|
572
|
+
let checked = 0;
|
|
573
|
+
for (const s of ppc.PROTECTED_SURFACES) {
|
|
574
|
+
const sample = s.glob.endsWith('/')
|
|
575
|
+
? `${s.glob}probe.js`
|
|
576
|
+
: s.glob.replace(/\*\*/g, 'x/y').replace(/\*/g, 'z');
|
|
577
|
+
assert.ok(ppc.surfaceFor(sample), `${s.glob} no longer protects ${sample} — this change must never narrow`);
|
|
578
|
+
checked += 1;
|
|
579
|
+
}
|
|
580
|
+
assert.ok(checked >= 40, `only ${checked} surfaces walked — the registry read is not working`);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test('an UNprotected path is still unprotected — folding must not over-reach', () => {
|
|
584
|
+
for (const p of ['README.md', 'docs/notes.md', 'tests/example.mjs']) {
|
|
585
|
+
assert.equal(ppc.surfaceFor(p), null, `${p} must not become protected by normalisation`);
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
test('the matcher never uses a LOCALE-sensitive case fold (the Turkish-I hazard)', () => {
|
|
590
|
+
const src = fs.readFileSync(path.join(ROOT, 'src', 'bongos', 'permission-path-check.js'), 'utf8')
|
|
591
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
592
|
+
.replace(/\/\/[^\n]*/g, '');
|
|
593
|
+
assert.ok(src.length > 1000, `comment-stripping left only ${src.length} chars — the scan is reading nothing`);
|
|
594
|
+
assert.ok(!/toLocaleLowerCase|toLocaleUpperCase/.test(src),
|
|
595
|
+
'toLocaleLowerCase("tr") maps I to dotless i — measured — so a Turkish locale would fold '
|
|
596
|
+
+ 'two different paths together. Plain toLowerCase is locale-independent by specification '
|
|
597
|
+
+ 'and is the correct primitive here; the ADR note warning against it does not apply to JS.');
|
|
598
|
+
assert.match(src, /toLowerCase\(\)/, 'and the fold must actually be applied');
|
|
599
|
+
assert.match(src, /normalize\('NFC'\)/, 'as must NFC composition');
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
// ---------------------------------------------------------------------------
|
|
604
|
+
// task 1002874, round 2 — the fold has to reach every PROTECTED matcher, not just
|
|
605
|
+
// the one in permission-path-check.
|
|
606
|
+
//
|
|
607
|
+
// Round 1 fixed matchProtected/surfaceFor and trusted the task description's claim
|
|
608
|
+
// that all eleven substrate consumers route through them. They do not.
|
|
609
|
+
// gate-review.js — the CI ESCALATE / HARD_FLOOR classifier — imported the RAW
|
|
610
|
+
// PROTECTED_GLOBS plus globToRegExp and compiled its own byte-exact regexes, so an
|
|
611
|
+
// uppercased spelling of a gate surface still walked past the auto-merge gate after
|
|
612
|
+
// the wall itself was fixed. Its isCanonicalPath() rejects non-NFC and non-ASCII
|
|
613
|
+
// but does not case-fold, so it never covered this.
|
|
614
|
+
// ---------------------------------------------------------------------------
|
|
615
|
+
|
|
616
|
+
test('compileGlobMatcher folds BOTH sides', () => {
|
|
617
|
+
const m = ppc.compileGlobMatcher(['src/bongos/auth.js', 'migrations/']);
|
|
618
|
+
assert.ok(m('src/bongos/auth.js'), 'exact still matches');
|
|
619
|
+
assert.ok(m('SRC/BONGOS/AUTH.JS'), 'case variant matches');
|
|
620
|
+
assert.ok(m('Migrations/001.sql'), 'a folded subtree prefix still matches');
|
|
621
|
+
assert.ok(!m('docs/readme.md'), 'and an unrelated path does not');
|
|
622
|
+
assert.ok(!m(''), 'empty is never a match');
|
|
623
|
+
assert.ok(!m(null), 'nor is a missing path');
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
test('gate-review classifies a CASE-VARIANT gate surface as hard-floor', () => {
|
|
627
|
+
const gr = require(path.join(ROOT, 'scripts', 'gds', 'gate-review.js'));
|
|
628
|
+
const literal = gr.HARD_FLOOR_GLOBS.find((g) => !g.includes('*') && !g.endsWith('/'));
|
|
629
|
+
assert.ok(literal, 'no literal hard-floor glob — this test is reading the wrong shape');
|
|
630
|
+
assert.ok(gr.matchesHardFloor(literal), 'the exact spelling must escalate (sanity)');
|
|
631
|
+
assert.ok(gr.matchesHardFloor(literal.toUpperCase()),
|
|
632
|
+
'an uppercased gate surface must still escalate — otherwise CI auto-merge is bypassable '
|
|
633
|
+
+ 'by re-spelling, which is the hole this task exists to close');
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
test('no consumer compiles a PROTECTED glob list byte-exact', () => {
|
|
637
|
+
// The drift guard. Both globToRegExp and the raw glob lists are exported, so a
|
|
638
|
+
// consumer can rebuild the byte-exact matcher by accident — which is exactly what
|
|
639
|
+
// gate-review had done. globToRegExp stays exported because ALLOW-lists
|
|
640
|
+
// legitimately need it: folding publish-manifest's PUBLISH_ALLOWLIST would widen
|
|
641
|
+
// what reaches the public OSS mirror, i.e. publish MORE. So the ban is narrow —
|
|
642
|
+
// pairing a PROTECTED list with the raw compiler in one file.
|
|
643
|
+
const dir = path.join(ROOT, 'scripts', 'gds');
|
|
644
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.js'));
|
|
645
|
+
assert.ok(files.length > 20, `only ${files.length} scripts scanned — the sweep is not reading the CLI directory`);
|
|
646
|
+
const offenders = [];
|
|
647
|
+
for (const f of files) {
|
|
648
|
+
const src = fs.readFileSync(path.join(dir, f), 'utf8')
|
|
649
|
+
.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '');
|
|
650
|
+
const usesRawCompiler = /globToRegExp\s*\(/.test(src);
|
|
651
|
+
const usesProtectedList = /(PROTECTED_GLOBS|HARD_FLOOR_GLOBS)\s*\.\s*map/.test(src);
|
|
652
|
+
if (usesRawCompiler && usesProtectedList) offenders.push(f);
|
|
653
|
+
}
|
|
654
|
+
assert.deepEqual(offenders, [],
|
|
655
|
+
`these compile a protected-surface list with the raw byte-exact compiler; use `
|
|
656
|
+
+ `compileGlobMatcher so the fold reaches them too: ${offenders.join(', ')}`);
|
|
657
|
+
});
|
|
@@ -76,7 +76,7 @@ await test('gradeByBuilder: quality-filtered, and pass_rate is NULL (not 0.000)
|
|
|
76
76
|
const sql = (await captureSql(() => db.gradeByBuilder(30)))[0];
|
|
77
77
|
assertQualityFiltered(sql, 'gradeByBuilder');
|
|
78
78
|
// The old shape ROUND(COALESCE(AVG(...),0),3) fabricated a 0.000 fail-streak
|
|
79
|
-
// for an all-outage window — the
|
|
79
|
+
// for an all-outage window — the example-owner symptom. COALESCE must be gone from
|
|
80
80
|
// the pass_rate expression so it reads NULL ("no graded work") instead.
|
|
81
81
|
assert.ok(!sql.includes('ROUND(COALESCE(AVG'), 'pass_rate must not coalesce an empty window to 0');
|
|
82
82
|
assert.ok(/AVG\(CASE WHEN g\.passed THEN 1 ELSE 0 END\) FILTER/.test(sql), 'pass_rate must average quality rows only');
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// task 2122 (demo-audit fix B) — the scaffolded .claude/hooks must NOT hardcode a
|
|
4
4
|
// founder-instance brand in their outbound HTTP User-Agent. Those hooks are copied
|
|
5
5
|
// into a consumer instance by claude-materialize, whose rebrand maps domain HOSTS
|
|
6
|
-
// (
|
|
7
|
-
// like `
|
|
6
|
+
// (example.com), config dirs, and env prefixes — but NOT a bare brand token
|
|
7
|
+
// like `example-gds-cli` (no TLD). So a branded UA there leaks the founder's
|
|
8
8
|
// host-brand into every scaffolded instance (criterion gcarry-prod-vanilla).
|
|
9
9
|
//
|
|
10
10
|
// Invariant: every `'User-Agent': '<value>'` literal in .claude/hooks/*.js uses the
|
package/tests/init.mjs
CHANGED
|
@@ -27,8 +27,8 @@ function test(name, fn) {
|
|
|
27
27
|
const fullSpec = () => init.normalizeSpec({
|
|
28
28
|
identity: { productName: 'Cloud Bongos' },
|
|
29
29
|
tagline: 'Building software is laying down a beat.',
|
|
30
|
-
repo: { owner: '
|
|
31
|
-
firstAdmin: '
|
|
30
|
+
repo: { owner: 'example-owner', name: 'cloud-bongos' },
|
|
31
|
+
firstAdmin: 'example-owner',
|
|
32
32
|
currency: { label: 'credits' },
|
|
33
33
|
domains: { publicOrigin: 'http://localhost:3000' },
|
|
34
34
|
modules: ['art-pipeline'],
|
|
@@ -48,8 +48,8 @@ test('buildBrandingConfig: lean overrides only (no theme block)', () => {
|
|
|
48
48
|
const b = init.buildBrandingConfig(fullSpec());
|
|
49
49
|
assert.equal(b.identity.productName, 'Cloud Bongos');
|
|
50
50
|
assert.equal(b.identity.worldName, 'Cloud Bongos'); // defaults to productName
|
|
51
|
-
assert.equal(b.repo.owner, '
|
|
52
|
-
assert.equal(b.firstAdmin, '
|
|
51
|
+
assert.equal(b.repo.owner, 'example-owner');
|
|
52
|
+
assert.equal(b.firstAdmin, 'example-owner');
|
|
53
53
|
assert.equal(b.currency.label, 'credits');
|
|
54
54
|
assert.equal(b.envPrefix, 'BONGOS'); // canonical since task 1003703
|
|
55
55
|
assert.equal(b.copy.tagline, 'Building software is laying down a beat.');
|
|
@@ -219,8 +219,8 @@ test('versionPayload: always sends an explicit, valid track', () => {
|
|
|
219
219
|
// ---- provisioning arm (ADR 0111 §3, task 1946) ----
|
|
220
220
|
const provSpec = (over = {}) => init.normalizeSpec({
|
|
221
221
|
identity: { productName: 'Cloud Bongos' },
|
|
222
|
-
repo: { owner: '
|
|
223
|
-
firstAdmin: '
|
|
222
|
+
repo: { owner: 'example-owner', name: 'cloud-bongos' },
|
|
223
|
+
firstAdmin: 'example-owner',
|
|
224
224
|
version: { id: 'V1', name: 'V1', done_when: 'x' },
|
|
225
225
|
provisioning: over,
|
|
226
226
|
});
|
|
@@ -177,7 +177,7 @@ test('resolveDocsRoot: the explicit instance root when set, else coreRoot (no-op
|
|
|
177
177
|
// Regression fixture for a bug that locked a builder out of re-authenticating.
|
|
178
178
|
// Three call sites resolved the API base three different ways and none reached
|
|
179
179
|
// the instance the builder was actually signed into:
|
|
180
|
-
// * paste-token.js hardcoded 'https://
|
|
180
|
+
// * paste-token.js hardcoded 'https://example.com' (a straggler the task
|
|
181
181
|
// 1002201 sweep missed), so a token minted by cloudbongos.com was verified
|
|
182
182
|
// against the founder's host and rejected 401 — a VALID token, wrong host.
|
|
183
183
|
// * reauth.js used the module-level branding default, so it printed a
|
|
@@ -14,10 +14,10 @@ const RANK_TIER = { xenos: 1, thetes: 2, metic: 3, archon: 4 };
|
|
|
14
14
|
|
|
15
15
|
// A client-safe branding projection (the shape clientBranding() returns).
|
|
16
16
|
const BRAND_OTB = {
|
|
17
|
-
identity: { productName: '
|
|
17
|
+
identity: { productName: 'Example World', worldName: 'Example', company: { name: 'Hermeslines' } },
|
|
18
18
|
currency: { label: 'drachmae', symbol: '' },
|
|
19
19
|
theme: { colors: {}, fonts: { display: '', body: '', links: [] }, favicon: '', backdrop: '' },
|
|
20
|
-
copy: { tagline: 'Straight
|
|
20
|
+
copy: { tagline: 'Straight example world', hallName: 'Builders’ Hall', notFound: 'Page not found.' },
|
|
21
21
|
rankLabels: { xenos: 'Xenos', thetes: 'Thetes', metic: 'Metic', archon: 'Archon' },
|
|
22
22
|
// the per-project policy the platform pushes (task 1003140) — the settings read-back
|
|
23
23
|
project: { platformVisibility: 'gated', joinability: 'invite_only' },
|
|
@@ -37,12 +37,12 @@ const MODULES_VANILLA = {
|
|
|
37
37
|
enabled: { game: false, 'art-pipeline': false, discord: false, 'dev-box': false },
|
|
38
38
|
disciplines: ['engineer'],
|
|
39
39
|
};
|
|
40
|
-
const REPO = { owner: '
|
|
40
|
+
const REPO = { owner: 'example-owner', repo: 'example', branch: 'main', blob_base: 'x', tree_base: 'y' };
|
|
41
41
|
|
|
42
42
|
test('manifest: schema id + headline name + embedded brand', () => {
|
|
43
43
|
const m = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
|
|
44
44
|
assert.equal(m.schema, MANIFEST_SCHEMA);
|
|
45
|
-
assert.equal(m.name, '
|
|
45
|
+
assert.equal(m.name, 'Example World');
|
|
46
46
|
assert.equal(m.brand, BRAND_OTB);
|
|
47
47
|
assert.deepEqual(m.modules, MODULES_OTB);
|
|
48
48
|
});
|
|
@@ -69,7 +69,7 @@ test('manifest: rank label falls back to the key when unlabelled', () => {
|
|
|
69
69
|
|
|
70
70
|
test('manifest: repo projects to public clone coords; null on error', () => {
|
|
71
71
|
const ok = buildInstanceManifest({ branding: BRAND_OTB, modules: MODULES_OTB, rankTier: RANK_TIER, repo: REPO, authConfigured: true, openEnrollment: false, devBoxEnabled: true });
|
|
72
|
-
assert.deepEqual(ok.repo, { owner: '
|
|
72
|
+
assert.deepEqual(ok.repo, { owner: 'example-owner', repo: 'example', branch: 'main', url: 'https://github.com/example-owner/example' });
|
|
73
73
|
const none = buildInstanceManifest({ branding: BRAND_VANILLA, modules: MODULES_VANILLA, rankTier: RANK_TIER, repo: { error: 'no_repo_info' }, authConfigured: false, openEnrollment: false, devBoxEnabled: false });
|
|
74
74
|
assert.equal(none.repo, null);
|
|
75
75
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// The guard stops the CLI from silently borrowing a DIFFERENT instance's session
|
|
9
9
|
// (the 2026-07-08 demo-session failure, where a fresh demo clone used the
|
|
10
|
-
// founder's
|
|
10
|
+
// founder's example.com session and offered to write into the wrong live
|
|
11
11
|
// backlog). Both helpers are deterministic with no DB/network/fs, so they
|
|
12
12
|
// unit-test standalone. Run: node tests/instance_mismatch_guard.mjs
|
|
13
13
|
|
|
@@ -25,14 +25,14 @@ function test(name, fn) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const DEMO = { domains: { publicOrigin: 'https://demo.cloudbongos.com' } };
|
|
28
|
-
const OTB = { domains: { publicOrigin: 'https://
|
|
28
|
+
const OTB = { domains: { publicOrigin: 'https://example.com' } };
|
|
29
29
|
const noEnv = {}; // no <PREFIX>_API_BASE override in scope
|
|
30
30
|
const branded = () => true; // simulate a checkout that HAS config/branding.json (a declared instance)
|
|
31
31
|
const neutral = () => false; // simulate a NEUTRAL checkout (portable core — no config/branding.json)
|
|
32
32
|
|
|
33
33
|
test('instanceOriginHost: extracts host, tolerates trailing slash', () => {
|
|
34
|
-
assert.equal(cli.instanceOriginHost('https://
|
|
35
|
-
assert.equal(cli.instanceOriginHost('https://
|
|
34
|
+
assert.equal(cli.instanceOriginHost('https://example.com'), 'example.com');
|
|
35
|
+
assert.equal(cli.instanceOriginHost('https://example.com/'), 'example.com');
|
|
36
36
|
assert.equal(cli.instanceOriginHost('https://demo.cloudbongos.com/api/gds'), 'demo.cloudbongos.com');
|
|
37
37
|
});
|
|
38
38
|
|
|
@@ -43,20 +43,20 @@ test('instanceOriginHost: unparseable input → null (edge)', () => {
|
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
test('match: same-host session + branding → no mismatch (the OTB/normal case)', () => {
|
|
46
|
-
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
46
|
+
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://example.com' }, brand: OTB, env: noEnv, hasInstanceConfig: branded });
|
|
47
47
|
assert.equal(r.mismatch, false);
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
test('match: scheme/trailing-slash differences still match on host', () => {
|
|
51
|
-
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
51
|
+
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://example.com/' }, brand: OTB, env: noEnv, hasInstanceConfig: branded });
|
|
52
52
|
assert.equal(r.mismatch, false);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
test('mismatch: demo repo + founder
|
|
56
|
-
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
55
|
+
test('mismatch: demo repo + founder example session → REFUSE (the reported failure)', () => {
|
|
56
|
+
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://example.com' }, brand: DEMO, env: noEnv, hasInstanceConfig: branded });
|
|
57
57
|
assert.equal(r.mismatch, true);
|
|
58
58
|
assert.equal(r.expected, 'https://demo.cloudbongos.com');
|
|
59
|
-
assert.equal(r.actual, 'https://
|
|
59
|
+
assert.equal(r.actual, 'https://example.com');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
test('mismatch: demo repo + cloudbongos.com session (sibling instance) → REFUSE', () => {
|
|
@@ -66,15 +66,15 @@ test('mismatch: demo repo + cloudbongos.com session (sibling instance) → REFUS
|
|
|
66
66
|
|
|
67
67
|
test('env-override: an explicit *_API_BASE override wins (dev/staging) → no mismatch', () => {
|
|
68
68
|
const r = cli.detectInstanceMismatch({
|
|
69
|
-
session: { api_base: 'https://
|
|
69
|
+
session: { api_base: 'https://example.com' }, brand: DEMO,
|
|
70
70
|
env: { GDS_API_BASE: 'http://localhost:3000' },
|
|
71
71
|
});
|
|
72
72
|
assert.equal(r.mismatch, false);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test('no-branding: repo declares no publicOrigin → cannot assert a mismatch → no mismatch', () => {
|
|
76
|
-
assert.equal(cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
77
|
-
assert.equal(cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
76
|
+
assert.equal(cli.detectInstanceMismatch({ session: { api_base: 'https://example.com' }, brand: {}, env: noEnv, hasInstanceConfig: branded }).mismatch, false);
|
|
77
|
+
assert.equal(cli.detectInstanceMismatch({ session: { api_base: 'https://example.com' }, brand: { domains: {} }, env: noEnv, hasInstanceConfig: branded }).mismatch, false);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test('NEUTRAL checkout (no config/branding.json) → trust the session, NO mismatch (task 1002399)', () => {
|
|
@@ -88,7 +88,7 @@ test('NEUTRAL checkout (no config/branding.json) → trust the session, NO misma
|
|
|
88
88
|
|
|
89
89
|
test('neutral guard is precedence-safe: env override still wins even when neutral', () => {
|
|
90
90
|
// env override short-circuits before the neutral check — order-independent.
|
|
91
|
-
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://
|
|
91
|
+
const r = cli.detectInstanceMismatch({ session: { api_base: 'https://example.com' }, brand: DEMO, env: { GDS_API_BASE: 'http://localhost:3000' }, hasInstanceConfig: neutral });
|
|
92
92
|
assert.equal(r.mismatch, false);
|
|
93
93
|
});
|
|
94
94
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// tests/leak_scan_allowlist.mjs
|
|
2
|
+
//
|
|
3
|
+
// Task 1003869 — the named exceptions to the prior-instance leak scan. The allowlist is
|
|
4
|
+
// a security carve-out, so what matters is not that it permits the right things but that
|
|
5
|
+
// it CANNOT permit the wrong ones: scoping is per (path, literal), a listed file does not
|
|
6
|
+
// become a free pass, and an entry that stops matching is reported rather than forgotten.
|
|
7
|
+
//
|
|
8
|
+
// Run: node tests/leak_scan_allowlist.mjs
|
|
9
|
+
|
|
10
|
+
import { strict as assert } from 'node:assert';
|
|
11
|
+
import { createRequire } from 'node:module';
|
|
12
|
+
|
|
13
|
+
const require = createRequire(import.meta.url);
|
|
14
|
+
const A = require('../scripts/gds/leak-scan-allowlist.js');
|
|
15
|
+
const { allowReason, partitionHits, unusedEntries, FILE_ALLOWLIST, WELL_KNOWN_PUBLIC_IPS } = A;
|
|
16
|
+
const { PRIOR_INSTANCE_FLOOR } = require('../scripts/gds/package-core.js');
|
|
17
|
+
|
|
18
|
+
// This test file is itself published, so it derives the literals it exercises rather
|
|
19
|
+
// than spelling them out — the same self-leak invariant the allowlist module holds.
|
|
20
|
+
const OWNER = PRIOR_INSTANCE_FLOOR.owners[0];
|
|
21
|
+
const TOKEN = PRIOR_INSTANCE_FLOOR.tokens[0];
|
|
22
|
+
|
|
23
|
+
let passed = 0, failed = 0;
|
|
24
|
+
function t(name, fn) {
|
|
25
|
+
try { fn(); passed++; console.log(` PASS ${name}`); }
|
|
26
|
+
catch (e) { failed++; console.log(` FAIL ${name}\n ${e.message}`); }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── every entry carries its reason ───────────────────────────────────────────────────
|
|
30
|
+
t('every entry names a path, at least one literal, and a substantive reason', () => {
|
|
31
|
+
for (const e of FILE_ALLOWLIST) {
|
|
32
|
+
assert.ok(e.path && typeof e.path === 'string', 'entry needs a path');
|
|
33
|
+
assert.ok(Array.isArray(e.classes) && e.classes.length > 0, `${e.path} needs classes`);
|
|
34
|
+
assert.ok(typeof e.reason === 'string' && e.reason.length > 40,
|
|
35
|
+
`${e.path} needs a reason that explains itself, not a label`);
|
|
36
|
+
}
|
|
37
|
+
for (const [ip, why] of Object.entries(WELL_KNOWN_PUBLIC_IPS)) {
|
|
38
|
+
assert.ok(typeof why === 'string' && why.length > 10, `${ip} needs a reason`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ── scoping: a listed file is NOT a free pass ────────────────────────────────────────
|
|
43
|
+
t('an allowlisted file still fails on a literal it was not listed for', () => {
|
|
44
|
+
// mirror-redact.js is listed for public-ip only. An owner login appearing there would
|
|
45
|
+
// be a real leak — the SELF-LEAK INVARIANT that file documents about itself.
|
|
46
|
+
assert.ok(allowReason('scripts/gds/mirror-redact.js', 'public-ip', '8.8.8.8'));
|
|
47
|
+
assert.equal(allowReason('scripts/gds/mirror-redact.js', `literal:${OWNER}`, OWNER), null,
|
|
48
|
+
'a literal whose class is not named for this path must still fail');
|
|
49
|
+
});
|
|
50
|
+
t('an entry does not leak across to another path', () => {
|
|
51
|
+
// audit-authorship.sh may carry the owner login; an arbitrary script may not.
|
|
52
|
+
assert.ok(allowReason('scripts/gds/audit-authorship.sh', `literal:${OWNER}`, OWNER));
|
|
53
|
+
assert.equal(allowReason('scripts/gds/some-other-script.js', `literal:${OWNER}`, OWNER), null);
|
|
54
|
+
});
|
|
55
|
+
t('an unlisted file gets no exception at all', () => {
|
|
56
|
+
assert.equal(allowReason('src/bongos/routes/auth.js', `literal:${TOKEN}`, TOKEN), null);
|
|
57
|
+
assert.equal(allowReason('src/anything.js', 'public-ip', '203.0.113.9'), null,
|
|
58
|
+
'an address that is not well-known is not covered anywhere');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ── well-known addresses are global, and exact ───────────────────────────────────────
|
|
62
|
+
t('a well-known public address is covered in any file, but only by exact match', () => {
|
|
63
|
+
assert.ok(allowReason('tests/anything.mjs', 'public-ip', '8.8.8.8'));
|
|
64
|
+
assert.ok(allowReason('modules/whatever/x.js', 'public-ip', '1.1.1.1'));
|
|
65
|
+
// An address that is not on the well-known list is covered NOWHERE — the whole point of
|
|
66
|
+
// the sweep. Asserted with RFC 5737 documentation addresses rather than this instance's
|
|
67
|
+
// real ones: a test that pins "our droplet must not be allowlisted" by NAMING the droplet
|
|
68
|
+
// puts it straight back into the published trunk, which is the leak it is guarding against.
|
|
69
|
+
assert.equal(allowReason('tests/anything.mjs', 'public-ip', '198.51.100.7'), null);
|
|
70
|
+
assert.equal(allowReason('tests/anything.mjs', 'public-ip', '192.0.2.99'), null);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ── partition + staleness ────────────────────────────────────────────────────────────
|
|
74
|
+
t('partitionHits separates covered hits from real ones and attaches the reason', () => {
|
|
75
|
+
const hits = [
|
|
76
|
+
{ path: 'tests/mirror_redact.mjs', label: 'public-ip', sample: '8.8.8.8' },
|
|
77
|
+
{ path: 'src/bongos/routes/auth.js', label: `literal:${TOKEN}`, sample: TOKEN },
|
|
78
|
+
];
|
|
79
|
+
const { allowed, remaining } = partitionHits(hits);
|
|
80
|
+
assert.equal(allowed.length, 1);
|
|
81
|
+
assert.ok(allowed[0].reason.length > 10, 'a covered hit carries the reason it was covered');
|
|
82
|
+
assert.equal(remaining.length, 1);
|
|
83
|
+
assert.equal(remaining[0].path, 'src/bongos/routes/auth.js');
|
|
84
|
+
});
|
|
85
|
+
t('unusedEntries reports an entry that has stopped matching', () => {
|
|
86
|
+
// No hits at all → every file entry is stale, which is the signal to prune.
|
|
87
|
+
assert.ok(unusedEntries([]).length > 0);
|
|
88
|
+
// A hit that covers one entry removes exactly that one from the stale list.
|
|
89
|
+
const stale = unusedEntries([{ path: 'scripts/gds/mirror-redact.js', label: 'public-ip', sample: '8.8.8.8' }]);
|
|
90
|
+
assert.ok(!stale.some((s) => s.path === 'scripts/gds/mirror-redact.js' && s.literal === 'public-ip'));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
94
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -72,7 +72,7 @@ function printRoots({ cwd, initCwd } = {}) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// A synthetic consumer: a dir with a bongos.config.json pointing at itself, exactly
|
|
75
|
-
// as the post-split
|
|
75
|
+
// as the post-split example repo carries (bongos.config.json { instanceRoot: "." }).
|
|
76
76
|
function makeConsumer() {
|
|
77
77
|
const dir = mkFixture('r89-consumer-');
|
|
78
78
|
writeFileSync(path.join(dir, 'bongos.config.json'), JSON.stringify({ instanceRoot: '.' }));
|
|
@@ -249,7 +249,7 @@ t('the pre-fix unquoted loop now fails loudly instead of silently skipping', ()
|
|
|
249
249
|
// ==========================================================================
|
|
250
250
|
// WHICH DATABASE does migrate target? (task 1002755)
|
|
251
251
|
//
|
|
252
|
-
// scripts/migrate.sh:52 used to read `DB="${PGDATABASE:-
|
|
252
|
+
// scripts/migrate.sh:52 used to read `DB="${PGDATABASE:-example}"` — one host instance's
|
|
253
253
|
// database name, baked into a script the core ships to EVERY instance. With no PGDATABASE in the
|
|
254
254
|
// environment, a co-hosted box silently aimed at a stranger's database and everywhere else died
|
|
255
255
|
// with a confusing "database ... does not exist" — after the caller had already moved its version
|
|
@@ -314,7 +314,7 @@ t('an instance that names no database is REFUSED, never defaulted', () => {
|
|
|
314
314
|
assert.match(r.stderr, /cannot determine which database/i);
|
|
315
315
|
assert.match(r.stderr, /Refusing to guess/i);
|
|
316
316
|
// the regression itself: it must not reach for another instance's database
|
|
317
|
-
assert.doesNotMatch(`${r.stdout}${r.stderr}`, /
|
|
317
|
+
assert.doesNotMatch(`${r.stdout}${r.stderr}`, /example/i);
|
|
318
318
|
});
|
|
319
319
|
|
|
320
320
|
t('--print-migrations stays DB-free — a dry run must not require a database name', () => {
|
|
@@ -333,7 +333,7 @@ t('migrate.sh hardcodes no instance database name', () => {
|
|
|
333
333
|
const code = src.split(/\r?\n/).filter((l) => !/^\s*#/.test(l)).join('\n');
|
|
334
334
|
assert.doesNotMatch(code, /PGDATABASE:-\s*[A-Za-z]/,
|
|
335
335
|
'PGDATABASE must have no literal fallback — resolve from the branding pack instead');
|
|
336
|
-
assert.doesNotMatch(code, /
|
|
336
|
+
assert.doesNotMatch(code, /example/i, 'no instance database name in executable lines');
|
|
337
337
|
});
|
|
338
338
|
|
|
339
339
|
console.log(`\nmigrate_root_resolution: ${passed} passed, ${failed} failed`);
|
package/tests/mirror_redact.mjs
CHANGED
|
@@ -104,12 +104,12 @@ t('isPublicRoutableIPv4 classifier boundaries', () => {
|
|
|
104
104
|
|
|
105
105
|
// ── IPv6 floor: global unicast redacted, loopback/link-local/ULA kept ─────────────────
|
|
106
106
|
t('public IPv6 (the droplet) redacted; loopback/link-local/ULA kept', () => {
|
|
107
|
-
const out = redactText('v6
|
|
107
|
+
const out = redactText('v6 2606:4700:4700::1111 keep fe80::1 fc00::5 ::1', rules);
|
|
108
108
|
assert.ok(!out.includes('2604:a880'), `public IPv6 leaked: ${out}`);
|
|
109
109
|
assert.ok(out.includes('fe80::1') && out.includes('fc00::5') && out.includes('::1'), out);
|
|
110
110
|
});
|
|
111
111
|
t('isPublicRoutableIPv6 + a "12:34:56" timestamp is NOT treated as an IP', () => {
|
|
112
|
-
assert.equal(isPublicRoutableIPv6('
|
|
112
|
+
assert.equal(isPublicRoutableIPv6('2606:4700:4700::1111'), true);
|
|
113
113
|
assert.equal(isPublicRoutableIPv6('fe80::1'), false);
|
|
114
114
|
assert.equal(isPublicRoutableIPv6('fc00::5'), false);
|
|
115
115
|
assert.equal(isPublicRoutableIPv6('::1'), false);
|
|
@@ -127,14 +127,38 @@ t('reserved ::/8 is NOT a public IP — degenerate shorthand in binary is not a
|
|
|
127
127
|
}
|
|
128
128
|
// ...and the narrowing did NOT blunt the gate: real global unicast still fires, in
|
|
129
129
|
// both the detector and the redactor.
|
|
130
|
-
for (const s of ['2000::1', '
|
|
130
|
+
for (const s of ['2000::1', '2606:4700:4700::1111', '3fff::1']) {
|
|
131
131
|
assert.equal(isPublicRoutableIPv6(s), true, `${s} is a real public address and must still be flagged`);
|
|
132
132
|
}
|
|
133
|
+
// 2001:db8::/32 is RESERVED FOR DOCUMENTATION (RFC 3849) — the IPv6 analogue of the
|
|
134
|
+
// RFC 5737 TEST-NET ranges v4 already exempts, and for the same reason: it is the
|
|
135
|
+
// address an example is SUPPOSED to use, nothing is reachable there, and flagging it
|
|
136
|
+
// only punishes docs for being written correctly (task 1003869). It was listed above
|
|
137
|
+
// as "a real public address" until then, which RFC 3849 says it is not.
|
|
138
|
+
for (const s of ['2001:db8::1', '2001:0db8:85a3:0000:0000:8a2e:0370:7334']) {
|
|
139
|
+
assert.equal(isPublicRoutableIPv6(s), false, `${s} is the RFC 3849 doc range and must not be flagged`);
|
|
140
|
+
}
|
|
133
141
|
assert.equal(firstPublicIP('sprite bytes :: more bytes'), null, 'binary-ish shorthand is not reported');
|
|
134
142
|
assert.equal(firstPublicIP('origin 2606:4700:4700::1111'), '2606:4700:4700::1111');
|
|
135
143
|
assert.ok(!redactText('host 2606:4700:4700::1111', []).includes('2606:4700'), 'a real public IPv6 is still redacted');
|
|
136
144
|
});
|
|
137
145
|
|
|
146
|
+
// ── candidate guards: things that LOOK like an address but are not one ───────────────
|
|
147
|
+
t('a leading-zero octet is packed numeric data, not an address (task 1003869)', () => {
|
|
148
|
+
// The committed GitHub logo path contains "…5.47 7.59.4.07.5…". The candidate regex
|
|
149
|
+
// reads 7.59.4.07 as a dotted quad, and it flagged ten phantom public-ip leaks across
|
|
150
|
+
// the design mocks and the landing page. No notation writes a host with a leading-zero
|
|
151
|
+
// octet ("07" is octal), so the guard costs nothing on a genuinely leaked address.
|
|
152
|
+
assert.equal(firstPublicIP('M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.5-.17'), null);
|
|
153
|
+
assert.equal(firstPublicIP('host 7.59.4.07'), null);
|
|
154
|
+
assert.equal(firstPublicIP('host 7.59.4.7'), '7.59.4.7', 'the same quad without the zero IS an address');
|
|
155
|
+
});
|
|
156
|
+
t('RFC 2544 benchmarking space 198.18/15 is not instance topology', () => {
|
|
157
|
+
assert.equal(isPublicRoutableIPv4(198, 18, 0, 1), false);
|
|
158
|
+
assert.equal(isPublicRoutableIPv4(198, 19, 255, 254), false);
|
|
159
|
+
assert.equal(isPublicRoutableIPv4(198, 20, 0, 1), true, 'the neighbouring /15 is ordinary public space');
|
|
160
|
+
});
|
|
161
|
+
|
|
138
162
|
// ── the DETECTOR is stricter than redaction ───────────────────────────────────────────
|
|
139
163
|
t('findLeaks catches a raw literal substring a bounded rule would miss', () => {
|
|
140
164
|
// "azoltanb" has no word boundary around zoltan; a \bzoltan\b rule misses it, the
|
|
@@ -143,7 +167,7 @@ t('findLeaks catches a raw literal substring a bounded rule would miss', () => {
|
|
|
143
167
|
assert.ok(findLeaks('inline widgetco reference', literals).length > 0);
|
|
144
168
|
});
|
|
145
169
|
t('findLeaks is clean on fully-redacted output (fixed point)', () => {
|
|
146
|
-
const dirty = 'https://widgetco.com by zoltan at 8.8.8.8 and
|
|
170
|
+
const dirty = 'https://widgetco.com by zoltan at 8.8.8.8 and 2606:4700:4700::1111';
|
|
147
171
|
const clean = redactText(dirty, rules);
|
|
148
172
|
assert.deepEqual(findLeaks(clean, literals), [], `residual leaks: ${JSON.stringify(findLeaks(clean, literals))}`);
|
|
149
173
|
});
|
package/tests/module_catalog.mjs
CHANGED
|
@@ -121,7 +121,7 @@ test('buildCatalog: keeps the DECLARED manifest fields beside the display credit
|
|
|
121
121
|
|
|
122
122
|
test('buildCatalog: source and provenance are independent axes', () => {
|
|
123
123
|
const rows = catalog.buildCatalog({
|
|
124
|
-
loaded: [loadedIn(CORE, 'provisioning', { origin: '
|
|
124
|
+
loaded: [loadedIn(CORE, 'provisioning', { origin: 'Example World', author: 'Example World' })],
|
|
125
125
|
coreRoot: CORE, instanceRoot: INST,
|
|
126
126
|
ownProjectName: 'Mercury',
|
|
127
127
|
});
|
|
@@ -156,7 +156,7 @@ test('buildCatalog: survives a manifest with nothing but the required keys', ()
|
|
|
156
156
|
const sample = () => catalog.buildCatalog({
|
|
157
157
|
loaded: [
|
|
158
158
|
loadedIn(CORE, 'economy', { description: 'drachmae and credits' }),
|
|
159
|
-
loadedIn(CORE, 'provisioning', { author: '
|
|
159
|
+
loadedIn(CORE, 'provisioning', { author: 'Example World', origin: 'Example World', license: 'AGPL-3.0' }),
|
|
160
160
|
loadedIn(INST, 'game', { description: 'the world' }),
|
|
161
161
|
],
|
|
162
162
|
enabled: { economy: true, game: true },
|
|
@@ -174,7 +174,7 @@ test('searchCatalog: matches key, description, author, origin and licence; empty
|
|
|
174
174
|
assert.equal(catalog.searchCatalog(rows, '').length, 3);
|
|
175
175
|
assert.equal(catalog.searchCatalog(rows, ' ').length, 3);
|
|
176
176
|
assert.deepEqual(catalog.searchCatalog(rows, 'DRACHMAE').map((r) => r.key), ['economy'], 'case-insensitive, over description');
|
|
177
|
-
assert.deepEqual(catalog.searchCatalog(rows, '
|
|
177
|
+
assert.deepEqual(catalog.searchCatalog(rows, 'example world').map((r) => r.key), ['provisioning'], 'over the credit');
|
|
178
178
|
assert.deepEqual(catalog.searchCatalog(rows, 'agpl').map((r) => r.key), ['provisioning'], 'over the licence');
|
|
179
179
|
assert.deepEqual(catalog.searchCatalog(rows, 'nothing-matches-this'), []);
|
|
180
180
|
});
|
|
@@ -202,7 +202,7 @@ test('formatCatalog: renders every row with its credit, and marks the inferred o
|
|
|
202
202
|
assert.match(out, /2 in the core catalog \(1 enabled\) · 1 built here · 3 total/);
|
|
203
203
|
assert.match(out, /\[x\]\s+economy/, 'an enabled module is marked [x]');
|
|
204
204
|
assert.match(out, /\[ \]\s+provisioning/, 'a disabled module is marked [ ]');
|
|
205
|
-
assert.match(out, /
|
|
205
|
+
assert.match(out, /Example World/, 'a declared credit is shown');
|
|
206
206
|
assert.match(out, /credit not declared in module\.json/, 'the inferred-credit footnote appears when one is shown');
|
|
207
207
|
});
|
|
208
208
|
|
|
@@ -227,7 +227,7 @@ test('formatCatalog: a search with no hits says so instead of printing an empty
|
|
|
227
227
|
|
|
228
228
|
test('cmdList: --json emits the rows + summary, and honours --catalog/--search', () => {
|
|
229
229
|
const lines = [];
|
|
230
|
-
const code = mod.cmdList(['--json', '--catalog', '--search', '
|
|
230
|
+
const code = mod.cmdList(['--json', '--catalog', '--search', 'example world'], {
|
|
231
231
|
log: (s) => lines.push(s),
|
|
232
232
|
gather: () => ({ rows: sample(), skipped: [], errors: [], core: '1.19.0' }),
|
|
233
233
|
});
|
|
@@ -59,14 +59,14 @@ await test('createModuleSubmission() INSERTs, jsonb-casts manifest/files/sign_of
|
|
|
59
59
|
const precheckPassedAt = new Date('2026-07-08T00:00:00.000Z');
|
|
60
60
|
nextRows = [{
|
|
61
61
|
id: 1, module_key: 'widget', submitted_by: 42, manifest, sign_off: signOff,
|
|
62
|
-
precheck_passed_at: precheckPassedAt, source_repo: '
|
|
62
|
+
precheck_passed_at: precheckPassedAt, source_repo: 'example-owner/example',
|
|
63
63
|
status: 'pending', reviewed_by: null, reviewed_at: null, review_notes: null,
|
|
64
64
|
submitted_at: '2026-07-08T00:00:01.000Z',
|
|
65
65
|
}];
|
|
66
66
|
|
|
67
67
|
const row = await moduleSubmissions.createModuleSubmission({
|
|
68
68
|
moduleKey: 'widget', submittedBy: 42, manifest, files, signOff, precheckPassedAt,
|
|
69
|
-
sourceRepo: '
|
|
69
|
+
sourceRepo: 'example-owner/example',
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
const { text, params } = lastCall();
|
|
@@ -83,7 +83,7 @@ await test('createModuleSubmission() INSERTs, jsonb-casts manifest/files/sign_of
|
|
|
83
83
|
assert.equal(params[3], JSON.stringify(files));
|
|
84
84
|
assert.equal(params[4], JSON.stringify(signOff));
|
|
85
85
|
assert.equal(params[5], precheckPassedAt);
|
|
86
|
-
assert.equal(params[6], '
|
|
86
|
+
assert.equal(params[6], 'example-owner/example');
|
|
87
87
|
|
|
88
88
|
assert.equal(row.id, 1);
|
|
89
89
|
assert.equal(row.status, 'pending');
|
package/tests/package_core.mjs
CHANGED
|
@@ -585,7 +585,7 @@ t('scanArtifactForLeaks: flags a prior-instance literal in any shipped file', ()
|
|
|
585
585
|
|
|
586
586
|
t('scanArtifactForLeaks: flags a public IP but not a private/localhost one', () => {
|
|
587
587
|
const r = scanArtifactForLeaks([
|
|
588
|
-
{ path: 'a.js', content: 'host =
|
|
588
|
+
{ path: 'a.js', content: 'host = 1.2.3.4\n' }, // public → flagged
|
|
589
589
|
{ path: 'b.js', content: 'host = 127.0.0.1; lan = 10.0.0.5\n' }, // localhost + private → not
|
|
590
590
|
]);
|
|
591
591
|
const byPath = Object.fromEntries(r.literalHits.map((h) => [h.path, h.label]));
|