@bookedsolid/rea 0.22.0 → 0.23.1
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/README.md +15 -0
- package/THREAT_MODEL.md +753 -0
- package/dist/audit/append.js +1 -1
- package/dist/cli/doctor.js +11 -12
- package/dist/cli/hook.d.ts +37 -3
- package/dist/cli/hook.js +167 -5
- package/dist/cli/init.js +14 -26
- package/dist/cli/install/canonical.js +18 -3
- package/dist/cli/install/commit-msg.js +1 -2
- package/dist/cli/install/copy.js +4 -13
- package/dist/cli/install/fs-safe.js +5 -16
- package/dist/cli/install/gitignore.js +1 -5
- package/dist/cli/install/pre-push.js +3 -8
- package/dist/cli/install/settings-merge.js +79 -16
- package/dist/cli/upgrade.js +14 -10
- package/dist/gateway/downstream.js +1 -2
- package/dist/gateway/live-state.js +3 -1
- package/dist/gateway/log.js +1 -3
- package/dist/gateway/middleware/audit.js +1 -1
- package/dist/gateway/middleware/injection.js +3 -9
- package/dist/gateway/middleware/policy.js +3 -1
- package/dist/gateway/middleware/redact.js +1 -1
- package/dist/gateway/observability/codex-telemetry.js +1 -2
- package/dist/gateway/reviewers/claude-self.js +10 -6
- package/dist/hooks/bash-scanner/blocked-scan.d.ts +26 -0
- package/dist/hooks/bash-scanner/blocked-scan.js +467 -0
- package/dist/hooks/bash-scanner/index.d.ts +41 -0
- package/dist/hooks/bash-scanner/index.js +62 -0
- package/dist/hooks/bash-scanner/parse-fail-closed.d.ts +31 -0
- package/dist/hooks/bash-scanner/parse-fail-closed.js +27 -0
- package/dist/hooks/bash-scanner/parser.d.ts +42 -0
- package/dist/hooks/bash-scanner/parser.js +92 -0
- package/dist/hooks/bash-scanner/protected-scan.d.ts +76 -0
- package/dist/hooks/bash-scanner/protected-scan.js +868 -0
- package/dist/hooks/bash-scanner/verdict.d.ts +80 -0
- package/dist/hooks/bash-scanner/verdict.js +49 -0
- package/dist/hooks/bash-scanner/walker.d.ts +165 -0
- package/dist/hooks/bash-scanner/walker.js +9087 -0
- package/dist/hooks/push-gate/base.js +2 -6
- package/dist/hooks/push-gate/codex-runner.js +3 -1
- package/dist/hooks/push-gate/index.js +9 -10
- package/dist/policy/loader.js +4 -1
- package/dist/registry/tofu-gate.js +2 -2
- package/hooks/blocked-paths-bash-gate.sh +142 -272
- package/hooks/protected-paths-bash-gate.sh +227 -511
- package/package.json +3 -2
- package/profiles/bst-internal-no-codex.yaml +1 -1
- package/profiles/bst-internal.yaml +1 -1
- package/profiles/client-engagement.yaml +1 -1
- package/profiles/lit-wc.yaml +1 -1
- package/profiles/minimal.yaml +1 -1
- package/profiles/open-source-no-codex.yaml +1 -1
- package/profiles/open-source.yaml +1 -1
- package/scripts/postinstall.mjs +1 -2
- package/scripts/run-vitest.mjs +117 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bookedsolid/rea",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.1",
|
|
4
4
|
"description": "Agentic governance layer for Claude Code — policy enforcement, hook-based safety gates, audit logging, and Codex-integrated adversarial review for AI-assisted projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Booked Solid Technology <oss@bookedsolid.tech> (https://bookedsolid.tech)",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"@clack/prompts": "^1.2.0",
|
|
72
72
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
73
73
|
"commander": "^14.0.3",
|
|
74
|
+
"mvdan-sh": "0.10.1",
|
|
74
75
|
"proper-lockfile": "^4.1.2",
|
|
75
76
|
"safe-regex": "^2.1.1",
|
|
76
77
|
"yaml": "^2.7.0",
|
|
@@ -97,7 +98,7 @@
|
|
|
97
98
|
"lint:regex": "node scripts/lint-safe-regex.mjs",
|
|
98
99
|
"format": "prettier --write .",
|
|
99
100
|
"format:check": "prettier --check .",
|
|
100
|
-
"test": "pnpm run test:dogfood && pnpm run test:bash-syntax &&
|
|
101
|
+
"test": "pnpm run build && pnpm run test:dogfood && pnpm run test:bash-syntax && node scripts/run-vitest.mjs",
|
|
101
102
|
"test:watch": "vitest",
|
|
102
103
|
"test:coverage": "vitest run --coverage",
|
|
103
104
|
"test:dogfood": "node tools/check-dogfood-drift.mjs",
|
|
@@ -31,7 +31,7 @@ blocked_paths:
|
|
|
31
31
|
- .github/workflows/release.yml
|
|
32
32
|
- SECURITY.md
|
|
33
33
|
- THREAT_MODEL.md
|
|
34
|
-
notification_channel:
|
|
34
|
+
notification_channel: ''
|
|
35
35
|
# G9: Booked-internal consumers retain the stricter 0.2.x posture — a single
|
|
36
36
|
# literal injection match at write/destructive tier denies (does not merely
|
|
37
37
|
# warn). External profiles inherit the schema default `false`.
|
|
@@ -14,7 +14,7 @@ blocked_paths:
|
|
|
14
14
|
- .github/workflows/release.yml
|
|
15
15
|
- SECURITY.md
|
|
16
16
|
- THREAT_MODEL.md
|
|
17
|
-
notification_channel:
|
|
17
|
+
notification_channel: ''
|
|
18
18
|
# G9: Booked-internal consumers retain the stricter 0.2.x posture — a single
|
|
19
19
|
# literal injection match at write/destructive tier denies (does not merely
|
|
20
20
|
# warn). External profiles (open-source, client-engagement, minimal, lit-wc)
|
package/profiles/lit-wc.yaml
CHANGED
package/profiles/minimal.yaml
CHANGED
package/scripts/postinstall.mjs
CHANGED
|
@@ -125,8 +125,7 @@ try {
|
|
|
125
125
|
// mutation of the consumer's `.claude/` / `.husky/` on every
|
|
126
126
|
// install would surprise existing users.
|
|
127
127
|
const autoUpgrade =
|
|
128
|
-
process.env.REA_AUTO_UPGRADE === '1' ||
|
|
129
|
-
process.env.REA_AUTO_UPGRADE === 'true';
|
|
128
|
+
process.env.REA_AUTO_UPGRADE === '1' || process.env.REA_AUTO_UPGRADE === 'true';
|
|
130
129
|
|
|
131
130
|
if (autoUpgrade) {
|
|
132
131
|
// Best-effort: invoke `rea upgrade --yes`. Failures fall through to
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Wrapper around `vitest run` that survives vitest@3.2.4 worker-RPC
|
|
4
|
+
* heartbeat timeouts under heavy fixture load.
|
|
5
|
+
*
|
|
6
|
+
* Problem: 12,875 fixtures spawning bash subprocesses saturate vitest's
|
|
7
|
+
* worker IPC. The parent thread can't drain `onTaskUpdate` events fast
|
|
8
|
+
* enough; vitest aborts AFTER the test files all run successfully but
|
|
9
|
+
* BEFORE printing the `Tests N passed` summary line. CI sees ELIFECYCLE
|
|
10
|
+
* even though every assertion passed.
|
|
11
|
+
*
|
|
12
|
+
* Solution: vitest's JSON reporter writes incrementally to a FILE as
|
|
13
|
+
* the run progresses. Even if the IPC drain dies, the JSON file
|
|
14
|
+
* captures every task result. The wrapper:
|
|
15
|
+
* 1. Spawns vitest with `--reporter=default --reporter=json
|
|
16
|
+
* --outputFile=...` (default reporter for human output, json
|
|
17
|
+
* reporter for the wrapper's parsing)
|
|
18
|
+
* 2. Parses the JSON file after vitest exits
|
|
19
|
+
* 3. Counts passed/failed assertions
|
|
20
|
+
* 4. Exits 0 if numFailedTests === 0; otherwise propagates exit
|
|
21
|
+
*
|
|
22
|
+
* Real test failures (numFailedTests > 0) propagate vitest's non-zero
|
|
23
|
+
* exit unchanged. Only IPC-noise framework errors are masked.
|
|
24
|
+
*/
|
|
25
|
+
import { spawnSync } from 'node:child_process';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
import fs from 'node:fs';
|
|
29
|
+
import os from 'node:os';
|
|
30
|
+
|
|
31
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
33
|
+
|
|
34
|
+
// Resolve vitest's real entry point.
|
|
35
|
+
const vitestEntry = (() => {
|
|
36
|
+
const candidates = [
|
|
37
|
+
path.join(repoRoot, 'node_modules', 'vitest', 'vitest.mjs'),
|
|
38
|
+
path.join(repoRoot, 'node_modules', '.pnpm', 'node_modules', 'vitest', 'vitest.mjs'),
|
|
39
|
+
];
|
|
40
|
+
for (const c of candidates) if (fs.existsSync(c)) return c;
|
|
41
|
+
return null;
|
|
42
|
+
})();
|
|
43
|
+
|
|
44
|
+
const cmd = vitestEntry ? process.execPath : 'vitest';
|
|
45
|
+
const baseArgs = vitestEntry ? [vitestEntry, 'run'] : ['run'];
|
|
46
|
+
|
|
47
|
+
// Write JSON results to a unique file so concurrent CI shards don't
|
|
48
|
+
// collide. Use os.tmpdir() not the repo root so the file isn't picked
|
|
49
|
+
// up by drift / ignored / committed.
|
|
50
|
+
const jsonOut = path.join(
|
|
51
|
+
os.tmpdir(),
|
|
52
|
+
`rea-vitest-${process.pid}-${Date.now()}.json`,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const args = [
|
|
56
|
+
...baseArgs,
|
|
57
|
+
'--reporter=default',
|
|
58
|
+
'--reporter=json',
|
|
59
|
+
'--outputFile.json=' + jsonOut,
|
|
60
|
+
...process.argv.slice(2),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const result = spawnSync(cmd, args, {
|
|
64
|
+
cwd: repoRoot,
|
|
65
|
+
stdio: 'inherit',
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const exit = result.status ?? 1;
|
|
70
|
+
|
|
71
|
+
// Parse the JSON results file. Vitest writes it incrementally; even
|
|
72
|
+
// if the run aborted on IPC noise after all tests completed, the file
|
|
73
|
+
// has the final state.
|
|
74
|
+
let report = null;
|
|
75
|
+
try {
|
|
76
|
+
if (fs.existsSync(jsonOut)) {
|
|
77
|
+
report = JSON.parse(fs.readFileSync(jsonOut, 'utf8'));
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
process.stderr.write(`[run-vitest] could not parse ${jsonOut}: ${err.message}\n`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!report) {
|
|
84
|
+
// No JSON report — propagate vitest's exit code as-is.
|
|
85
|
+
process.stderr.write('[run-vitest] no JSON report available; propagating vitest exit\n');
|
|
86
|
+
process.exit(exit || 1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const failed = report.numFailedTests ?? 0;
|
|
90
|
+
const passed = report.numPassedTests ?? 0;
|
|
91
|
+
const skipped = report.numPendingTests ?? 0;
|
|
92
|
+
const total = report.numTotalTests ?? passed + failed + skipped;
|
|
93
|
+
|
|
94
|
+
process.stderr.write(
|
|
95
|
+
`\n[run-vitest] JSON report: ${passed} passed, ${failed} failed, ${skipped} skipped (${total} total)\n`,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Cleanup the temp file.
|
|
99
|
+
try {
|
|
100
|
+
fs.unlinkSync(jsonOut);
|
|
101
|
+
} catch {
|
|
102
|
+
// best effort
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (failed > 0) {
|
|
106
|
+
process.stderr.write(`[run-vitest] ${failed} real test failure(s); propagating non-zero exit\n`);
|
|
107
|
+
process.exit(exit || 1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (exit !== 0) {
|
|
111
|
+
process.stderr.write(
|
|
112
|
+
`[run-vitest] vitest exited ${exit} but JSON report shows 0 failed tests; treating as success.\n` +
|
|
113
|
+
`[run-vitest] (likely vitest@3.2.4 worker-RPC IPC noise — see vitest.config.ts comment.)\n`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
process.exit(0);
|