@bongos/core 1.19.312 → 1.19.314

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.312",
6
- "core_contract": "1.19.312",
7
- "source_commit": "1115918f0f231eb592993c52535bccf7459ba201",
5
+ "core_version": "1.19.314",
6
+ "core_contract": "1.19.314",
7
+ "source_commit": "7d49e680ed9b88bc3fd32760e275bc2f52348673",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-23T20:51:36.647Z",
9
+ "built_at": "2026-08-23T21:48:01.343Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 323,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1766,
20
- "tree_sha256": "8fa5a70e1592609c53386120743ab236e02cd64aaf8c3911891bd79d6a8da6ea",
20
+ "tree_sha256": "59daa4e8041f110026fee1110197d244eb74cd075b36da6d4fc0d18f4bac0b18",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1672,7 +1672,7 @@
1672
1672
  {
1673
1673
  "path": "docs/recipes/core-release-pipeline.md",
1674
1674
  "mode": "0000644",
1675
- "sha256": "b42d8b4620355bef0e27480b2ddfaa07140e322de988839f0e102f1a1545ac47"
1675
+ "sha256": "15517dbf462ce85e7d161b4e173c90376a19ecad3d0cf57a728f066533697bb2"
1676
1676
  },
1677
1677
  {
1678
1678
  "path": "docs/recipes/devbox-app-release.md",
@@ -4582,12 +4582,12 @@
4582
4582
  {
4583
4583
  "path": "package-lock.json",
4584
4584
  "mode": "0000644",
4585
- "sha256": "57b834edcb83e8b41bb7535bbacc1740537b0f3551726295884ea35615a88686"
4585
+ "sha256": "e2b6abac7133ee151991f267ba79cb55ed677547ebc8adaf573288a959aecc01"
4586
4586
  },
4587
4587
  {
4588
4588
  "path": "package.json",
4589
4589
  "mode": "0000644",
4590
- "sha256": "7e8d9420573b8eabbe2ad7f96f0317d4b454fcf5788b68bfc6afdf6fb45b3cb4"
4590
+ "sha256": "47771a7e9c28952af15efa1c8f64fb44cae34698bc385484d1250878145dfc18"
4591
4591
  },
4592
4592
  {
4593
4593
  "path": "public-docs/index.html",
@@ -5317,7 +5317,7 @@
5317
5317
  {
5318
5318
  "path": "scripts/gds/go-live.js",
5319
5319
  "mode": "0000644",
5320
- "sha256": "05734e21763cc0b0614275efa76b7b68afabee8be93f3f2d797400d6a97e86c1"
5320
+ "sha256": "fa7803b178d64eda47d4f05032353582f7000359b13caea7d018112f03c9906a"
5321
5321
  },
5322
5322
  {
5323
5323
  "path": "scripts/gds/goal.js",
@@ -6257,7 +6257,7 @@
6257
6257
  {
6258
6258
  "path": "src/module-api.js",
6259
6259
  "mode": "0000644",
6260
- "sha256": "a35a7a2ca59f77d06b12aaaec28b4d82bbe5a4713289c072d4e9cc45d0d467e5"
6260
+ "sha256": "eac68b3c94db0a8eb79241d515a0df3d19d96f6fb41165c32ea4a963753bd5b1"
6261
6261
  },
6262
6262
  {
6263
6263
  "path": "src/module-loader/catalog.js",
@@ -6422,7 +6422,7 @@
6422
6422
  {
6423
6423
  "path": "tests/audit_gate.mjs",
6424
6424
  "mode": "0000644",
6425
- "sha256": "af91c6018f572d9cb0a213b40ec7953f9f81cc20a33634fb5a66615a1649fee8"
6425
+ "sha256": "ee9dc18e86ac819235d7215f0cee14ff7bfc8d07c46eba6895c23f99c7d0ae0e"
6426
6426
  },
6427
6427
  {
6428
6428
  "path": "tests/audit_middleware.mjs",
@@ -7182,7 +7182,7 @@
7182
7182
  {
7183
7183
  "path": "tests/go_live.mjs",
7184
7184
  "mode": "0000644",
7185
- "sha256": "90c8cbfd8bf0b446261f9fd837b9b08b1035210f1cbf1f7ff81998ec535c32f1"
7185
+ "sha256": "360712fee43b00b410e4b3b8ca27e801ae41966ac0c5ce0a4679fa7943aa554b"
7186
7186
  },
7187
7187
  {
7188
7188
  "path": "tests/goal_abuse_matrix.mjs",
@@ -188,6 +188,11 @@ The permission classifier blocks the agent from running a mutating `sudo -u lars
188
188
  What each piece is for — each guards a failure that actually happened:
189
189
 
190
190
  - **Token extraction runs as lars** (the whole command is inside `sudo -u lars bash -c`). Reading `/home/lars/.npmrc` as `jaxri` is `Permission denied`, which yields an **empty** `NPM_TOKEN`, which sends npm to the **public** registry, which 404s on the private `@bongos/core` — and the upgrade rolls back cleanly, having taught you nothing. The `[ -n "$NPM_TOKEN" ] || exit` guard aborts instead of half-running.
191
+ - **Never put the token in the command line — `sudo` logs the full argv (task 1003218).** The shape above is safe *because* the token is read INSIDE the quoted script, so only the `grep` reaches argv. The tempting variant is not:
192
+ ```
193
+ sudo -u lars -H env NPM_TOKEN="<redacted>" … bongos upgrade … # ← LEAKS
194
+ ```
195
+ The shell expands `$NPM_TOKEN` before `sudo` ever runs, so the real token lands in the system journal — which is persistent on this box. That variant put the live token into 26 journal lines between 2026-08-17 and 2026-08-23. If you need to hand a secret to a privileged child, use `sudo --preserve-env=NPM_TOKEN …`, which passes it through the environment where nothing logs it. Passing "by reference" in the script text is NOT enough; what matters is what argv looks like after expansion.
191
196
  - **`--to <VER>` is required** — `bongos upgrade` refuses to guess a target.
192
197
  - **`--service` + `--health-url` buy the automatic rollback.** Point the health URL at `http://127.0.0.1:3002/healthz`, **not the public URL** — a pre-launch `basic_auth` on the apex makes a healthy boot look failed and triggers a needless rollback.
193
198
  - **`--commit-pin` commits AND pushes the pin.** The instance is a pull-deploy repo: the next sync runs `git reset --hard origin/main`, which reverts an uncommitted pin, and `npm ci` then reinstalls the old core. Nothing errors. This is exactly how live sat 13 releases behind (task 1002712). The same applies to any tracked instance-config edit (`config/branding.json`): commit + push or the next deploy tick undoes it. A commit that fails to *push* saves nothing — deploy.sh resets to the remote.
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.312",
3
+ "version": "1.19.314",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.312",
9
+ "version": "1.19.314",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
@@ -48,9 +48,9 @@
48
48
  }
49
49
  },
50
50
  "node_modules/body-parser": {
51
- "version": "1.20.5",
52
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
53
- "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
51
+ "version": "1.20.6",
52
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
53
+ "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
54
54
  "license": "MIT",
55
55
  "dependencies": {
56
56
  "bytes": "~3.1.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.312",
3
+ "version": "1.19.314",
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",
@@ -29,6 +29,6 @@
29
29
  "pino": "^9.14.0"
30
30
  },
31
31
  "overrides": {
32
- "undici": "^6.27.0"
32
+ "undici": "^6.28.0"
33
33
  }
34
34
  }
@@ -177,22 +177,36 @@ function remote(target, script, { run = spawnSync, timeoutMs = 900000 } = {}) {
177
177
  // `sudo -u <user> -H env K=V … <cmd>` — the shape every runbook entry uses. Returns
178
178
  // a string safe to embed because every component was allowlist-validated.
179
179
  //
180
- // `forward` names variables to pass through BY REFERENCE (`K="$K"`), for values that
181
- // exist in the calling root shell but must not appear in this script's text. That is
182
- // how NPM_TOKEN reaches the upgrade: sudo's env_reset drops it, and an explicit
183
- // `env K=V` list that omits it drops it too — so `npm install` 404s the PRIVATE
184
- // package and the upgrade auto-rolls-back, which reads as "the release is broken"
185
- // when it is only "the token never arrived". Sourcing the env file in the OUTER shell
186
- // is necessary and NOT sufficient; this is the second half of that trap.
180
+ // `forward` names variables that exist in the calling root shell and must reach the
181
+ // child — NPM_TOKEN above all. sudo's env_reset drops it, and an explicit `env K=V`
182
+ // list that omits it drops it too, so `npm install` 404s the PRIVATE package and the
183
+ // upgrade auto-rolls-back, which reads as "the release is broken" when it is only
184
+ // "the token never arrived". Sourcing the env file in the OUTER shell is necessary
185
+ // and NOT sufficient.
186
+ //
187
+ // A FORWARDED VARIABLE IS A SECRET, SO IT MUST NOT ENTER argv (task 1003218).
188
+ // `env K="$K"` keeps the value out of the script TEXT, but the shell EXPANDS it
189
+ // before sudo runs — and sudo logs the full argv. That is how 26 lines of
190
+ // `COMMAND=... NPM_TOKEN=npm_<real token> ...` ended up in the droplet's PERSISTENT
191
+ // journal between 2026-08-17 and 2026-08-23. Passing by reference is NOT the same as
192
+ // keeping a secret out of argv; only `--preserve-env` does that, handing the variable
193
+ // over through the environment where nothing logs it. Verified side by side on the
194
+ // box: both forms reach the child, only this one leaves no value in the journal.
195
+ //
196
+ // The plain `env K=V` list is still used for NON-secret values (PGDATABASE, HOME) —
197
+ // those are configuration, and seeing them in a log is useful rather than harmful.
187
198
  function asOwner(target, cmd, { forward = [] } = {}) {
188
199
  const envPairs = Object.entries(target.env || {}).map(([k, v]) => `${k}=${v}`);
189
- for (const k of forward) {
190
- if (!SAFE_ENV_KEY_RE.test(k)) continue; // never interpolate an unvetted name
191
- envPairs.push(`${k}="$${k}"`); // by reference — the VALUE is never written here
200
+ const preserve = forward.filter((k) => SAFE_ENV_KEY_RE.test(k)); // never interpolate an unvetted name
201
+ if (!target.runAs) {
202
+ // No privilege drop: the child already inherits our environment, so a forwarded
203
+ // secret needs no mention at all.
204
+ const joined = envPairs.join(' ');
205
+ return `${joined ? `env ${joined} ` : ''}${cmd}`.trim();
192
206
  }
193
- const joined = envPairs.join(' ');
194
- const prefix = target.runAs ? `sudo -u ${target.runAs} -H env ${joined} HOME=/home/${target.runAs}` : (joined ? `env ${joined}` : '');
195
- return `${prefix} ${cmd}`.trim();
207
+ const keep = preserve.length ? `--preserve-env=${preserve.join(',')} ` : '';
208
+ const joined = [...envPairs, `HOME=/home/${target.runAs}`].join(' ');
209
+ return `sudo ${keep}-u ${target.runAs} -H env ${joined} ${cmd}`.trim();
196
210
  }
197
211
 
198
212
  // POSIX single-quote a value for embedding in a shell command.
package/src/module-api.js CHANGED
@@ -1051,8 +1051,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
1051
1051
  // landed since 1.19.310 with no explicit bump. run 32665535518. (task 1002620)
1052
1052
  // 1.19.312 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1053
1053
  // landed since 1.19.311 with no explicit bump. run 32665664699. (task 1002620)
1054
+ // 1.19.313 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1055
+ // landed since 1.19.312 with no explicit bump. run 32668457836. (task 1002620)
1056
+ // 1.19.314 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1057
+ // landed since 1.19.313 with no explicit bump. run 32668599111. (task 1002620)
1054
1058
  // ---------------------------------------------------------------------------
1055
- const CORE_VERSION = '1.19.312'; // CI auto-patch carrier (ADR 0161); see changelog above
1059
+ const CORE_VERSION = '1.19.314'; // CI auto-patch carrier (ADR 0161); see changelog above
1056
1060
 
1057
1061
  // A namespaced logger so a module's log lines are attributable + consistent.
1058
1062
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -79,9 +79,14 @@ test('empty/missing baseline means every high gates (the fresh-repo case)', () =
79
79
  assert.equal(v.newAdvisory.length, 2, 'the two undici moderates advise');
80
80
  });
81
81
 
82
+ // task 1003201 (goal 1000079) drove the baseline to ZERO — every advisory it
83
+ // recorded is fixed, not tolerated. An empty findings[] is therefore the goal
84
+ // state, not a broken file; the shape assertions below still hold if debt ever
85
+ // reappears. (It must stay an array — a missing/!array findings would make the
86
+ // gate silently treat every new high as pre-existing.)
82
87
  test('the committed baseline file parses and only carries known severities', () => {
83
88
  const b = require(path.join(ROOT, 'config', 'audit-baseline.json'));
84
- assert.ok(Array.isArray(b.findings) && b.findings.length > 0);
89
+ assert.ok(Array.isArray(b.findings), 'findings must be an array (empty = no tolerated debt)');
85
90
  for (const f of b.findings) {
86
91
  assert.ok(['low', 'moderate', 'high', 'critical', 'info', 'unknown'].includes(f.severity), `odd severity ${f.severity}`);
87
92
  assert.ok(f.id && f.name, 'baseline entries carry advisory id + package');
package/tests/go_live.mjs CHANGED
@@ -296,11 +296,17 @@ test("'could not check' is a distinct halt from 'absent'", () => {
296
296
  // `env K=V` list that omits it drops it too. This cost a real failed go-live.
297
297
  // ---------------------------------------------------------------------------
298
298
 
299
- test('a forwarded var is passed BY REFERENCE, never by value', () => {
299
+ test('a forwarded secret NEVER enters argv — it is handed over via --preserve-env', () => {
300
300
  const cmd = G.asOwner({ runAs: 'lars', env: { PGDATABASE: 'cb' } }, 'npm install', { forward: ['NPM_TOKEN'] });
301
- assert.match(cmd, /NPM_TOKEN="\$NPM_TOKEN"/);
302
- // The point of by-reference: the secret's VALUE must never appear in the script text.
303
- assert.ok(!cmd.includes('npm_'), 'a token value must never be interpolated here');
301
+ assert.match(cmd, /--preserve-env=NPM_TOKEN/);
302
+ // THE REGRESSION (task 1003218). `env K="$K"` kept the value out of the script TEXT,
303
+ // but the shell expands it before sudo runs and sudo LOGS THE FULL argv — which put
304
+ // the real token into the droplet's persistent journal 26 times. So the command must
305
+ // not mention the variable as an assignment at all, by reference or otherwise.
306
+ assert.ok(!/NPM_TOKEN=/.test(cmd.replace('--preserve-env=NPM_TOKEN', '')),
307
+ 'a secret must never appear as an argv assignment — sudo logs argv');
308
+ // Non-secret config is still passed plainly; seeing PGDATABASE in a log is useful.
309
+ assert.match(cmd, /PGDATABASE=cb/);
304
310
  });
305
311
 
306
312
  test('forwarding is only wired when the target names an env file to source from', () => {
@@ -310,12 +316,24 @@ test('forwarding is only wired when the target names an env file to source from'
310
316
  assert.deepEqual(G.npmForward({}), []);
311
317
  });
312
318
 
313
- test('an unvetted variable name is never interpolated', () => {
319
+ test('an unvetted variable name never reaches the command', () => {
314
320
  const cmd = G.asOwner({ runAs: 'me' }, 'x', { forward: ['GOOD', 'bad name', '$(id)', '1BAD'] });
315
- assert.match(cmd, /GOOD="\$GOOD"/);
321
+ assert.match(cmd, /--preserve-env=GOOD/);
316
322
  assert.ok(!cmd.includes('bad name') && !cmd.includes('$(id)') && !cmd.includes('1BAD'));
317
323
  });
318
324
 
325
+ test('with no privilege drop a secret needs no mention at all', () => {
326
+ // The child already inherits our environment, so naming it would be pure exposure.
327
+ const cmd = G.asOwner({ env: { A: 'b' } }, 'x', { forward: ['NPM_TOKEN'] });
328
+ assert.equal(cmd, 'env A=b x');
329
+ assert.ok(!cmd.includes('NPM_TOKEN'));
330
+ });
331
+
332
+ test('no forwarding leaves the documented sudo shape untouched', () => {
333
+ assert.equal(G.asOwner({ runAs: 'me', env: { PGDATABASE: 'mydb' } }, 'echo hi'),
334
+ 'sudo -u me -H env PGDATABASE=mydb HOME=/home/me echo hi');
335
+ });
336
+
319
337
  // ---------------------------------------------------------------------------
320
338
  // A failed run must say what the tool said
321
339
  // ---------------------------------------------------------------------------