5.5 KiB
5.5 KiB
Tier 3 Steal Arc — Adversarial Verification Verdict (2026-07-15)
Branch feat/steals-tier3 (commits 2d4b0ca2..f322cc2c + fix commit below).
Verifier fleet: 6 agents (workflow wf_ac6e9d94-dd9); 4 completed, 2 (verify:17, verify:cross)
died on API safeguard false-positives — those two audits were re-executed inline by the
orchestrator (findings below marked [inline]).
Verdicts
| Item | Fleet verdict | After fixes |
|---|---|---|
| S1 sanitizer | APPROVE (0 C/H) | APPROVE |
| #13 automation gate | BLOCK — 1 HIGH | fixed → APPROVE |
| #15 skill badges | APPROVE (0 C/H) | APPROVE |
| #12 compaction persist | BLOCK — 1 HIGH + 1 MEDIUM | fixed → APPROVE |
| #17 ai_task | [inline] 2 findings (1 HIGH-class SEC, 1 MEDIUM) | fixed → APPROVE |
| cross-cutting | [inline] no blocking findings | APPROVE |
Fixed in the post-verdict commit
- #13 HIGH — error-path transcript dump (chat.ts:2160). The #3 launch-blocker raw-turn
persistence in the outer catch was not gated: an automated review turn failing on
context_length (likeliest failure for transcript-embedding turns) persisted the ENTIRE
review instruction + session transcript as a
user_statedframe, then memory-lane cron would LLM-amplify it. Fix:&& !isAutomatedTurn. - #12 HIGH — cross-mind destructive overwrite (orchestrator.ts).
compactionFrameIdsis keyed by sessionId while persist routes by active mind; after a workspace switch the same rowid can be an unrelated user frame in the new mind, andframes.update()overwrote it. Verifier demonstrated with a repro test. Fix: update-in-place only when the existing frame's content starts with this session's[Session summary — <key>]marker; else create fresh. Regression test added (foreign frame with colliding id survives untouched). - #12 MEDIUM — sign-gate over-trigger.
isSelfIncapacityAssertionover the whole multi-section summary downgraded the entire session gist totemporary(recall-invisible) whenever one boilerplate "you'll need to run X" line appeared — silently no-op'ing the feature for long sessions. Fix: no sign-gate on compaction summaries (importance alwaysnormal, provenancesource='system'); D1 ruling amended. - #12 LOW — seam consistency. Persist now also gated
!hasCustomRunnerlike every sibling write-back seam. - [inline] #17 SEC — deliverTo smuggling via job_data.
create_schedule's free-formjob_dataJSON could setmode:'ai_task'+deliverTo:{platform,chatId}(with or without thepromptparam), routing scheduled agent output to an arbitrary, unpaired chat — bypassing the trusted-origin-snapshot design. Fix:mode/deliverTo/onceare always stripped from parsed job_data (settable only via the typed param path). Test added. - [inline] #17 MEDIUM — firesTooOften bypass. Range (
1-59 * * * *) and step-on-range (0-59/2) minute fields passed the guard. Fix: allowlist (fixed minute |*/NN≥5 | ≤12-item fixed list); everything else rejected. Bypass exprs added to the test matrix. (Damage was already bounded by the daily cap — verified ALIVE, not dead code:executeJob/tick→onJobComplete→makeRecordExecutionCallback→cronStore.recordExecutionfires for every run mode-agnostically, filling exactly the tablecountExecutionsTodayreads; the cron-ai-task test seeds that table and observes the skip.)
Residual findings — documented, NO action (LOW/NIT)
- S1 MEDIUM (pre-existing, out of scope): 4th legacy sanitizer copy in
packages/server/src/local/routes/memory.ts:102(scope=globalUI search) still zeroes Cyrillic/diacritic queries — pre-existing (blame 2026-04-12), not benchmark-affecting. Backlog: 3-line swap tobuildFtsOrQuery(needs barrel export from hive-mind-core). - S1 LOW ×3: mixed CJK+stopword queries reach LIKE fallback with stopword noise (narrow trigger,
fusion dampens); CJK terms silently dropped from mixed queries when ASCII tokens survive
(documented tradeoff, vector lane compensates, v2 = per-token LIKE augmentation); pure-CJK
still
[]in MultiMind.ftsSearch / raw-detail-lane (per spec, not a regression). - #13 LOW:
NOT LIKE '[Loop:%'is ASCII case-insensitive in SQLite — a user frame starting[loop:is also excluded from lane amplification (stays recallable; bounded). - #13 NIT ×2:
originis self-inflicted opt-out only (localhost+auth, no escalation); auto skill-capture heuristic (sessionToolSequences) not gated for automated turns. - #15 LOW ×2: vault-POST cache invalidation is a near-no-op today (env/vault checks are uncached;
only bin lookups cache) — harmless, kept as forward-compat; YAML sequence form (
- KEY) underrequires:not parsed (only inline[a, b]/ comma form) — document the supported grammar. - #15 NIT:
k in process.envwalks the prototype chain (cosmetic false-positive edge). - #12 LOW/NIT:
frames.update()leaves stalememory_frame_chunksrows underWAGGLE_CHUNK_RETRIEVAL=1(off by default); injection scan threshold 0.7 lets single-category signals through (tool-output provenance issource='system', not user-trusted).
Gate status after fixes
agent tsc 0 · server tsc clean via paths-harness · fix-affected suites 36/36 (cron-tools incl. new SEC + range-bypass cases, compaction-persist incl. cross-mind guard + no-downgrade cases, cron-ai-task daily-cap alive). Full suites re-run pre-push.
FINAL: APPROVE — all HIGH resolved, 0 CRITICAL, residuals are LOW/NIT/pre-existing.