Skip to content

feat(security): egress network logging + VIGIL verify-before-commit gate#3069

Merged
bug-ops merged 5 commits intomainfrom
egress-vigil-security
Apr 17, 2026
Merged

feat(security): egress network logging + VIGIL verify-before-commit gate#3069
bug-ops merged 5 commits intomainfrom
egress-vigil-security

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Apr 16, 2026

Summary

Closes #3058, closes #2306.

Implements two complementary security features for Zeph's tool execution pipeline:

  • Egress network logging (zeph-tools): every outbound HTTP request from WebScrapeExecutor emits a structured EgressEvent to the audit log, with URL redaction, per-hop tracking, bounded mpsc telemetry channel, and TUI Security panel surface.
  • VIGIL verify-before-commit (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 correlated AuditEntry emitted on each violation.

Egress logging (closes #3058)

  • EgressEvent struct in crates/zeph-tools/src/audit.rs with correlation_id, hop, URL, method, status, duration, bytes
  • AuditEntry.correlation_id: Option<String> for cross-event correlation
  • AuditLogger::log_egress() via bounded mpsc::channel(256) + Arc<AtomicU64> drop counter (egress_dropped_total)
  • WebScrapeExecutor instrumented: success, non-2xx, connection failure, body-too-large, redirect-blocked, SSRF/domain/scheme-blocked paths all emit events
  • redact_url_for_log() strips userinfo and secret query params before storage (no credential leak)
  • EgressConfig in [tools.egress]: enabled, log_blocked, log_response_bytes, log_hosts_to_tui
  • Wired in agent_setup.rs, acp.rs, daemon.rs, runner.rs; shared drain_egress_events() function
  • egress_requests_total, egress_blocked_total, egress_dropped_total in MetricsSnapshot
  • TUI Security panel: 3 new egress metric rows

VIGIL intent-anchoring gate (closes #2306)

  • VigilGate in crates/zeph-core/src/agent/vigil.rs: regex tripwire using zeph_tools::patterns::RAW_INJECTION_PATTERNS + strip_format_chars pre-processing
  • Integration point: tool_execution/native.rs after tool executor returns, before sanitize_tool_output
  • Block mode: emits AuditEntry with vigil_risk=High, error_category="vigil_blocked" (non-retryable), sentinel text "[security: content blocked by guardrails; retrying will produce the same result]"
  • Sanitize mode (default): truncates output, emits AuditEntry with vigil_risk=Medium
  • current_turn_intent cached per turn in SecurityState, cleared at turn end and /clear
  • Subagents exempt via parent_tool_use_id.is_some()
  • FailureKind::SecurityBlocked for skill outcome tracking
  • [security.vigil] config: enabled, strict_mode, extra_patterns (validated with RegexBuilder size limits)
  • Wired in runner.rs, acp.rs, daemon.rs
  • vigil_flags_total, vigil_blocks_total counters in MetricsSnapshot

Config

[tools.egress]
enabled = true
log_blocked = true
log_response_bytes = true
log_hosts_to_tui = true

[security.vigil]
enabled = true
strict_mode = false
extra_patterns = []

Spec compliance

  • specs/010-security/010-5-egress-logging.md — 11 FRs
  • specs/010-security/010-6-vigil-intent-anchoring.md — 12 FRs

Testing

  • 8279 tests pass (up from 8073 before this PR)
  • cargo +nightly fmt --check clean
  • cargo clippy --workspace --features "desktop,ide,server,chat,pdf,scheduler" -- -D warnings clean

Security notes

  • VIGIL v1 is a regex tripwire, not a defense against sophisticated attacks. Unicode homoglyph, base64, multilingual, and paraphrase bypasses are known non-goals documented in the spec threat model. v2 follow-up will add semantic/embedding-based verification.
  • URL logging uses redact_url_for_log() — strips userinfo and query params matching: token, key, secret, password, auth, sig, api_key, apikey.

Test plan

  • Start agent with [tools.egress] enabled = true and run a web scrape; verify JSONL audit log contains EgressEvent entries with redacted URLs
  • Start agent with [security.vigil] enabled = true and send a prompt that causes a tool to return injection text ("ignore all previous instructions"); verify AuditEntry with vigil_risk=High in log and sentinel in tool result
  • Verify TUI Security panel shows egress metrics rows
  • Run with strict_mode = true; verify blocked tool call does not retry

@github-actions github-actions Bot added documentation Improvements or additions to documentation skills zeph-skills crate rust Rust code changes core zeph-core crate dependencies Dependency updates enhancement New feature or request size/XL Extra large PR (500+ lines) labels Apr 16, 2026
bug-ops added 3 commits April 17, 2026 01:57
… 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.
@bug-ops bug-ops force-pushed the egress-vigil-security branch from 85b5133 to b6f5026 Compare April 16, 2026 23:57
@bug-ops bug-ops enabled auto-merge (squash) April 16, 2026 23:57
@bug-ops bug-ops merged commit f181eb4 into main Apr 17, 2026
32 checks passed
@bug-ops bug-ops deleted the egress-vigil-security branch April 17, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core zeph-core crate dependencies Dependency updates documentation Improvements or additions to documentation enhancement New feature or request rust Rust code changes size/XL Extra large PR (500+ lines) skills zeph-skills crate

Projects

None yet

1 participant