File size: 12,265 Bytes
1d3f990
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# AUDITOR-LOGIC FINAL AUDIT REPORT
## ROWM Polymorphic Notebook β€” Logic Engine Security & Correctness Review

**Date:** 2026-07-27  
**Scope:** `/c/Users/jessi/Desktop/rowm-polymorphic-notebook/logic/` directory  
**Files Audited:** 10 Prolog modules (5 fact, 5 rule), test suite  
**Total Lines:** ~900 lines of logic code  

---

## EXECUTIVE SUMMARY

### Verdict: **NOT RELEASE READY**

The Prolog/Datalog authorization and release-readiness logic contains **2 CRITICAL security/correctness bugs** and **7 HIGH/MEDIUM issues** that create exploitable authorization bypasses and release-gate vulnerabilities.

### Counts by Severity
- **CRITICAL:** 2 findings (authorization bypass, expired capability enforcement)
- **HIGH:** 3 findings (tier_2 bypass, hardcoded proofs, unverified metadata)

- **MEDIUM:** 4 findings (recursion, caching, unreachable code, semantics)

- **LOW:** 1 finding (code quality)



### Key Risks

1. **Authorization gate is bypassable** β€” dispatch_permitted/5 can be circumvented by direct predicate queries
2. **Expired capabilities remain active** β€” get_time/1 hardcoded, boundary condition wrong

3. **Tier_2 agents can execute** β€” capability_satisfies/4 has no trust check
4. **Release gate incomplete** β€” proof list hardcoded, won't detect new obligations
5. **Notebook metadata unverified** β€” cell status not cryptographically linked to ledger

---

## CRITICAL FINDINGS (2)

### CRITICAL-1: Authorization Gate Bypassable via Direct Predicate Queries

**File:** `logic/rules/authorization.pl` (lines 44-51)

**The Issue:**
External code can bypass dispatch_permitted/5 by querying authorization predicates directly:



```prolog

?- capability_active('capa_001_loc_rust_exec', Result).  % Bypasses all checks
?- agent_authorized(loc, 'capa_001_loc_rust_exec', rust).  % Partial checks only

```



Dispatch_permitted enforces these checks:
- Agent trust level (tier_2 agents should be blocked)

- Runtime active status

- Permission scope



**Attack:** Query capability_active directly to bypass agent_authorized tier_2 check.

**Impact:** Any agent can execute by circumventing dispatch_permitted/5.



---



### CRITICAL-2: Expired Capabilities Remain Active (Timestamp Bug)



**File:** `logic/rules/authorization.pl` (lines 17-22, 55)



**The Issue:**



```prolog

get_time(1719432000).  % HARDCODED β€” not dynamic

capability_active(CapID, true) :-

    capability_issued(CapID, _, _, _, _, IssuedAt, ExpiresAt),
    \+ capability_revoked(CapID, _),

    get_time(Now),

    Timestamp is floor(Now),

    Timestamp =< ExpiresAt.  % Inclusive (WRONG)

```


Example from capabilities.pl:
```prolog

capability_issued('capa_006_phantom_rust_log', ..., 1719432000, 1719432000).

% IssuedAt = 1719432000, ExpiresAt = 1719432000 (same time)



capability_valid('capa_006_phantom_rust_log', false).  % Declared expired

```

**The Bug:**
- Comparison: 1719432000 =< 1719432000 β†’ TRUE
- Capability flagged active despite ExpiresAt being NOW
- Contradictory facts: capability_active returns true, capability_valid asserts false

**Root Causes:**
1. get_time/1 hardcoded, not dynamic

2. Boundary uses =< instead of <

3. Contradictory facts create logic inconsistency



**Impact:** Expired capabilities cannot be enforced. System allows active use of expired credentials.



---



## HIGH SEVERITY FINDINGS (3)



### HIGH-1: Tier_2 Agents Can Execute via capability_satisfies Query



**File:** `logic/rules/authorization.pl` (lines 35-42)



**Issue:** capability_satisfies/4 has no agent trust level check. Tier_2 agents are blocked only in agent_authorized/3.

```prolog

% Tier_2 check in agent_authorized:

agent_authorized(AgentID, CapID, TargetRuntime) :-

    ...

    TrustLevel \= tier_2,  % ← ONLY HERE

    ...



% But capability_satisfies has NO agent check:

capability_satisfies(CapID, Permission, TargetRuntime, true) :-

    capability_issued(CapID, _, _, TargetRuntime, Permissions, _, _),

    member(Permission, Permissions),

    capability_active(CapID, true).  % No trust check

```

**Attack:**
```prolog

?- capability_satisfies('capa_006_phantom_rust_log', log, rust, Result).

% Returns true β€” phantom is tier_2 but no check performed

```

**Impact:** Observer agents can gain execution permissions.

---

### HIGH-2: release_ready Gate Has Hardcoded Proof List



**File:** `logic/rules/release.pl` (lines 45-49)



**Issue:** Proof verification is hardcoded to 4 specific proof IDs. No dynamic discovery of new proofs.



```prolog

all_proofs_satisfied(true) :-

    proof_satisfied('proof_borrow_step_sound', true),

    proof_satisfied('proof_cons_cell_model', true),

    proof_satisfied('proof_dispatch_safe', true),
    proof_satisfied('proof_receipt_chain_integrity', true).

```


If developer adds new proof obligation, release_ready won't check it.



**Scenario:**

```prolog

% New proof added:

proof_obligation('proof_new_invariant', 'Some theorem', 'lean4').

% But all_proofs_satisfied still checks only 4 proofs
?- release_ready(Result).

% Result = true  ← NEW PROOF NOT CHECKED

```



**Impact:** Release gate can be bypassed by new proof obligations.



---



### HIGH-3: Notebook Metadata Overrides Logic Without Ledger Verification



**File:** `logic/rules/release.pl` (lines 73-80)



**Issue:** Cell completion status checked against notebook_cells.pl facts (parsed from JSON) without cryptographic verification against receipt chain.

```prolog

all_cells_complete(true) :-

    \+ (cell_exists(CellID, _Type, _Kernel, _Visibility, _SourceHash, _OutputHash),

        \+ (cell_sealed(CellID, true) ; cell_metadata(CellID, _Class, _DepCount, passed))).

```

**Attack:**
```prolog

% Original:

cell_metadata('rust-bridge-test', test, 1, failed).



% Attacker modifies to:

cell_metadata('rust-bridge-test', test, 1, passed).



% Logic engine has no way to know:

?- release_ready(Result).

% Result = true  ← BYPASSES ACTUAL EXECUTION

```

**Root Cause:** Notebook metadata facts are not linked to immutable receipts.

**Impact:** Attacker can mark failing cells as passed, bypassing proof requirements.

---

## MEDIUM SEVERITY FINDINGS (4)

### MEDIUM-1: state_reachable Recursion Has No Cycle Detection



**File:** `logic/rules/transitions.pl` (lines 45-54)



**Issue:** Recursive rule lacks tabling, cycle detection, or depth limits.



```prolog

state_reachable(Stage, FromState, true) :-
    Stage \= receive,

    stage_prerequisite(Stage, PrevStage),

    state_reachable(PrevStage, FromState, true).  % RECURSION


state_reachable(_, _, false).

```



Current fact set is acyclic, but logic has no guards.



**Scenario:**

```prolog

% Buggy fact:

stage_prerequisite(dispatch, receive).  % Creates cycle

% Infinite loop:
?- state_reachable(dispatch, initial, Result).

% Hangs indefinitely

```



**Impact:** Logic engine can hang on state queries.



---



### MEDIUM-2: Revocation May Not Propagate (Caching)



**File:** `logic/rules/authorization.pl` (line 19)



**Issue:** Uses negation-as-failure which may be cached by Prolog. External code may cache capability_active results.

```prolog

capability_active(CapID, true) :-

    capability_issued(CapID, _, _, _, _, IssuedAt, ExpiresAt),

    \+ capability_revoked(CapID, _),  % Negation-as-failure

    ...

```

**Scenario:**
```prolog

% Cached at T1:

capability_active('cap_X', Result).  % Result = true



% Revocation at T2:

assert(capability_revoked('cap_X', 'reason')).



% Cached result still used at T3:

% Result = true  ← Revocation not reflected

```

**Impact:** Revoked capabilities can remain active in cached state.

---

### MEDIUM-3: receipt_sequence_valid Has Unreachable Dead Code

**File:** `logic/rules/provenance.pl` (lines 33-42)

**Issue:** First clause calls undefined predicate previous_receipt_id/1 (should be previous_receipt/2).



```prolog

receipt_sequence_valid(ReceiptID) :-

    ...

    (previous_receipt(ReceiptID, _) -> 

     receipt_sequence_valid(previous_receipt_id(ReceiptID)) ; true).

    % ↑ undefined predicate β€” always fails



% Falls back to simplified version:

receipt_sequence_valid(ReceiptID) :-

    receipt_issued(ReceiptID, _SeqNum, Agent, CapID, ...),

    agent_active(Agent, true),
    capability_valid(CapID, true).

```


**Impact:** Sequence validation logic is incomplete. Monotonic sequence number verification is skipped.

---

### MEDIUM-4: no_revoked_capabilities Semantics Undocumented

**File:** `logic/rules/release.pl` (lines 66-71)

**Issue:** Double negation-as-failure semantics are unclear and potentially misunderstood.

```prolog

no_revoked_capabilities(true) :-

    \+ (agent_active(Agent, true),

        capability_issued(_CapID, _Issuer, Agent, _Runtime, _Perms, _Issued, _Expires),

        capability_revoked(_CapID, _)).

```

**Semantic Gap:**
- **Checks:** "There does NOT exist an active agent with a revoked capability"
- **Name suggests:** "All capabilities are not revoked"
- These are different: inactive agents' revocations are ignored

**Impact:** Confusing semantics may lead to misuse.

---

## LOW SEVERITY FINDINGS (1)

### LOW-1: dispatch_permitted Overly Permissive Catch-All



**File:** `logic/rules/authorization.pl` (line 51)



**Issue:** Catch-all clause with all unbound variables creates infinite choice points.



```prolog

dispatch_permitted(_, _, _, _, false).  % All variables unbound
```



On backtracking with unbound variables, generates infinite choice points. Minor performance issue, not a correctness bug.



---



## RELEASE-READINESS BLOCKERS



### Blockers (Must Fix Before Release)



**CRITICAL Fixes:**

1. Enforce dispatch_permitted/5 as sole authorization entry point

2. Fix get_time/1 to be dynamic and use exclusive boundary (<)

3. Fix capability_active contradiction with capability_valid



**HIGH Priority Fixes:**

4. Replace hardcoded proof list with dynamic discovery (findall)

5. Add cryptographic verification of notebook metadata against receipts

6. Add tier_2 check to capability_satisfies



**MEDIUM Priority Fixes:**

7. Add cycle detection to state_reachable (tabling or explicit detection)

8. Fix dead code in receipt_sequence_valid

9. Document closed-world assumptions for negation-as-failure

10. Implement revocation propagation or cache invalidation



---



## SYSTEM ARCHITECTURE ISSUES



### Issue 1: No Forced Authorization Routing

Individual authorization predicates are exported as library functions with no enforcement that callers use dispatch_permitted/5.



**Fix:** Seal authorization as a module, export only dispatch_permitted and release_ready.



### Issue 2: Notebook Metadata Not Cryptographically Linked

Cell completion status (notebook_cells.pl) is separate from receipt chain (receipts.pl).



**Fix:** Add predicate to verify cell_metadata against receipt chain hashes.



### Issue 3: Hardcoded Lists Instead of Dynamic Discovery

Proof list, agent list, runtime list should be dynamically discovered.



**Fix:** Use findall to query all proof_obligation facts instead of hardcoding.



---



## TEST GAPS



**Missing Tests:**

- Direct capability_active query bypassing dispatch_permitted

- Tier_2 agent execution via capability_satisfies

- Revocation propagation

- Dynamic proof discovery

- Cycle detection in state transitions



**Current Test Issues:**

- test_queries.pl expects capability_active('capa_006_phantom_rust_log', false)

- But logic returns true due to timestamp bug

- **Test would pass incorrectly** without fixing the bug



---



## AUDIT SUMMARY



**Audit Tool:** AUDITOR-LOGIC  

**Date:** 2026-07-27  

**Modules Audited:** 10 (5 fact, 5 rule)  

**Lines Audited:** ~900  

**Issues Found:** 9 (2 CRITICAL, 3 HIGH, 4 MEDIUM, 1 LOW)  

**Release Verdict:** NOT READY



**Files Audited:**

- logic/facts/agents.pl

- logic/facts/capabilities.pl

- logic/facts/notebook_cells.pl

- logic/facts/receipts.pl

- logic/facts/runtimes.pl

- logic/rules/authorization.pl

- logic/rules/proofs.pl

- logic/rules/provenance.pl

- logic/rules/release.pl

- logic/rules/transitions.pl

- logic/queries/test_queries.pl