agentsec-core 0.5.0

AgentSec core library — scan / web / paste logic, pure Rust
Documentation

agentsec-core — AgentSec pure-logic library

Pure-Rust logic crate. No rmcp / clap / GUI dependency — the umbrella binary agentsec is the only transport adapter (see the agentsec crate for CLI / MCP server / hook wiring).

This crate doc is the single source of truth for the threat model, module roster, and read-only invariants. Module-level docs reference the sections below by anchor (e.g. crate root §Threat surface × vector).


§Threat surface × vector

Defense is framed as a 2-axis matrix. The vertical axis is where the threat is observed, the horizontal axis is how the injection arrives. Modules cover the following cells:

Surface Vector Module
L1 Agent config dir V3 Config tampering [scan]
L4 Network egress V1 Prompt injection [web]
L5 User-facing input V1 Prompt injection [paste]

Surface key:

  • L1 Agent config~/.claude/, ~/.cursor/, .mcp.json, skills / agents / plugins dirs, dependency manifests, lockfiles, .env
  • L2 Dependency manifestpackage.json, Cargo.toml, lockfiles (covered by [scan] as a sub-axis of L1)
  • L3 Runtime process — Agent / MCP subprocess (not implemented)
  • L4 Network egress — outbound URL fetch from Agent context
  • L5 User-facing input — pasted text, prompt body, tool args

Vector key:

  • V1 Prompt injection — instruction-override text in fetched URL / RAG / pasted content
  • V2 Supply chain — typosquat / hijacked package (not implemented)
  • V3 Config tampering — newly-installed / mutated config under L1
  • V4 Runtime tampering — process injection (not implemented)

§Module roster

Module path Role
[scan] Hash inventory of agent config + lockfiles; diff against the previous snapshot.
[paste] Multi-layer decode + multi-pattern scan + unicode anomaly check; verdict Clean / Suspicious / Blocked.
[web] URL fetch with body cap + 2-layer sanitize (regex then optional LLM) + <untrusted_content> envelope.
[output] Render a [scan::ScanOutcome] as Markdown.
[registry] Known-good MCP server registry (builtin + cache + net fetch).
[plain_mode] Temporarily disable .mcp.json files via rename + stub.
[diagnostics] Observability: info / status / recent_activity / doctor.
[emergency_stop] Enumerate Agent / MCP processes by name and SIGTERM them.
[config] Edge-resolved Config (env → struct); the only env-reading code.
[error] Crate-wide error enum.

§Runtime data root

All persisted state lives under [config::Paths::home], resolved at the binary's outer rim by [config::Config::from_env] as:

  1. $AGENTSEC_HOME if set (used by integration tests to redirect to a tempdir), else
  2. $HOME/.agentsec/, else
  3. ./.agentsec/ (last-resort fallback when HOME is unset).

Layout:

<home>/snapshots/<UTC-ts>.json   — scan snapshots (scan::snapshot)
<home>/scans/last-session-start.txt — last SessionStart hook summary
<home>/paste_log/<UTC-ts>-<id>.json — paste verdicts
<home>/web_log/<UTC-ts>-<id>.json  — sanitize results

§Environment variables

All env reads are funneled through [config::Config::from_env] (the only function in this crate that touches std::env). Library functions take &Config (or sub-references) as a parameter; they do not consult the process environment at the point of use. See [config] for the full mapping and the test-friendly [config::Config::from_env_lookup] override.

§Read-only invariants

  • [scan] never mutates any scanned path; it only reads bytes and writes to <home>/snapshots/.
  • [paste] never persists the raw input plaintext outside the JSON audit row under <home>/paste_log/.
  • [web::fetch] enforces a 2 MiB body cap and 15 s timeout; oversized bodies are rejected, not truncated.
  • [web::sanitize::semantic_layer] fails open: a missing API key or non-2xx response returns the regex-stripped input unchanged. The 1st (regex) layer alone is the floor of protection.
  • Symbolic links are not followed during [scan::inventory::collect].