feat(security): egress network logging + VIGIL verify-before-commit gate#3069
Merged
feat(security): egress network logging + VIGIL verify-before-commit gate#3069
Conversation
… gate Implements two complementary security layers for tool output processing: 1. Egress network logging (FR-001 to FR-004): records all outbound HTTP requests from the scrape executor to a JSONL audit trail with correlation IDs, domain, method, status, and duration. Events are surfaced in the TUI Security panel and metrics counters. 2. VIGIL gate (FR-005 to FR-009): pre-sanitizer regex tripwire that scans tool outputs for prompt injection patterns before they enter LLM context. Supports block (sentinel replacement) and sanitize (truncate + annotate) actions. Exempt tool list, extra user patterns via config, and strict_mode flag. Subagents are exempt from VIGIL checks. Additional changes: - VigilRiskLevel::Low variant added to audit trail enum - SEC-M-01: strip Unicode Cf-category chars before regex match - SEC-M-02: RegexBuilder size_limit for user-supplied extra patterns - CR-3: apply_vigil wired in ACP and daemon paths - CR-4: AuditEntry emitted on VIGIL block/sanitize with error_category - CR-5: tool success outcome deferred past VIGIL gate to prevent double skill-outcome recording on blocked calls - S1-vigil: --init wizard prompts and --migrate-config step for [security.vigil] - TUI security widget: VigilFlag category arm, increased test render height
SEC-M-02: validate() used bare Regex::new; switch to RegexBuilder with size_limit and dfa_size_limit to prevent ReDoS from pathological operator-supplied patterns, consistent with VigilGate::try_new.
Change apply_vigil signature to accept &VigilConfig instead of &Config to make it callable from acp.rs session handler where the full Config is not in scope. Add vigil_config to SharedAgentDeps and extract it per the causal_ipi_config pattern. Update runner.rs and daemon.rs call sites to pass &config.security.vigil.
85b5133 to
b6f5026
Compare
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
Closes #3058, closes #2306.
Implements two complementary security features for Zeph's tool execution pipeline:
zeph-tools): every outbound HTTP request fromWebScrapeExecutoremits a structuredEgressEventto the audit log, with URL redaction, per-hop tracking, bounded mpsc telemetry channel, and TUI Security panel surface.zeph-core): a regex-based tripwire gate that checks tool output against cached user intent before the result enters the LLM context. Flagged outputs are sanitized (default) or blocked (strict mode), with a correlatedAuditEntryemitted on each violation.Egress logging (closes #3058)
EgressEventstruct incrates/zeph-tools/src/audit.rswithcorrelation_id,hop, URL, method, status, duration, bytesAuditEntry.correlation_id: Option<String>for cross-event correlationAuditLogger::log_egress()via boundedmpsc::channel(256)+Arc<AtomicU64>drop counter (egress_dropped_total)WebScrapeExecutorinstrumented: success, non-2xx, connection failure, body-too-large, redirect-blocked, SSRF/domain/scheme-blocked paths all emit eventsredact_url_for_log()strips userinfo and secret query params before storage (no credential leak)EgressConfigin[tools.egress]:enabled,log_blocked,log_response_bytes,log_hosts_to_tuiagent_setup.rs,acp.rs,daemon.rs,runner.rs; shareddrain_egress_events()functionegress_requests_total,egress_blocked_total,egress_dropped_totalinMetricsSnapshotVIGIL intent-anchoring gate (closes #2306)
VigilGateincrates/zeph-core/src/agent/vigil.rs: regex tripwire usingzeph_tools::patterns::RAW_INJECTION_PATTERNS+strip_format_charspre-processingtool_execution/native.rsafter tool executor returns, beforesanitize_tool_outputAuditEntrywithvigil_risk=High,error_category="vigil_blocked"(non-retryable), sentinel text"[security: content blocked by guardrails; retrying will produce the same result]"AuditEntrywithvigil_risk=Mediumcurrent_turn_intentcached per turn inSecurityState, cleared at turn end and/clearparent_tool_use_id.is_some()FailureKind::SecurityBlockedfor skill outcome tracking[security.vigil]config:enabled,strict_mode,extra_patterns(validated withRegexBuildersize limits)runner.rs,acp.rs,daemon.rsvigil_flags_total,vigil_blocks_totalcounters inMetricsSnapshotConfig
Spec compliance
specs/010-security/010-5-egress-logging.md— 11 FRsspecs/010-security/010-6-vigil-intent-anchoring.md— 12 FRsTesting
cargo +nightly fmt --checkcleancargo clippy --workspace --features "desktop,ide,server,chat,pdf,scheduler" -- -D warningscleanSecurity notes
redact_url_for_log()— strips userinfo and query params matching:token,key,secret,password,auth,sig,api_key,apikey.Test plan
[tools.egress] enabled = trueand run a web scrape; verify JSONL audit log containsEgressEvententries with redacted URLs[security.vigil] enabled = trueand send a prompt that causes a tool to return injection text ("ignore all previous instructions"); verifyAuditEntrywithvigil_risk=Highin log and sentinel in tool resultstrict_mode = true; verify blocked tool call does not retry