@bookedsolid/rea 0.48.1 → 0.49.0

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.
@@ -41,21 +41,44 @@ shim_cli_missing_relevant() {
41
41
  if [ -z "$cli_missing_cmd" ]; then
42
42
  return 1
43
43
  fi
44
+
45
+ # R5-P1: substring scan DETERMINES refusal; allowlist OPENS gates
46
+ # only. R7-P1: PM-route can ALSO return 2 (audit-integrity fail)
47
+ # which MUST refuse via banner regardless of substring scan.
48
+ local matched_blocked=0
44
49
  local policy_file="${REA_ROOT}/.rea/policy.yaml"
45
- if [ ! -f "$policy_file" ]; then
50
+ if [ -f "$policy_file" ]; then
51
+ # shellcheck source=_lib/policy-reader.sh
52
+ source "$(dirname "$0")/_lib/policy-reader.sh"
53
+ local entry
54
+ while IFS= read -r entry; do
55
+ [ -z "$entry" ] && continue
56
+ case "$cli_missing_cmd" in
57
+ *"$entry"*) matched_blocked=1; break ;;
58
+ esac
59
+ done < <(policy_reader_get_list blocked_paths 2>/dev/null)
60
+ fi
61
+
62
+ # shellcheck source=_lib/bootstrap-allowlist.sh
63
+ source "$(dirname "$0")/_lib/bootstrap-allowlist.sh"
64
+
65
+ # R7-P1 (codex round 7): 3-state PM-route return.
66
+ # 0 = auditable allow → exit 0 immediately.
67
+ # 2 = refuse-HARD (audit-integrity fail) → banner regardless
68
+ # of substring scan (the helper printed an explainer to
69
+ # stderr; we must NOT silently allow a payload whose audit
70
+ # record could not be written).
71
+ # * = refuse-fallthrough → defer to substring-scan verdict.
72
+ _bootstrap_shim_pm_route "blocked-paths-bash-gate" "$cli_missing_cmd" "$REA_ROOT"
73
+ case "$?" in
74
+ 0) exit 0 ;;
75
+ 2) return 0 ;;
76
+ esac
77
+
78
+ if [ "$matched_blocked" -eq 0 ]; then
46
79
  return 1
47
80
  fi
48
- # 0.37.0: route blocked_paths reads through the unified policy-reader.
49
- # shellcheck source=_lib/policy-reader.sh
50
- source "$(dirname "$0")/_lib/policy-reader.sh"
51
- local entry
52
- while IFS= read -r entry; do
53
- [ -z "$entry" ] && continue
54
- case "$cli_missing_cmd" in
55
- *"$entry"*) return 0 ;;
56
- esac
57
- done < <(policy_reader_get_list blocked_paths 2>/dev/null)
58
- return 1
81
+ return 0
59
82
  }
60
83
 
61
84
  # shellcheck source=_lib/shim-runtime.sh
@@ -45,21 +45,24 @@ shim_cli_missing_relevant() {
45
45
  if [ -z "$cli_missing_cmd" ]; then
46
46
  return 1
47
47
  fi
48
+
49
+ # R5-P1 (codex round 5): substring scan is DETERMINATIVE for
50
+ # refusal. Allowlist opens an audited-allow gate but never closes
51
+ # one.
52
+ local matched_protected=0
48
53
  case "$cli_missing_cmd" in
49
- *".claude/"*) return 0 ;;
50
- *".husky/"*) return 0 ;;
51
- *".rea/policy.yaml"*) return 0 ;;
52
- *".rea/HALT"*) return 0 ;;
53
- *".rea/last-review"*) return 0 ;;
54
- *".claude\\"*|*".husky\\"*|*".rea\\"*) return 0 ;;
54
+ *".claude/"*) matched_protected=1 ;;
55
+ *".husky/"*) matched_protected=1 ;;
56
+ *".rea/policy.yaml"*) matched_protected=1 ;;
57
+ *".rea/HALT"*) matched_protected=1 ;;
58
+ *".rea/last-review"*) matched_protected=1 ;;
59
+ *".claude\\"*|*".husky\\"*|*".rea\\"*) matched_protected=1 ;;
55
60
  esac
56
61
  # 0.37.0: route protected_writes reads through the unified
57
62
  # policy-reader (Tier 1 CLI → Tier 2 python3 → Tier 3 awk
58
- # block-form). Pre-0.37.0 the inline awk parser missed flow-form
59
- # arrays (`protected_writes: [path/a, path/b]`) on CLI-missing
60
- # installs.
63
+ # block-form).
61
64
  local policy_file="${REA_ROOT}/.rea/policy.yaml"
62
- if [ -f "$policy_file" ]; then
65
+ if [ "$matched_protected" -eq 0 ] && [ -f "$policy_file" ]; then
63
66
  # shellcheck source=_lib/policy-reader.sh
64
67
  source "$(dirname "$0")/_lib/policy-reader.sh"
65
68
  local entry base
@@ -71,11 +74,26 @@ shim_cli_missing_relevant() {
71
74
  esac
72
75
  [ -z "$base" ] && continue
73
76
  case "$cli_missing_cmd" in
74
- *"$base"*) return 0 ;;
77
+ *"$base"*) matched_protected=1; break ;;
75
78
  esac
76
79
  done < <(policy_reader_get_list protected_writes 2>/dev/null)
77
80
  fi
78
- return 1
81
+
82
+ # shellcheck source=_lib/bootstrap-allowlist.sh
83
+ source "$(dirname "$0")/_lib/bootstrap-allowlist.sh"
84
+
85
+ # R7-P1 (codex round 7): 3-state PM-route return. See the
86
+ # blocked-paths shim for the contract.
87
+ _bootstrap_shim_pm_route "protected-paths-bash-gate" "$cli_missing_cmd" "$REA_ROOT"
88
+ case "$?" in
89
+ 0) exit 0 ;;
90
+ 2) return 0 ;;
91
+ esac
92
+
93
+ if [ "$matched_protected" -eq 0 ]; then
94
+ return 1
95
+ fi
96
+ return 0
79
97
  }
80
98
 
81
99
  # shellcheck source=_lib/shim-runtime.sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bookedsolid/rea",
3
- "version": "0.48.1",
3
+ "version": "0.49.0",
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)",
@@ -75,6 +75,7 @@
75
75
  "mvdan-sh": "0.10.1",
76
76
  "proper-lockfile": "^4.1.2",
77
77
  "safe-regex": "^2.1.1",
78
+ "semver": "^7.7.4",
78
79
  "yaml": "^2.7.0",
79
80
  "zod": "^3.23.0"
80
81
  },
@@ -83,6 +84,7 @@
83
84
  "@types/node": "^25.5.2",
84
85
  "@types/proper-lockfile": "^4.1.4",
85
86
  "@types/safe-regex": "^1.1.6",
87
+ "@types/semver": "^7.7.1",
86
88
  "@typescript-eslint/eslint-plugin": "^8.0.0",
87
89
  "@typescript-eslint/parser": "^8.0.0",
88
90
  "@vitest/coverage-v8": "^3.2.4",
@@ -31,6 +31,10 @@ blocked_paths:
31
31
  - .github/workflows/release.yml
32
32
  - SECURITY.md
33
33
  - THREAT_MODEL.md
34
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
35
+ # See `bst-internal.yaml` for the rationale and explicit
36
+ # out-of-scope statement on PM-induced writes.
37
+ - package.json
34
38
  notification_channel: ''
35
39
  # G9: Booked-internal consumers retain the stricter 0.2.x posture — a single
36
40
  # literal injection match at write/destructive tier denies (does not merely
@@ -14,6 +14,21 @@ blocked_paths:
14
14
  - .github/workflows/release.yml
15
15
  - SECURITY.md
16
16
  - THREAT_MODEL.md
17
+ # 0.49.0 — gates Write/Edit/MultiEdit/NotebookEdit tool calls
18
+ # targeting `package.json`. The bootstrap allowlist passes a
19
+ # CLI-missing PM payload through ONLY when `package.json` already
20
+ # declares `@bookedsolid/rea`, so blocking AGENT edits to the
21
+ # manifest narrows the Edit/Write-tier forge surface (an attacker
22
+ # can no longer add the declaration via Edit then invoke the
23
+ # allowed PM command). PM-induced writes to the manifest are
24
+ # explicitly out of scope (see THREAT_MODEL.md §5.23 "Out of
25
+ # scope"): a path-based blocklist cannot distinguish agent edits
26
+ # from PM-tool writes. The static manifest-write detector that
27
+ # would close that gap was deliberately removed in R17;
28
+ # defending against agent-initiated PM mutations needs a
29
+ # different abstraction (process-tree-aware policy / per-tool
30
+ # capability tokens) — not shipped in 0.49.0.
31
+ - package.json
17
32
  notification_channel: ''
18
33
  # G9: Booked-internal consumers retain the stricter 0.2.x posture — a single
19
34
  # literal injection match at write/destructive tier denies (does not merely
@@ -80,3 +95,16 @@ attribution:
80
95
  delegation_advisory:
81
96
  enabled: true
82
97
  threshold: 25
98
+ # 0.49.0 bootstrap allowlist (P3-1) — explicit declaration for parity
99
+ # with the dogfood `.rea/policy.yaml`. The other five shipped profiles
100
+ # (open-source*, client-engagement, lit-wc, minimal) inherit the zod
101
+ # schema default (`enabled: true`), so they need no entry here. The
102
+ # bst-internal profile pins it so the operator-visible policy file
103
+ # clearly states the position rather than relying on schema defaults.
104
+ # The allowlist passes a single-segment pnpm install / npm ci / yarn /
105
+ # corepack invocation through when (a) the rea CLI is unreachable, (b)
106
+ # the consumer's package.json declares `@bookedsolid/rea`, and (c)
107
+ # every other precondition (single-segment payload, exact argv shape,
108
+ # audit emission OK) holds. See hooks/_lib/bootstrap-allowlist.sh.
109
+ bootstrap_allowlist:
110
+ enabled: true
@@ -15,6 +15,15 @@ blocked_paths:
15
15
  - THREAT_MODEL.md
16
16
  - secrets/
17
17
  - credentials/
18
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
19
+ # Pairs with the always-on `bootstrap_allowlist` (schema default):
20
+ # the allowlist trusts a CLI-missing PM command only when
21
+ # `package.json` declares `@bookedsolid/rea`, so blocking AGENT
22
+ # edits to the manifest narrows the Edit/Write-tier forge surface.
23
+ # PM-induced writes are explicitly out of scope (see
24
+ # THREAT_MODEL.md §5.23 "Out of scope") — a path-based blocklist
25
+ # cannot distinguish agent edits from PM-tool writes.
26
+ - package.json
18
27
  notification_channel: ''
19
28
  context_protection:
20
29
  delegate_to_subagent:
@@ -14,6 +14,12 @@ blocked_paths:
14
14
  - .github/workflows/release.yml
15
15
  - .github/workflows/publish.yml
16
16
  - tokens/
17
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
18
+ # Pairs with the always-on `bootstrap_allowlist` so the
19
+ # "consumer declares @bookedsolid/rea" precondition is meaningful.
20
+ # PM-induced writes are explicitly out of scope (THREAT_MODEL.md
21
+ # §5.23 "Out of scope").
22
+ - package.json
17
23
  notification_channel: ''
18
24
  # 0.30.0 attribution augmenter — opt-in.
19
25
  # Husky prepare-commit-msg hook appends a Co-Authored-By trailer to
@@ -8,6 +8,17 @@ block_ai_attribution: true
8
8
  blocked_paths:
9
9
  - .env
10
10
  - .env.*
11
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
12
+ # The always-on `bootstrap_allowlist` (schema default) trusts a
13
+ # CLI-missing PM payload only when `package.json` declares
14
+ # `@bookedsolid/rea`; without an Edit/Write gate on the manifest,
15
+ # an agent could ADD that declaration first and then route an
16
+ # otherwise-disallowed PM command through the allowlist. PM-
17
+ # induced writes are explicitly out of scope (THREAT_MODEL.md
18
+ # §5.23 "Out of scope"). Operators who deliberately want agent
19
+ # freedom to edit `package.json` should remove this entry AND
20
+ # set `bootstrap_allowlist.enabled: false` in tandem.
21
+ - package.json
11
22
  notification_channel: ''
12
23
  # 0.30.0 attribution augmenter — opt-in.
13
24
  # When enabled: true, the husky prepare-commit-msg hook appends a
@@ -30,6 +30,10 @@ blocked_paths:
30
30
  - SECURITY.md
31
31
  - .github/workflows/release.yml
32
32
  - .github/workflows/publish.yml
33
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
34
+ # See `open-source.yaml` for the rationale; this variant inherits
35
+ # the same posture sans Codex review.
36
+ - package.json
33
37
  notification_channel: ''
34
38
  # 0.30.0 attribution augmenter — opt-in.
35
39
  # Husky prepare-commit-msg hook appends a Co-Authored-By trailer to
@@ -15,6 +15,17 @@ blocked_paths:
15
15
  - SECURITY.md
16
16
  - .github/workflows/release.yml
17
17
  - .github/workflows/publish.yml
18
+ # 0.49.0 — gates Write/Edit tool calls targeting `package.json`.
19
+ # The always-on `bootstrap_allowlist` (schema default) trusts a
20
+ # CLI-missing PM payload only when `package.json` declares
21
+ # `@bookedsolid/rea`. Without an Edit/Write-tier gate on the
22
+ # manifest, an agent could ADD that declaration first and then
23
+ # invoke the allowed PM command, defeating the precondition.
24
+ # PM-induced writes are explicitly out of scope (THREAT_MODEL.md
25
+ # §5.23 "Out of scope"). Operators who need agent freedom to edit
26
+ # package.json should remove this entry in `.rea/policy.yaml`
27
+ # AND set `bootstrap_allowlist.enabled: false` in tandem.
28
+ - package.json
18
29
  notification_channel: ''
19
30
  # 0.30.0 attribution augmenter — opt-in.
20
31
  # Husky prepare-commit-msg hook appends a Co-Authored-By trailer to