agentsec-core 0.1.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 manifest** — `package.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. |
//! | [`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:
//!
//! ```text
//! <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`].
pub mod config;
pub mod emergency_stop;
pub mod error;
pub mod output;
pub mod paste;
pub mod plain_mode;
pub mod registry;
pub mod scan;
pub mod web;

pub use config::{Config, LlmConfig, Paths};
pub use error::Error;