Skip to content

security+arch: Top-10 action list — A1..A10 from full audit#158

Merged
AVADSA25 merged 2 commits into
mainfrom
top10-action-list
May 30, 2026
Merged

security+arch: Top-10 action list — A1..A10 from full audit#158
AVADSA25 merged 2 commits into
mainfrom
top10-action-list

Conversation

@AVADSA25
Copy link
Copy Markdown
Owner

Summary

Closes the 10 prioritized items from ~/codec-audit-reports/CODEC-FULL-AUDIT-2026-05-30.md. Pytest: 2001 → 2026 (+25 new regression tests, 0 regressions).

What landed

🔴 CRITICAL security (3 items)

  • A1/api/save_file mirrors PR-1C blocklist (codec_dashboard.py:1950-2055). RCE chain via drop-plugin → write-allowlist-hash is closed.
  • A2 — DOMPurify fail-closed in codec_vibe.html:502. CDN blip / sub-resource hijack no longer escalates to XSS.
  • A3 — License _fetch_pubkey + license_state caching (codec_license.py:118-180,260-310). At 100 paying customers: ~5 req/sec → ~0.03 req/sec on the license server, ~4s latency tail removed.

🟠 HIGH XSS (1 item)

  • A4 — Escape ev.src in codec_audit.html:503 + p.label/description/file/e.message in codec_dashboard.html:1700-1735 renderPrompts.

🟡 MEDIUM (4 items)

  • A5_correlation_id_var + _voice_correlation_id_var + _new_correlation_id moved to codec_audit (canonical home). codec_agents + codec_voice re-export via is-equality. Eliminates 3 of 4 documented import cycles.
  • A6 — New CONTRIBUTING.md (~300 lines): quick-start, how to add a skill, how to add a crew, code conventions, audit emit pattern, PR workflow, security-boundary checklist.
  • A7codec_jsonstore.file_lock opens the lock file INSIDE the try-block. Closes the leaked-LOCK_EX-handle case on canonical cross-process lock primitive.
  • A8codec_voice.py 50 print()log.error/warning/debug/info (classified by payload substring). Daemon emits to structured log.

🟢 LOW (2 items)

  • A9busy_timeout=5000 applied to codec_memory_upgrade._conn() + 5 raw sqlite3.connect sites in codec_heartbeat.
  • A10_bg_watcher poll 0.2s → 1.0s in codec_dashboard. 5× reduction in stat()+exists() syscalls/day per customer (432K → 86K).

Test plan

  • pytest tests/test_top10_action_list.py25/25 new regression tests pass, parametrized for both vulnerable and safe paths
  • pytest tests/test_dangerous_command.py — 77/77 PR-2G tests still pass
  • pytest tests/test_license_blockers.py — 43/43 PR-security: license-readiness — close 7 stress-test blockers (LS-1..LS-7) + SR-6 bonus #157 regression tests still pass
  • pytest tests/test_destructive_consent.py — 21/21 pass
  • pytest tests/test_voice_pipeline.py — voice tests pass after print→log migration
  • pytest tests/test_a12_invariant.py — passes (A8 prints replaced with log calls, no new chat/completions POSTs)
  • pytest tests/ (full suite) — 2026 passed, 80 skipped, 0 failed in 1m25s
  • Import identity verified: codec_agents._correlation_id_var is codec_audit._correlation_id_var (True)
  • _save_file_is_safe refuses 7 dangerous-path classes (~/.codec, repo skills/, /etc, sensitive filenames, etc.)
  • Operator action after merge: pm2 restart codec-dashboard codec-mcp-http to pick up new save_file behavior

Report

Full findings: ~/codec-audit-reports/CODEC-FULL-AUDIT-2026-05-30.md (full 12-section graded audit + Top-10 action list).

🤖 Generated with Claude Code

Mikarina13 and others added 2 commits May 30, 2026 09:38
Closes the 10 prioritized items from CODEC-FULL-AUDIT-2026-05-30.md.
Pytest: 2001 → 2026 (+25 new regression tests, 0 regressions).

CRITICAL security
- A1 / SR-8: /api/save_file now mirrors PR-1C blocklist — refuses writes
  under ~/.codec, repo skills/, system roots, sensitive filenames/exts.
  Emits save_file_blocked audit on refusal. Was: ~/.codec was in the
  allowlist, enabling RCE via drop-plugin → write allowlist hash chain.
  codec_dashboard.py:1950-2055
- A2 / SR-9: DOMPurify fail-closed in codec_vibe.html. If DOMPurify
  fails to load (CDN blip, ad-blocker, sub-resource hijack), raw LLM
  output no longer goes to innerHTML; falls back to escape + br.
- A3 / SR-10: license._fetch_pubkey + license_state caches with 1h TTL
  (pubkey) and 60s TTL (state). At 100 paying customers this drops
  license-server load from ~5 req/sec sustained to ~0.03 req/sec, and
  removes the ~4s latency tail when AVA's edge flaps. New
  _invalidate_caches() escape hatch for operator-driven rotation.
  codec_license.py:118-180,260-310

HIGH XSS
- A4 / SR-XSS: escape ev.src in codec_audit.html:503; escape p.label /
  description / file / e.message in codec_dashboard.html:1700-1735
  renderPrompts. Closes stored-XSS sink through ~/.codec/audit.log and
  ~/.codec/prompt_overrides.json.

MEDIUM architecture
- A5 / SR-5: _correlation_id_var + _voice_correlation_id_var + _new_-
  correlation_id moved from codec_agents / codec_voice into codec_audit
  as canonical home. codec_agents and codec_voice re-export (back-compat
  via `is`-equality). codec_ask_user reads only from codec_audit.
  Eliminates 3 of 4 documented import cycles.
- A6: New CONTRIBUTING.md (~300 lines) covering quick-start, how to add
  a skill, how to add a crew, how to propose substantive changes, code
  conventions, audit emit pattern, PR workflow, security-boundary
  checklist. Replaces the previous 92-line stub.
- A7 / SR-11: codec_jsonstore.file_lock — open() inside try block so a
  raise between open() and the body's try-block can't leak the LOCK_EX
  sidecar handle. This is the canonical cross-process lock primitive
  used by every multi-daemon JSON writer.
- A8: codec_voice.py 50 print() → log.error/warning/debug/info (auto-
  classified by payload substring). Daemon now emits to structured log;
  PM2 stdout no longer the only signal.

LOW infrastructure
- A9 / SR-12: busy_timeout=5000 applied to codec_memory_upgrade._conn()
  + the 5 raw sqlite3.connect sites in codec_heartbeat. Drop-in fix for
  intermittent SQLITE_BUSY under concurrent chat + observer + facts.
- A10 / SR-13: _bg_watcher poll 0.2s → 1.0s in codec_dashboard. 5×
  reduction in stat()+exists() syscalls/day per customer (432K → 86K).

Tests
- tests/test_top10_action_list.py — 25 new parametrized regression
  tests covering A1, A3, A5, A7, A9, A10. Each pins both the safe and
  the previously-vulnerable paths.
- CONTRIBUTING.md "current baseline" reads "1,300+" to match the
  reconciled F-17 number that test_readme_investor pins (the actual
  count is 2026; the doc reads the marketing-number-of-record).

Full pytest: 2026 passed, 80 skipped (env-dependent), 0 failed (1m25s).
Audit report: ~/codec-audit-reports/CODEC-FULL-AUDIT-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #158 CI smoke ruff gate failed:
- codec_agents.py:11 `secrets` — only used by the old local `_new_correlation_id`
  which now lives in codec_audit (A5 refactor)
- tests/test_top10_action_list.py:19,20 `time` + `pathlib.Path` — unused after
  parametrize simplification

No behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@AVADSA25 AVADSA25 merged commit 2054671 into main May 30, 2026
1 check passed
AVADSA25 added a commit that referenced this pull request May 30, 2026
…s/infra) (#159)

* audit cleanup: Tier 2-5 sweep — B1..B8 (security/quality/coverage/docs/infra)

Closes everything remaining from CODEC-FULL-AUDIT-2026-05-30.md beyond
the Top-10 already landed in PR #158. Pytest: 2026 → 2099 (+73 new
regression tests, 0 regressions).

SECURITY (B1)
- B1/SR-14: X-Content-Type-Options:nosniff + Referrer-Policy:same-origin
  applied to every response via CSPMiddleware.
- B1/SR-15: /api/upload Content-Length pre-check + decoded-size cap +
  base64 expansion guard (3-layer defense vs memory exhaustion at 50MB).
- B1/SR-16: prompt-injection fence markers wrap uploaded-doc text via
  _fence_user_document — `<<<USER_DOCUMENT name="...">>> ... <<<END>>>`.
  Source-side fence stripping prevents attacker smuggling a fake "end"
  to escape the marker.

CODE QUALITY (B2)
- B2/SR-17: narrowed 14 bare `except Exception: pass` sites to specific
  types — OSError in codec_dictate (10 sites: os.unlink + proc cleanup),
  ProcessLookupError⊂OSError in codec_textassist (2), (ImportError,
  AttributeError) in codec_dispatch's license gate.
- B2/SR-18: codec_watcher + codec_dictate now import service URLs from
  codec_config (was: 5 hardcoded localhost URLs that silently desynced
  on a non-default setup).
- B2/SR-19: codec_heartbeat._is_dangerous fallback now fail-CLOSED (was:
  silent fallback to an 11-pattern stale blocklist while PR-2G has 50+).
- B2/SR-20: codec_session 4 raw `sqlite3.connect` sites migrated to
  `with sqlite3.connect(...) as c` + busy_timeout=5000. Auto-commit on
  clean exit, auto-rollback on exception, no leaked connections.

TEST COVERAGE (B3)
- B3/SR-21: tests/test_security.py crew floor bumped 8 → 12.
- B3/SR-22: tests/test_wake_word.py (NEW, 19 tests) — covers homophone
  set, ≥5-char gate, case-insensitivity, anti-false-wake. Was: ZERO
  unit tests on the highest-traffic security-relevant code.
- B3/SR-23: tests/test_skill_isolation_shadowing.py (NEW, 3 tests) —
  pins that codec_dispatch does NOT load from ~/.codec/skills/, plus
  per-skill exception isolation invariants.
- B3/SR-24: tests/test_all_crews_build.py (NEW, 36 tests) — parametrized
  smoke for every crew in CREW_REGISTRY (registration, builder returns
  Crew, allowed_tools non-empty). Was: only 3 of 12 had runtime tests.
- B3/SR-25: tests/test_oauth_flow_e2e.py (NEW, 4 tests) — pins PKCE
  format + provider TTL constants + scope-escalation guard.

DOCUMENTATION (B4)
- B4/SR-26: 3 trigger collisions resolved —
  * Removed "open google" from chrome_open → "open google docs" now
    routes to google_docs as intended.
  * Removed bare "find file"/"my files"/"recent files" from google_drive
    + added "my files"/"list my files"/"recent files" to file_search →
    "list my files" now routes to local FS, not Drive.
  * Removed "search the web"/"web search"/"search for" from chrome_search
    → "search the web" now routes to web_search (text result) instead of
    Chrome.
- B4/SR-26: FEATURES.md Section 6 cleanup — removed phantom `codec
  (meta-dispatcher)` entry (file doesn't exist), added 4 real skills
  that were missing (active_window, audit_verify, pilot, plugin_approve),
  renumbered Section 7 sequence break at lines 248-264 (was 22→18 jump).

INFRASTRUCTURE (B5)
- B5/SR-27: install.sh now checks Apple Silicon (warns on Intel) +
  macOS version floor (warns below Ventura 13).
- B5/SR-28: PWA manifest declares 192x192 + 512x512 icon entries (maskable
  purpose) — Android Add-to-Home-Screen no longer warns about missing
  standard sizes.
- B5/SR-29: codec_vibe.html's two cdnjs.cloudflare.com references gained
  crossorigin="anonymous" so SRI hash pinning is a one-line addition
  (`integrity="sha384-..."`) once hashes are computed.

CLOUD LLM (B7)
- B7/SR-30: codec_ava_client.ava_chat now adds `cache_control: ephemeral`
  to the system + first-user message when routing to Claude models.
  50-75% input-token cost savings on repeat turns of the same session.
  New _tag_messages_for_anthropic_cache lifts string-content into
  rich-content format for cache_control attachment. Idempotent.

AUTH HARDENING (B8)
- B8/SR-31: codec_pinhash.py (NEW) — hash_pin/verify_pin/needs_rehash
  with argon2id (memory-hard, GPU-resistant) for new hashes and
  backward-compat SHA-256 verification for legacy hashes. routes/auth.py
  /api/auth/pin uses the new verify_pin (handles both formats).
  argon2-cffi added to requirements.txt; falls back to SHA-256 with
  warning if missing.
- tests/test_pinhash.py (NEW, 9 tests) — argon2id round-trip, legacy
  SHA-256 round-trip, format detection, empty/malformed rejection,
  needs_rehash signaling.

DEFERRED (B6 architecture refactor)
- chat_completion (608 LOC) extraction → chat_pipeline.py: deferred.
  High-risk for in-session execution; the handler has many implicit
  module-level state dependencies (cfg cache, _step_budgets, _saved_
  agents). Worth a dedicated PR with thorough manual verification.
- codec_hooks.py split into runtime + trust store: same reason.
- Dashboard route-group extraction (the remaining /api/qchat, /api/vibe,
  /api/heartbeat, /api/schedules, /api/cortex, /api/audit, /api/observer,
  /api/notifications groups): genuinely sprint-sized, not in scope here.

Full pytest: 2099 passed, 82 skipped (env-dependent), 0 failed (1m34s).
Audit reports: ~/codec-audit-reports/CODEC-FULL-AUDIT-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(lint): ruff F401 — drop unused hmac/pytest imports

PR #159 CI smoke ruff gate failed:
- routes/auth.py imported `hmac` (was used by legacy SHA-256 verify;
  codec_pinhash.verify_pin now owns the compare path)
- tests/test_pinhash.py imported `pytest` (no parametrize/fixtures used)

No behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(tests): skipif argon2-cffi missing — CI runner doesn't install optional deps

PR #159 CI smoke pytest failed: argon2-cffi is declared in
requirements.txt but the CI runner installs a subset. The 4 argon2-
specific tests now skip gracefully when the package is absent; the
backward-compat SHA-256 + needs_rehash-returns-false tests run
unconditionally (those are the ship-critical invariants).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Mickael Farina <farina.mickael@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants