Audit-pass follow-up: security/OTP/test hardening#60
Merged
Conversation
…-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)
…sions stores), safer slug-index write order
…rrect async_nolink completion-clause comment
nyo16
added a commit
that referenced
this pull request
Jun 22, 2026
The audit content for releases 0.16.2-0.16.5 had piled up under [Unreleased] and was never versioned (releases were tagged without splitting the changelog). Split it into dated sections using the tag-snapshot delta as ground truth (the [Unreleased] content at each tag = that release's cumulative content): - [0.16.2] 2026-05-16 — provider marshalling, ETS lifecycle, OTP hygiene, telemetry (#58) - [0.16.3] 2026-05-29 — security pass: RCE gate, SSRF, sandbox + audit findings (#59) - [0.16.4] 2026-06-05 — security/OTP/test hardening (#60); summarized from the commit, since this release added nothing to the changelog at the time - [0.16.5] 2026-06-12 — InputGuard fail-closed, permissive execute gate, policy bypass (#61) - [Unreleased] (-> 0.16.6) — perf hot-path hardening (#62) + the docs overhaul (#63) Bullets were moved verbatim, not rewritten; dates match the git tags exactly. Added compare-links for each new version. Verified: no [Unreleased] bullet was dropped, mix docs is 0 warnings, mix format clean.
nyo16
added a commit
that referenced
this pull request
Jun 22, 2026
The audit content for releases 0.16.2-0.16.5 had piled up under [Unreleased] and was never versioned (releases were tagged without splitting the changelog). Split it into dated sections using the tag-snapshot delta as ground truth (the [Unreleased] content at each tag = that release's cumulative content): - [0.16.2] 2026-05-16 — provider marshalling, ETS lifecycle, OTP hygiene, telemetry (#58) - [0.16.3] 2026-05-29 — security pass: RCE gate, SSRF, sandbox + audit findings (#59) - [0.16.4] 2026-06-05 — security/OTP/test hardening (#60); summarized from the commit, since this release added nothing to the changelog at the time - [0.16.5] 2026-06-12 — InputGuard fail-closed, permissive execute gate, policy bypass (#61) - [Unreleased] (-> 0.16.6) — perf hot-path hardening (#62) + the docs overhaul (#63) Bullets were moved verbatim, not rewritten; dates match the git tags exactly. Added compare-links for each new version. Verified: no [Unreleased] bullet was dropped, mix docs is 0 warnings, mix format clean.
nyo16
added a commit
that referenced
this pull request
Jun 22, 2026
…#64) * docs: follow-ups — CHANGELOG entries, silent mix docs, 4 new examples Post-overhaul cleanup (follow-up to #63). CHANGELOG: - Add the missing 0.12.13 (custom: provider, #34) and 0.12.12 (memory/context/ AgentServer fixes) entries — both had release tags but no changelog sections — with matching compare-links. mix docs — now 0 warnings: - Qualify `Agent.new/2` -> `Nous.Agent.new/2` (resolves & links) in CHANGELOG. - De-link historical references to since-private/hidden APIs in CHANGELOG and AGENTS.md (run_with_tools/6, Gemini.parse_content/1, Model.default_receive_timeout/1, Provider.request/3, Plugins.Memory.init/2, Nous.Application, Persistence.ETS.TableOwner). New examples (all run or degrade gracefully without a provider): - examples/llm_oneshot.exs — bare Nous.LLM API (generate_text/3, /3 bang, stream_text/3). - examples/knowledge_base.exs — KB store add/search + KB agent plugin. - examples/advanced/summarization.exs — auto-compaction via the Summarization plugin. - examples/advanced/web_tools.exs — WebFetch/SearchScrape/Tavily/Brave tools. - Indexed all four in examples/README.md. Docs: - Clarify the three LiveView examples' distinct roles (patterns reference vs complete chat app vs multi-agent dashboard). Verified: mix format, compile --warnings-as-errors, docs (0 warnings), credo --strict (clean); new examples run green offline. Note: v0.16.2-v0.16.5 are tagged but lack CHANGELOG entries and mix.exs @Version (0.16.1) is behind the latest tag — deferred to a separate version/release reconciliation pass rather than guessing release notes. * chore: bump @Version to 0.16.6 mix.exs @Version had been left at 0.16.1 across the v0.16.2-v0.16.5 release tags (each tagged without bumping it). master is 3 commits past v0.16.5, so the next release is 0.16.6. Note: the matching CHANGELOG entries for 0.16.2-0.16.5 are still outstanding (separate version/release reconciliation). * docs(CHANGELOG): split accumulated [Unreleased] into 0.16.2-0.16.5 The audit content for releases 0.16.2-0.16.5 had piled up under [Unreleased] and was never versioned (releases were tagged without splitting the changelog). Split it into dated sections using the tag-snapshot delta as ground truth (the [Unreleased] content at each tag = that release's cumulative content): - [0.16.2] 2026-05-16 — provider marshalling, ETS lifecycle, OTP hygiene, telemetry (#58) - [0.16.3] 2026-05-29 — security pass: RCE gate, SSRF, sandbox + audit findings (#59) - [0.16.4] 2026-06-05 — security/OTP/test hardening (#60); summarized from the commit, since this release added nothing to the changelog at the time - [0.16.5] 2026-06-12 — InputGuard fail-closed, permissive execute gate, policy bypass (#61) - [Unreleased] (-> 0.16.6) — perf hot-path hardening (#62) + the docs overhaul (#63) Bullets were moved verbatim, not rewritten; dates match the git tags exactly. Added compare-links for each new version. Verified: no [Unreleased] bullet was dropped, mix docs is 0 warnings, mix format clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
real_path, so file tools open the validated inode instead of re-traversing an attacker-swappable symlink (narrows TOCTOU).ensure_within/2gaineda resolved-root fallback to stay idempotent when
file_glob/file_grepre-validate wildcard results. The residual TOCTOU window (Erlang's:fileAPI has noO_NOFOLLOW) is documented.pin_connection(_, _, nil)now fails closed; no unpinned fetch path can be reintroduced viaallow_private_hosts.String.to_atom/1on YAML-controlled eval input →String.to_existing_atom/1+ fallback (eval/runner.ex,eval/evaluators/schema.ex), with a regression test proving no atomis interned.
rgpath was already safe.198.18.0.0/15+ RFC 5737 test-nets; new regression tests for decimal/hex/octal/encoded-IP SSRF bypasses.Correctness (OTP / Elixir)
get_tool_field/2:||→Map.fetch/2(falsy-safe forfalse/0/"").Process.alive?pre-check;safe_acquire/3catches:exitand fails open with a log + telemetry signal instead of crashing the agent loop.agent_serverasync 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 dedicatedhandle_infoclause instead of falling through to the catch-all.shared_stateclaims: O(1) prepend + reverse on read (matches the discoveries fix).:ets.inserttry/rescue that masked genuine table bugs.Tests
Process.sleepcalls (rely on FIFOGenServer.callserialization or an explicit flush);refute_receiveover instantrefute_received;start_supervised!for named helper Agents (noleak on crash); non-tautological glob-injection assertion; unique telemetry handler IDs + detach; membership asserts on the singleton persistence table;
async: truefor PathGuard tests.ETS ownership
:publicownership model for the KnowledgeBase and Decisions stores (cross-process tool execution requires:public; access is gated by possession of thetable reference). Safer slug-index write order.
Verification
mix compile --warnings-as-errors✅mix format --check-formatted✅mix credo --strict✅ (no issues)mix test✅ 1810 passed, 0 failed, 101 excluded (+4 new tests)Reviewed by security + Elixir specialist passes; findings addressed.
Deferred (intentional)
run_tool_with_hooks/11— pure readability; Credo already passes.UrlGuardresolver injectability.