@bongos/core 1.19.624 → 1.19.625

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 CHANGED
@@ -2,11 +2,11 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.624",
6
- "core_contract": "1.19.624",
7
- "source_commit": "40ba55a6d7d145b717b36a0b316bfb4ed91c0fdd",
5
+ "core_version": "1.19.625",
6
+ "core_contract": "1.19.625",
7
+ "source_commit": "19a2b295b0d1a0755168ec39b249dc1a5f9b961e",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-09T07:52:09.912Z",
9
+ "built_at": "2026-09-09T08:21:23.081Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 464,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2582,
20
- "tree_sha256": "19b515bb213f54c2eafb50bebf229ada6ea886fdff0fa68d3ec68e30c3b93cbf",
20
+ "tree_sha256": "c6d6c5f0e0b67618fd3d94d6beb816506c9d15c480cc0dc0de0768b4b27cce23",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/backlog-review/SKILL.md",
@@ -2757,7 +2757,7 @@
2757
2757
  {
2758
2758
  "path": "docs/module-api-changelog.md",
2759
2759
  "mode": "0000644",
2760
- "sha256": "ddb02e1135abdcdd075c164efb243f1fae2c43d87543cd4466bf24efb7e020e7"
2760
+ "sha256": "6979862e5d574d45a8cf53196775cb2f3c53e601e4f812e3bd69c3b3a9c7cc15"
2761
2761
  },
2762
2762
  {
2763
2763
  "path": "docs/modules-contract.md",
@@ -7682,12 +7682,12 @@
7682
7682
  {
7683
7683
  "path": "package-lock.json",
7684
7684
  "mode": "0000644",
7685
- "sha256": "b8c2a8fc2e6548e76390b5898057eb845926c9ab89b325670b94a5cc80f61e6f"
7685
+ "sha256": "b1bf1316b6bf58eede860ae199233be76e1beb440d2cf7f4c96171e8f6347f05"
7686
7686
  },
7687
7687
  {
7688
7688
  "path": "package.json",
7689
7689
  "mode": "0000644",
7690
- "sha256": "33c03cfa6c4529cce4cd599ce51b14bf480f4d6f2d5e7e5d9d7a6033e404840e"
7690
+ "sha256": "1e54c8559b66bcda15cbfb24e216bb5d3421a9e0c9d07f240c2414019288fe9a"
7691
7691
  },
7692
7692
  {
7693
7693
  "path": "public-docs/index.html",
@@ -9422,7 +9422,7 @@
9422
9422
  {
9423
9423
  "path": "src/module-api.js",
9424
9424
  "mode": "0000644",
9425
- "sha256": "f324be1b92aa674fa8bc1baff53336ad5ff21137864d9dd237c9ddc85f7ca273"
9425
+ "sha256": "7a6f5a46c866658041e6299fe46188834184f510dd496edef5cb2f7b0c414088"
9426
9426
  },
9427
9427
  {
9428
9428
  "path": "src/module-loader/catalog.js",
@@ -10032,7 +10032,7 @@
10032
10032
  {
10033
10033
  "path": "tests/cli_sessions.mjs",
10034
10034
  "mode": "0000644",
10035
- "sha256": "6ec406bf2bcbae261ab37ecaf820cfbe196c11007b0d8d3ee47cf21b6a3919ca"
10035
+ "sha256": "ab1ee394c03eccadee0958ea38595e1f4e221146b593559e3474c9573d81ecf7"
10036
10036
  },
10037
10037
  {
10038
10038
  "path": "tests/cli_surface.mjs",
@@ -1697,5 +1697,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
1697
1697
  landed since 1.19.622 with no explicit bump. run 34323141703. (task 1002620)
1698
1698
  1.19.624 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1699
1699
  landed since 1.19.623 with no explicit bump. run 34326009063. (task 1002620)
1700
+ 1.19.625 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1701
+ landed since 1.19.624 with no explicit bump. run 34328603214. (task 1002620)
1700
1702
  ---------------------------------------------------------------------------
1701
1703
  ```
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.624",
3
+ "version": "1.19.625",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.624",
9
+ "version": "1.19.625",
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.624",
3
+ "version": "1.19.625",
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
@@ -55,7 +55,7 @@ const { buildInfo } = require('./build-info');
55
55
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
56
56
  // the entry to that file. Look for a version's history there, not here.
57
57
  // ---------------------------------------------------------------------------
58
- const CORE_VERSION = '1.19.624'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
58
+ const CORE_VERSION = '1.19.625'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
59
59
 
60
60
  // A namespaced logger so a module's log lines are attributable + consistent.
61
61
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -28,16 +28,79 @@ const ic = require(path.join(REPO_ROOT, 'src', 'instance-config.js'));
28
28
  const lib = require(path.join(REPO_ROOT, 'scripts', 'gds', 'cli-lib.js'));
29
29
 
30
30
  // SESSION_PATH is frozen at module load from os.homedir(), so anything touching the real save path
31
- // runs in a child with its own HOME.
31
+ // runs in a child with its own home directory.
32
+ //
33
+ // SETTING `HOME` ALONE IS NOT ISOLATION, AND THE MISS COST REAL CREDENTIALS (task 1003760).
34
+ // On Windows os.homedir() reads USERPROFILE and never consults HOME, so the original
35
+ // `env: { ...process.env, HOME: home }` isolated nothing there: every child below wrote
36
+ // its fixtures into the developer's ACTUAL ~/.config/<instance>/, overwriting the live
37
+ // CLI session with `token:'TOKEN-A', api_base:'https://a.example.com'` and leaving
38
+ // a.example.com.json / b.example.com.json / broken.json in the real session store. The
39
+ // owner had to re-issue a CLI token to recover, and because a claim is bound to the
40
+ // session that created it, the re-auth then orphaned an in-flight claim too.
41
+ //
42
+ // CI is Linux, where HOME *is* honoured — so this can never go red there. Two layers,
43
+ // because the platform list is not knowable in advance:
44
+ // 1. Override every variable a platform might read (USERPROFILE is the Windows one;
45
+ // HOMEDRIVE/HOMEPATH are the legacy fallback os.homedir() tries next).
46
+ // 2. FAIL CLOSED IN THE CHILD: prove os.homedir() actually landed in the sandbox
47
+ // BEFORE running any body, and abort if it did not. A future platform that reads
48
+ // some third variable then fails this test loudly instead of silently eating the
49
+ // developer's credentials — which is the only outcome that is acceptable for a
50
+ // test whose whole job is writing to a session store.
51
+ // THE AUDIT THIS FIX OWED, RECORDED HERE RATHER THAN IN A SHIP NOTE. `grep -rn "HOME:"
52
+ // tests/ scripts/` at the time of the fix (task 1003760) found the pattern in ten files.
53
+ // Three already override the Windows variable and are correct: tests/memory_pull.mjs:438,
54
+ // tests/redteam_patrol.mjs:123, tests/setup_device_flow_fallback.mjs:130 — so the
55
+ // convention existed and THIS file simply missed it. Two still set HOME alone and can go
56
+ // FALSE-GREEN on Windows, tracked as task 1003763: tests/cli_package.mjs:140+149 (whose own
57
+ // comment says the temp HOME exists "so a real session file on this machine cannot make a
58
+ // verb look healthier than it is" — on Windows it can) and tests/cli_surface.mjs:174 (which
59
+ // points HOME at a nonexistent dir to simulate "no session", and on Windows sees the real
60
+ // one). Neither DESTROYS anything — this file was the only one writing a session store — so
61
+ // they were filed rather than folded in. Two uses are legitimately HOME-only and must stay:
62
+ // tests/scrubber_corpus.mjs:228 uses HOME:'/root' as corpus DATA, and
63
+ // tests/lib_sh_resolution.mjs exercises POSIX shell resolution where HOME is the subject.
64
+ // They are why closing this class needs an allowlist, not a blanket rule (1003763).
65
+ //
66
+ // Also done off-repo when this landed, and unverifiable from a diff, so stated plainly: the
67
+ // three fixture files this bug left in the REAL session store — a.example.com.json,
68
+ // b.example.com.json, broken.json under ~/.config/<instance>/instances/ — were deleted, and
69
+ // the live session + auth were re-verified afterwards.
70
+ //
71
+ // KNOWN GAP, deliberately left: on Windows this suite now asserts no permission-tightness
72
+ // property at all (see the owner-only test below). NTFS has no POSIX triad, so the real
73
+ // property is the per-user profile ACL; an icacls-style assertion would restore the signal
74
+ // and does not exist here.
75
+ function sandboxEnv(home) {
76
+ const { root } = path.parse(home);
77
+ return {
78
+ ...process.env,
79
+ HOME: home,
80
+ USERPROFILE: home, // Windows: what os.homedir() actually reads
81
+ HOMEDRIVE: root.replace(/[\\/]+$/, ''), // legacy Windows fallback pair
82
+ HOMEPATH: home.slice(root.length - 1) || '\\',
83
+ };
84
+ }
85
+
32
86
  function inSandbox(body) {
33
87
  const home = fs.mkdtempSync(path.join(os.tmpdir(), 'bongos-sess-'));
34
88
  try {
35
89
  const code = `const R=${JSON.stringify(REPO_ROOT)};
90
+ const SANDBOX=${JSON.stringify(home)};
91
+ const os=require('node:os');
92
+ // Layer 2: refuse to touch a session store outside the sandbox. Runs before the
93
+ // requires below so not even a module-load side effect can reach the real home.
94
+ if (os.homedir() !== SANDBOX) {
95
+ console.error('ERR sandbox-escape: os.homedir()=' + os.homedir() + ' but the sandbox is ' + SANDBOX
96
+ + ' — this platform does not take the home override, and writing here would clobber the real session store.');
97
+ process.exit(2);
98
+ }
36
99
  const lib=require(R+'/scripts/gds/cli-lib.js');
37
100
  const ic=require(R+'/src/instance-config.js');
38
101
  const fs=require('node:fs'), path=require('node:path');
39
102
  (async()=>{ ${body} })().catch((e)=>{ console.error('ERR '+e.message); process.exit(1); });`;
40
- const r = spawnSync(process.execPath, ['-e', code], { encoding: 'utf8', env: { ...process.env, HOME: home } });
103
+ const r = spawnSync(process.execPath, ['-e', code], { encoding: 'utf8', env: sandboxEnv(home) });
41
104
  return { home, out: `${r.stdout || ''}${r.stderr || ''}`, status: r.status };
42
105
  } finally {
43
106
  fs.rmSync(home, { recursive: true, force: true });
@@ -78,6 +141,36 @@ test('the store anchor is FIXED, not brand-derived — that is the whole fix', (
78
141
  assert.ok(!/configHome|configDirName|safeBrand/.test(fn), `sessionStoreDir must not read the brand:\n${fn}`);
79
142
  });
80
143
 
144
+ // ── 1b. The sandbox is really a sandbox ─────────────────────────────────────────────────────
145
+ //
146
+ // These guard the guard (task 1003760). Every test below writes to a session store, so if
147
+ // the isolation is a no-op they write to the DEVELOPER'S store — which is exactly what
148
+ // happened on Windows, where os.homedir() reads USERPROFILE and the original helper set
149
+ // only HOME. A test suite that can delete credentials must prove it cannot before it runs.
150
+
151
+ test('the sandbox actually relocates os.homedir() — on THIS platform', () => {
152
+ const { out, status, home } = inSandbox(`console.log(JSON.stringify({ h: require('node:os').homedir() }));`);
153
+ assert.equal(status, 0, `sandbox child failed: ${out}`);
154
+ const { h } = JSON.parse(out.trim().split('\n').pop());
155
+ assert.equal(h, home,
156
+ 'os.homedir() inside the sandbox must BE the sandbox — otherwise every test here writes to the real session store');
157
+ });
158
+
159
+ test('a session store path resolved inside the sandbox stays inside it', () => {
160
+ const { out, status, home } = inSandbox(`console.log(JSON.stringify({ p: ic.configPath('gds-session.json'), d: lib.sessionStoreDir() }));`);
161
+ assert.equal(status, 0, `sandbox child failed: ${out}`);
162
+ const { p, d } = JSON.parse(out.trim().split('\n').pop());
163
+ assert.ok(p.startsWith(home), `the active session pointer resolved OUTSIDE the sandbox: ${p}`);
164
+ assert.ok(d.startsWith(home), `the session store dir resolved OUTSIDE the sandbox: ${d}`);
165
+ });
166
+
167
+ test('sandboxEnv overrides every home variable a platform might read', () => {
168
+ const env = sandboxEnv(path.join(os.tmpdir(), 'probe-home'));
169
+ assert.equal(env.HOME, path.join(os.tmpdir(), 'probe-home'));
170
+ assert.equal(env.USERPROFILE, env.HOME, 'USERPROFILE is the one Windows reads — the original miss');
171
+ assert.ok('HOMEDRIVE' in env && 'HOMEPATH' in env, 'the legacy Windows fallback pair must be set too');
172
+ });
173
+
81
174
  // ── 2. A login never destroys another instance's session ────────────────────────────────────
82
175
 
83
176
  test('signing into a second instance PRESERVES the first — including one written before the store existed', () => {
@@ -127,13 +220,25 @@ test('re-saving the SAME instance is idempotent and files nothing extra', () =>
127
220
  assert.equal(r.token, 'T2');
128
221
  });
129
222
 
223
+ // POSIX MODE BITS DO NOT EXIST ON WINDOWS, so this assertion could never pass there
224
+ // (task 1003760). NTFS has no owner/group/other triad: Node reports 0666 for any
225
+ // writable file and 0444 for a read-only one, and chmod(0o600) is silently a no-op —
226
+ // measured on win32, both before and after an explicit chmod. Asserting '600' there
227
+ // made the unit suite permanently red on every Windows checkout, which is the exact
228
+ // harm task 1003754 names: a test that always fails is how a genuinely failing suite
229
+ // stops being read. So the mode claim is asserted where the platform can express it,
230
+ // and on Windows the file's EXISTENCE is still asserted — the security property has
231
+ // to be met by NTFS ACLs (the per-user profile directory), not by a mode integer.
130
232
  test('session files are owner-only', () => {
131
233
  const { out } = inSandbox(`
132
234
  await lib.saveSession({ token:'T', api_base:'https://a.example.com', builder:{github_login:'x'} });
133
- const f = fs.statSync(lib.sessionStorePath('https://a.example.com')).mode & 0o777;
235
+ const p = lib.sessionStorePath('https://a.example.com');
236
+ const f = fs.statSync(p).mode & 0o777;
134
237
  const d = fs.statSync(lib.sessionStoreDir()).mode & 0o777;
135
- console.log(JSON.stringify({ file: f.toString(8), dir: d.toString(8) }));`);
238
+ console.log(JSON.stringify({ file: f.toString(8), dir: d.toString(8), exists: fs.existsSync(p) }));`);
136
239
  const r = JSON.parse(out.trim().split('\n').pop());
240
+ assert.equal(r.exists, true, 'the session file must be written wherever we run');
241
+ if (process.platform === 'win32') return; // no POSIX triad to assert — see above
137
242
  assert.equal(r.file, '600', 'a session file holds a bearer token');
138
243
  assert.equal(r.dir, '700');
139
244
  });