Skip to content

Audit-pass follow-up: security/OTP/test hardening#60

Merged
nyo16 merged 7 commits into
masterfrom
fix/audit-pass-followup
Jun 5, 2026
Merged

Audit-pass follow-up: security/OTP/test hardening#60
nyo16 merged 7 commits into
masterfrom
fix/audit-pass-followup

Conversation

@nyo16

@nyo16 nyo16 commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-up hardening pass addressing review findings on commit #59 (the security/bug/performance audit). Implemented across 4 phases; all changes verified green. 24 files, +471/−120. No behavioral API
breaks.

Changes

Security

  • PathGuard now returns the canonical, symlink-resolved real_path, so file tools open the validated inode instead of re-traversing an attacker-swappable symlink (narrows TOCTOU). ensure_within/2 gained
    a resolved-root fallback to stay idempotent when file_glob/file_grep re-validate wildcard results. The residual TOCTOU window (Erlang's :file API has no O_NOFOLLOW) is documented.
  • web_fetchpin_connection(_, _, nil) now fails closed; no unpinned fetch path can be reintroduced via allow_private_hosts.
  • Atom-exhaustion DoS fixed: String.to_atom/1 on YAML-controlled eval input → String.to_existing_atom/1 + fallback (eval/runner.ex, eval/evaluators/schema.ex), with a regression test proving no atom
    is interned.
  • file_grep — the pure-Elixir fallback now runs under a 5s supervised timeout (ReDoS backstop); the rg path was already safe.
  • UrlGuard — adds 198.18.0.0/15 + RFC 5737 test-nets; new regression tests for decimal/hex/octal/encoded-IP SSRF bypasses.
  • Docs — pattern-guard marked best-effort (not authorization); tool_executor telemetry secret-handling note.

Correctness (OTP / Elixir)

  • get_tool_field/2: ||Map.fetch/2 (falsy-safe for false / 0 / "").
  • Rate limiter: dropped the racy Process.alive? pre-check; safe_acquire/3 catches :exit and fails open with a log + telemetry signal instead of crashing the agent loop.
  • agent_server async load task always replies (try/rescue/catch), so a raising backend can't wedge the caller until timeout.
  • async_nolink {ref, result} completion message is absorbed by a dedicated handle_info clause instead of falling through to the catch-all.
  • shared_state claims: O(1) prepend + reverse on read (matches the discoveries fix).
  • Stream result accumulator: flat O(n) iodata (prepend + reverse) instead of a right-nested improper list.
  • Dropped a :ets.insert try/rescue that masked genuine table bugs.

Tests

  • Removed redundant Process.sleep calls (rely on FIFO GenServer.call serialization or an explicit flush); refute_receive over instant refute_received; start_supervised! for named helper Agents (no
    leak on crash); non-tautological glob-injection assertion; unique telemetry handler IDs + detach; membership asserts on the singleton persistence table; async: true for PathGuard tests.

ETS ownership

  • Documented the intentional run-scoped/ephemeral :public ownership model for the KnowledgeBase and Decisions stores (cross-process tool execution requires :public; access is gated by possession of the
    table reference). Safer slug-index write order.

Verification

  • mix compile --warnings-as-errors
  • mix format --check-formatted
  • mix credo --strict ✅ (no issues)
  • mix test1810 passed, 0 failed, 101 excluded (+4 new tests)

Reviewed by security + Elixir specialist passes; findings addressed.

Deferred (intentional)

  • Bundling the 11-arg run_tool_with_hooks/11 — pure readability; Credo already passes.
  • Full DNS-rebind mock-resolver test — the pin contract is already covered; a full test needs UrlGuard resolver injectability.

nyo16 added 7 commits June 5, 2026 07:21
…-closed, atom-DoS, ReDoS cap, UrlGuard ranges, docs)
…async-load reply-on-crash, async_nolink absorb, claims O(1), iodata flat accumulation, drop needless ets rescue)
…receive, start_supervised!, non-tautological glob assertion, unique telemetry handler IDs+detach, membership asserts, encoded-IP SSRF cases, async path_guard)
…rrect async_nolink completion-clause comment
@nyo16 nyo16 merged commit e5e807c into master Jun 5, 2026
6 checks passed
@nyo16 nyo16 deleted the fix/audit-pass-followup branch June 5, 2026 14:20
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.

1 participant