@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 +12 -12
- package/docs/recipes/core-release-pipeline.md +5 -0
- package/package-lock.json +5 -5
- package/package.json +2 -2
- package/scripts/gds/go-live.js +27 -13
- package/src/module-api.js +5 -1
- package/tests/audit_gate.mjs +6 -1
- package/tests/go_live.mjs +24 -6
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
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-
|
|
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": "
|
|
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": "
|
|
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": "
|
|
4585
|
+
"sha256": "e2b6abac7133ee151991f267ba79cb55ed677547ebc8adaf573288a959aecc01"
|
|
4586
4586
|
},
|
|
4587
4587
|
{
|
|
4588
4588
|
"path": "package.json",
|
|
4589
4589
|
"mode": "0000644",
|
|
4590
|
-
"sha256": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|
|
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.
|
|
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.
|
|
52
|
-
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.
|
|
53
|
-
"integrity": "sha512-
|
|
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.
|
|
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.
|
|
32
|
+
"undici": "^6.28.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/scripts/gds/go-live.js
CHANGED
|
@@ -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
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
//
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
194
|
-
const
|
|
195
|
-
return
|
|
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.
|
|
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');
|
package/tests/audit_gate.mjs
CHANGED
|
@@ -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)
|
|
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
|
|
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,
|
|
302
|
-
//
|
|
303
|
-
|
|
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
|
|
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,
|
|
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
|
// ---------------------------------------------------------------------------
|