| Version | Supported |
|---|---|
main |
✅ |
| Latest LibreFang release | ✅ |
If you discover a security vulnerability in LibreFang, please report it privately.
Do NOT open a public GitHub issue for security vulnerabilities.
- Use GitHub's private vulnerability reporting flow:
https://github.com/librefang/librefang/security/advisories/new - Include:
- Description of the vulnerability
- Steps to reproduce
- Affected versions
- Potential impact assessment
- Suggested fix (if any)
- Acknowledgment within 48 hours
- Initial assessment within 7 days
- Fix timeline communicated within 14 days
- Credit given in the advisory (unless you prefer anonymity)
The following are in scope for security reports:
- Authentication/authorization bypass
- Remote code execution
- Path traversal / directory traversal
- Server-Side Request Forgery (SSRF)
- Privilege escalation between agents or users
- Information disclosure (API keys, secrets, internal state)
- Denial of service via resource exhaustion
- Supply chain attacks via skill ecosystem
- WASM sandbox escapes
LibreFang implements defense-in-depth with the following security controls:
- Capability-based permissions: Agents only access resources explicitly granted
- RBAC multi-user: Owner/Admin/User/Viewer role hierarchy
- Privilege escalation prevention: Child agents cannot exceed parent capabilities
- API authentication: Bearer token with loopback bypass for local CLI
- Path traversal protection:
safe_resolve_path()/safe_resolve_parent()on all file operations - SSRF protection: Private IP blocking, DNS resolution checks, cloud metadata endpoint filtering
- Image upload validation: exact-match MIME allowlist on
/api/agents/{id}/uploadcoversimage/png,image/jpeg,image/gif,image/webp; scriptable formats likeimage/svg+xmlare rejected. Upload size is capped byKernelConfig.max_upload_size_bytes(default 10 MiB — tighten it inconfig.tomlif your threat model demands a smaller limit). - Prompt injection heuristics (best-effort, not a security boundary): Skill content is
scanned for a short hard-coded list of English override phrases and exfiltration keywords
(
ignore previous instructions,exfiltrate,post to https, …) via case-insensitive substring match. Matches emit warnings and block installation of ClawHub skills whoseprompt_contextcontains a critical pattern. This is a warning layer for obviously malicious content, not a defence against a motivated attacker: Unicode homoglyphs, zero-width separators, line-split keywords, Base64/other encodings, markdown/link obfuscation, and non-English phrasing all bypass it. The actual runtime safety of installed skills comes from the capability system and the WASM / subprocess sandbox (see Runtime Isolation), which bound what a skill can do regardless of what its prompt text says.
- Ed25519 signed manifests: Agent identity verification
- HMAC-SHA256 wire protocol: Mutual authentication with nonce-based replay protection
- Secret zeroization (scoped to the credential vault): the encrypted
credential vault in
librefang-extensions/src/vault.rsstores every entry asZeroizing<String>, so individual vault reads drop plaintext immediately after use and the vault master key is also held inZeroizing<[u8; 32]>. The embedding driver re-wraps its API key withZeroizing::newafter reading it from config. Other secret-carrying fields onKernelConfig(api_key,dashboard_pass,dashboard_pass_hash) are still plainString: adding a destructor toKernelConfigbreaks partial-move patterns across ~700 call sites, and switching every field toZeroizing<String>requires a serde-compatible newtype rollout that is not yet in place. The in-process copy of these fields therefore persists in heap memory until the owningArc<KernelConfig>is dropped, which is good-enough against post-exit forensics on most platforms but is not the "wiped on every drop" guarantee the previous bullet implied. If you need stronger memory hygiene, run the daemon inside a memory-encrypted VM or disable core dumps for the process.
-
WASM dual metering: Fuel limits + epoch interruption with watchdog thread
-
Subprocess sandbox: Environment isolation (
env_clear()), restricted PATH -
Tool-sink heuristics (pattern match, not full information-flow tracking):
crates/librefang-types/src/taint.rsdefinesTaintLabel,TaintedValue, andTaintSink, and the LLM tool runner checks two sinks before executing risky tool calls:check_taint_shell_execrefuses commands matchingcurl,wget,| sh,| bash,base64 -d,eval(plus the shell-metacharacter denylist), andcheck_taint_net_fetchrefuses URLs whose query string or percent-decoded parameter names containapi_key,apikey,token,secret,password, or anauthorization:header fragment. Values that hit a pattern are wrapped inTaintedValueand run throughcheck_sink, which is where the refusal originates.This is not a general information-flow control system: labels are attached at the call site the moment a pattern matches, they do not propagate across function boundaries, LLM tool outputs are not automatically labelled, and there is no compiler/type-level enforcement — code that never constructs a
TaintedValueis entirely outside the check. Treat it as a targeted denylist for two specific exfiltration / injection shapes in the tool runner, not as a lattice that covers all untrusted data in the process.
- GCRA rate limiter: Cost-aware token buckets per IP
- Security headers: CSP, X-Frame-Options, X-Content-Type-Options, HSTS
- Health redaction: Public endpoint returns minimal info; full diagnostics require auth
- CORS policy: Restricted to localhost when no API key configured
- Hash-linked audit log: Each entry's hash covers its fields plus the previous entry's hash, and
/api/audit/verifyrecomputes the chain from the genesis sentinel. This detects in-place edits (flip a byte in one entry and the chain breaks at that row) and row deletions (the successor'sprev_hashno longer matches). - External tip anchor (Tier 1): every audit append also writes the new tip hash to
~/.librefang/data/audit.anchoroutside the SQLite database (seeAuditLog::with_db_anchoredincrates/librefang-runtime/src/audit.rs). On startup and on every/api/audit/verifycall, the in-DB tip is reconciled against the anchor file; if they diverge, verification fails closed (valid: false,anchor_status: "diverged"). An attacker rewriting the SQLite chain from genesis must now also forge the anchor file in lockstep, defeating the trivial DB-only forgery. The verify response surfacesanchor_statusasok,diverged, ornoneso the dashboard can show the anchor state alongside the chain check. - Threat model — what the anchor does and does not buy: the anchor file lives next to the database by default. An attacker with full write access to
~/.librefang/data/can still corrupt both files in lockstep — the anchor is meaningfully stronger only when operators syncaudit.anchorto an append-only store they control (offsite cron rsync, signed systemd-journald mirror, transparency log). Tier-2 (journald mirror) and Tier-3 (Ed25519-signed offsite mirror, transparency log) are tracked as follow-up work in #3339. Until then, treat the audit log as tamper-evident against single-file tampering and cooperative against multi-file tampering by an attacker with full filesystem write.
Security-critical dependencies are pinned and audited:
| Dependency | Purpose |
|---|---|
ed25519-dalek |
Manifest signing |
sha2 |
Hash chain, checksums |
hmac |
Wire protocol authentication |
subtle |
Constant-time comparison |
zeroize |
Secret memory wiping |
rand |
Cryptographic randomness |
governor |
Rate limiting |