From d9cebd128808f87dd583ed0928e15fbe58ce8802 Mon Sep 17 00:00:00 2001 From: Renn F Date: Sat, 10 Oct 2026 19:13:02 +0200 Subject: [PATCH] chore: re-vendor guard-core spec 4.1.0 corpus regen v2 at engine 4.3.2 The 4.3.2 corpus regen v2 (Guard-Core/guard-core#151, engine commit 37b6a132) is generated with the optional guard_agent module installed, so event_stream now pins 47 real envelopes across 38 non-empty cases, superseding the round-1 generation-environment artifact that pinned expected: [] for the bus-emitted scenarios. Vendor changes (cases/ is byte-identical to the reference export): - event_stream.json: real envelope pins for 38 cases (was 4) - index.json: engine_commit, generated_at, cost budget reference_ms jitter Ledger re-evaluation of php_events_xfail.json against the real pins (29 -> 32 entries, fail-closed): - removed 2 stale entries: evt_country_blocked_geo and evt_geo_lookup_failed_download now reproduce the reference envelopes - returned the 5 entries round 1 removed under the zero-pin artifact, now real got-0-want-N divergences citing their documented port gaps: evt_behavioral_violation_usage_log, evt_ip_unbanned, evt_path_excluded_health, evt_pattern_detected_xss, evt_security_bypass_all - re-reasoned the surviving entries from the count-level artifact wording to the actual envelope divergence (missing handler-level emitters, pipeline block-mapping shapes, trailing security_headers_applied envelope) Gates: detect 193/193, pipeline 35/35, pattern_safety 69 passed + 25 xfail, events 6 passed + 32 xfail + 1 advisory skip, redis_interop 7 passed + 7 xfail, cost budgets under ceilings. --- .../cases/event_stream.json | 927 +++++++++++++++++- .../guard-core-spec-4.1.0/cases/index.json | 22 +- .../php_events_xfail.json | 43 +- 3 files changed, 927 insertions(+), 65 deletions(-) diff --git a/conformance/guard-core-spec-4.1.0/cases/event_stream.json b/conformance/guard-core-spec-4.1.0/cases/event_stream.json index 65dfbe5..49f6e19 100644 --- a/conformance/guard-core-spec-4.1.0/cases/event_stream.json +++ b/conformance/guard-core-spec-4.1.0/cases/event_stream.json @@ -17,7 +17,27 @@ "body": "" } ], - "expected": [] + "expected": [ + { + "event_type": "penetration_attempt", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Penetration attempt detected: Request body: Value matched pattern ']*>[^<]*<\\/script\\s*>'", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "request_count": 1, + "trigger_info": "Request body: Value matched pattern ']*>[^<]*<\\/script\\s*>'" + } + } + ] }, { "id": "evt_penetration_attempt_passive", @@ -33,7 +53,28 @@ "body": "" } ], - "expected": [] + "expected": [ + { + "event_type": "penetration_attempt", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "logged_only", + "reason": "Suspicious pattern detected (passive mode): Request body: Value matched pattern ']*>[^<]*<\\/script\\s*>'", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "request_count": 1, + "passive_mode": true, + "trigger_info": "Request body: Value matched pattern ']*>[^<]*<\\/script\\s*>'" + } + } + ] }, { "id": "evt_ip_blocked_global_blacklist", @@ -50,7 +91,27 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "ip_blocked", + "ip_address": "203.0.113.11", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "IP 203.0.113.11 not in global allowlist/blocklist", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "ip_address": "203.0.113.11", + "filter_type": "global" + } + } + ] }, { "id": "evt_ip_banned_then_blocked", @@ -68,7 +129,45 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "ip_banned", + "ip_address": "203.0.113.12", + "country": null, + "user_agent": null, + "action_taken": "banned", + "reason": "corpus_ban", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "ip_ban", + "metadata": { + "duration": 3600 + } + }, + { + "event_type": "ip_blocked", + "ip_address": "203.0.113.12", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Banned IP attempted access: 203.0.113.12", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "ip_address": "203.0.113.12", + "filter_type": "banned" + } + } + ] }, { "id": "evt_ip_ban_failed_escalation", @@ -91,7 +190,45 @@ "body": "" } ], - "expected": [] + "expected": [ + { + "event_type": "ip_blocked", + "ip_address": "203.0.113.13", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "IP 203.0.113.13 not in global allowlist/blocklist", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "ip_address": "203.0.113.13", + "filter_type": "global" + } + }, + { + "event_type": "ip_ban_failed", + "ip_address": "203.0.113.13", + "country": null, + "user_agent": null, + "action_taken": "ban_not_applied", + "reason": "Escalation ban failed for 203.0.113.13: corpus injected ban failure", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "ip_address": "203.0.113.13" + } + } + ] }, { "id": "evt_ip_unbanned", @@ -109,7 +246,44 @@ "ip": "203.0.113.14" } ], - "expected": [] + "expected": [ + { + "event_type": "ip_banned", + "ip_address": "203.0.113.14", + "country": null, + "user_agent": null, + "action_taken": "banned", + "reason": "corpus_ban", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "ip_ban", + "metadata": { + "duration": 3600 + } + }, + { + "event_type": "ip_unbanned", + "ip_address": "203.0.113.14", + "country": null, + "user_agent": null, + "action_taken": "unbanned", + "reason": "dynamic_rule_whitelist", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "ip_ban", + "metadata": { + "action": "unban" + } + } + ] }, { "id": "evt_cloud_blocked_provider", @@ -133,7 +307,49 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "cloud_blocked", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "IP belongs to blocked cloud provider: AWS", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "cloud", + "metadata": { + "cloud_provider": "AWS", + "network": "203.0.113.0/24" + } + }, + { + "event_type": "decorator_violation", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Cloud provider IP 203.0.113.10 blocked", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": "block_clouds", + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "decorator_type": "block_clouds", + "violation_type": "cloud_provider", + "blocked_providers": [ + "AWS" + ] + } + } + ] }, { "id": "evt_https_enforced_redirect", @@ -148,7 +364,27 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "https_enforced", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "https_redirect", + "reason": "HTTP request redirected to HTTPS for security", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "original_scheme": "http", + "redirect_url": "https://example.com" + } + } + ] }, { "id": "evt_decorator_violation_auth_required", @@ -165,7 +401,28 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "decorator_violation", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Missing or invalid Bearer token", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": "authentication", + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "decorator_type": "authentication", + "violation_type": "require_auth", + "auth_type": "bearer" + } + } + ] }, { "id": "evt_behavioral_violation_usage_log", @@ -184,7 +441,29 @@ "details": "Usage threshold exceeded: 5 calls in 60s" } ], - "expected": [] + "expected": [ + { + "event_type": "behavioral_violation", + "ip_address": "203.0.113.15", + "country": null, + "user_agent": null, + "action_taken": "log", + "reason": "Behavioral rule violated: Usage threshold exceeded: 5 calls in 60s", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": "usage", + "pattern_matched": null, + "handler_name": "behavior", + "metadata": { + "endpoint": "corpus.endpoint", + "rule_type": "usage", + "threshold": 5, + "window": 60 + } + } + ] }, { "id": "evt_pattern_detected_xss", @@ -199,7 +478,39 @@ "context": "request_body" } ], - "expected": [] + "expected": [ + { + "event_type": "pattern_detected", + "ip_address": "203.0.113.7", + "country": null, + "user_agent": null, + "action_taken": "threat_detected", + "reason": "Threat detected in request_body", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": "]*>[^<]*<\\/script\\s*>", + "handler_name": "sus_patterns", + "metadata": { + "pattern": "]*>[^<]*<\\/script\\s*>", + "context": "request_body", + "content_preview": "", + "threat_score": 1.0, + "threats": 1, + "regex_threats": 1, + "semantic_threats": 0, + "timeouts": 0, + "detection_method": "enhanced", + "correlation_id": null, + "threat_categories": [ + "xss" + ], + "category": "xss" + } + } + ] }, { "id": "evt_dynamic_rules_update_apply", @@ -219,7 +530,50 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "dynamic_rule_updated", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "rules_received", + "reason": "Received updated rules corpus-rules v7", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "dynamic_rules", + "metadata": { + "rule_id": "corpus-rules", + "version": 7, + "previous_version": 0 + } + }, + { + "event_type": "dynamic_rule_applied", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "rules_updated", + "reason": "Applied dynamic rules corpus-rules v7", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "dynamic_rules", + "metadata": { + "rule_id": "corpus-rules", + "version": 7, + "ip_bans": 0, + "country_blocks": 0, + "emergency_mode": false + } + } + ] }, { "id": "evt_emergency_mode_activated", @@ -243,7 +597,71 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "dynamic_rule_updated", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "rules_received", + "reason": "Received updated rules corpus-rules v9", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "dynamic_rules", + "metadata": { + "rule_id": "corpus-rules", + "version": 9, + "previous_version": 0 + } + }, + { + "event_type": "emergency_mode_activated", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "emergency_lockdown", + "reason": "[EMERGENCY MODE] activated via dynamic rules", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "dynamic_rules", + "metadata": { + "whitelist_count": 1, + "whitelist": [ + "198.51.100.5" + ] + } + }, + { + "event_type": "dynamic_rule_applied", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "rules_updated", + "reason": "Applied dynamic rules corpus-rules v9", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "dynamic_rules", + "metadata": { + "rule_id": "corpus-rules", + "version": 9, + "ip_bans": 0, + "country_blocks": 0, + "emergency_mode": true + } + } + ] }, { "id": "evt_access_denied_decorator", @@ -263,7 +681,27 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "access_denied", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "blocked", + "reason": "corpus access denied", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": "access_control", + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "decorator_type": "access_control", + "violation_type": "corpus" + } + } + ] }, { "id": "evt_authentication_failed_decorator", @@ -282,7 +720,27 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "authentication_failed", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "blocked", + "reason": "corpus auth failed", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": "authentication", + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "decorator_type": "authentication", + "auth_type": "bearer" + } + } + ] }, { "id": "evt_content_filtered_max_size", @@ -301,7 +759,27 @@ "body": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ], - "expected": [] + "expected": [ + { + "event_type": "content_filtered", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Request size 64 exceeds limit: 10", + "endpoint": "/upload", + "method": "POST", + "status_code": null, + "decorator_type": "content_filtering", + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "decorator_type": "content_filtering", + "violation_type": "max_request_size" + } + } + ] }, { "id": "evt_country_blocked_geo", @@ -318,7 +796,27 @@ ] } ], - "expected": [] + "expected": [ + { + "event_type": "country_blocked", + "ip_address": "198.51.100.9", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Country CN is blocked", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": "country_blacklist", + "pattern_matched": null, + "handler_name": "ipinfo", + "metadata": { + "country": "CN", + "rule_type": "country_blacklist" + } + } + ] }, { "id": "evt_csp_violation_report", @@ -337,7 +835,30 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "csp_violation", + "ip_address": "", + "country": null, + "user_agent": null, + "action_taken": "logged", + "reason": "", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "security_headers", + "metadata": { + "document_uri": "https://example.com/page", + "violated_directive": "script-src", + "blocked_uri": "https://evil.example/x.js", + "source_file": "None", + "line_number": null + } + } + ] }, { "id": "evt_custom_request_check_reject", @@ -352,7 +873,27 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "custom_request_check", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Custom request check returned blocking response", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "response_status": 418, + "check_function": "corpus_reject_all" + } + } + ] }, { "id": "evt_decoding_error_unreachable", @@ -377,7 +918,27 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "emergency_mode_block", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "[EMERGENCY MODE] IP 203.0.113.10 not in whitelist", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "emergency_whitelist_count": 0, + "emergency_active": true + } + } + ] }, { "id": "evt_geo_lookup_failed_download", @@ -389,7 +950,24 @@ "call": "geo_download_failure" } ], - "expected": [] + "expected": [ + { + "event_type": "geo_lookup_failed", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "database_download_failed", + "reason": "Failed to download IPInfo database: RuntimeError", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "ipinfo", + "metadata": {} + } + ] }, { "id": "evt_path_excluded_health", @@ -407,7 +985,29 @@ "url_path": "/health" } ], - "expected": [] + "expected": [ + { + "event_type": "path_excluded", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "security_checks_bypassed", + "reason": "Path /health excluded from security checks", + "endpoint": "/health", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "excluded_path": "/health", + "configured_exclusions": [ + "/health" + ] + } + } + ] }, { "id": "evt_pattern_added_custom", @@ -420,7 +1020,28 @@ "pattern": "corpuspattern-[a-z]+" } ], - "expected": [] + "expected": [ + { + "event_type": "pattern_added", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "pattern_added", + "reason": "Custom pattern added to detection system", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "sus_patterns", + "metadata": { + "pattern": "corpuspattern-[a-z]+", + "pattern_type": "custom", + "total_patterns": 1 + } + } + ] }, { "id": "evt_pattern_removed_custom", @@ -433,7 +1054,28 @@ "pattern": "corpuspattern-[a-z]+" } ], - "expected": [] + "expected": [ + { + "event_type": "pattern_removed", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "pattern_removed", + "reason": "Custom pattern removed from detection system", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "sus_patterns", + "metadata": { + "pattern": "corpuspattern-[a-z]+", + "pattern_type": "custom", + "total_patterns": 0 + } + } + ] }, { "id": "evt_rate_limited_global", @@ -453,7 +1095,28 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "rate_limited", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Rate limit exceeded: 2 requests in 60s window", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "rate_limit", + "metadata": { + "request_count": 2, + "rate_limit": 1, + "window": 60 + } + } + ] }, { "id": "evt_dynamic_rule_violation_endpoint", @@ -477,7 +1140,49 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "rate_limited", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Rate limit exceeded: 2 requests in 60s window", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "rate_limit", + "metadata": { + "request_count": 2, + "rate_limit": 10, + "window": 60 + } + }, + { + "event_type": "dynamic_rule_violation", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Endpoint-specific rate limit exceeded: 1 requests per 60s for /api", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": "endpoint_rate_limit", + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "rule_type": "endpoint_rate_limit", + "endpoint": "/api", + "rate_limit": 1, + "window": 60 + } + } + ] }, { "id": "evt_rate_limit_script_reloaded", @@ -491,7 +1196,24 @@ "url_path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "rate_limit_script_reloaded", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "script_reloaded", + "reason": "NOSCRIPT recovery: Lua script re-cached on Redis", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "rate_limit", + "metadata": {} + } + ] }, { "id": "evt_redis_connection_established", @@ -503,7 +1225,26 @@ "call": "redis_connect" } ], - "expected": [] + "expected": [ + { + "event_type": "redis_connection", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "connection_established", + "reason": "Redis connection successfully established", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "redis", + "metadata": { + "redis_url": "redis://localhost:6379/0" + } + } + ] }, { "id": "evt_redis_error_connection_failed", @@ -515,7 +1256,27 @@ "call": "redis_connect_error" } ], - "expected": [] + "expected": [ + { + "event_type": "redis_error", + "ip_address": "system", + "country": null, + "user_agent": null, + "action_taken": "connection_failed", + "reason": "Redis connection failed: Error Multiple exceptions: [Errno 61] Connect call failed ('::1', 59999, 0, 0), [Errno 61] Connect call failed ('127.0.0.1', 59999) connecting to localhost:59999.", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "redis", + "metadata": { + "redis_url": "redis://localhost:59999/0", + "error_type": "connection_error" + } + } + ] }, { "id": "evt_route_unresolved_strict", @@ -531,7 +1292,24 @@ "guard_route_unresolved": true } ], - "expected": [] + "expected": [ + { + "event_type": "route_unresolved", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "request_blocked", + "reason": "Route resolution failed; per-route decorator config could not be applied", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": {} + } + ] }, { "id": "evt_security_bypass_all", @@ -551,7 +1329,29 @@ "url_path": "/open" } ], - "expected": [] + "expected": [ + { + "event_type": "security_bypass", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": null, + "action_taken": "all_checks_bypassed", + "reason": "Route configured to bypass all security checks", + "endpoint": "/open", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "bypassed_checks": [ + "all" + ], + "endpoint": "/open" + } + } + ] }, { "id": "evt_security_headers_applied_default", @@ -564,7 +1364,29 @@ "path": "/api" } ], - "expected": [] + "expected": [ + { + "event_type": "security_headers_applied", + "ip_address": "", + "country": null, + "user_agent": null, + "action_taken": "headers_added", + "reason": "", + "endpoint": null, + "method": null, + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "security_headers", + "metadata": { + "path": "/api", + "headers_count": 11, + "has_csp": false, + "has_hsts": true + } + } + ] }, { "id": "evt_user_agent_blocked_global", @@ -584,7 +1406,27 @@ } } ], - "expected": [] + "expected": [ + { + "event_type": "user_agent_blocked", + "ip_address": "203.0.113.10", + "country": null, + "user_agent": "evilbot/1.0", + "action_taken": "request_blocked", + "reason": "User agent 'evilbot/1.0' in global blocklist", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "middleware", + "metadata": { + "user_agent": "evilbot/1.0", + "filter_type": "global" + } + } + ] }, { "id": "evt_suspicious_request_untrusted_proxy", @@ -605,7 +1447,24 @@ "drop_cached_client_ip": true } ], - "expected": [] + "expected": [ + { + "event_type": "suspicious_request", + "ip_address": "203.0.113.17", + "country": null, + "user_agent": null, + "action_taken": "spoofing_detected", + "reason": "Potential IP spoof attempt: X-Forwarded-For header 198.51.100.77", + "endpoint": "/api", + "method": "GET", + "status_code": null, + "decorator_type": null, + "rule_type": null, + "pattern_matched": null, + "handler_name": "ip_extraction", + "metadata": {} + } + ] }, { "id": "evt_detection_engine_callback_error", diff --git a/conformance/guard-core-spec-4.1.0/cases/index.json b/conformance/guard-core-spec-4.1.0/cases/index.json index ccf5a4d..c0ea0d5 100644 --- a/conformance/guard-core-spec-4.1.0/cases/index.json +++ b/conformance/guard-core-spec-4.1.0/cases/index.json @@ -1,8 +1,8 @@ { "spec_version": "4.1.0", "engine_version": "4.3.2", - "engine_commit": "58957987b677d1e06930347443df7c8394758ea7", - "generated_at": "2026-10-10T14:39:35.908173+00:00", + "engine_commit": "37b6a132b7df974f0ea014f3100fd90a76e1ebc5", + "generated_at": "2026-10-10T16:34:12.919136+00:00", "fixed_ip": "203.0.113.7", "config_knobs": { "detection_compiler_timeout": 2.0, @@ -275,22 +275,22 @@ "k": 5, "floor_ms": 250, "timing_runs": 5, - "generated_at": "2026-10-10T14:40:11+00:00", + "generated_at": "2026-10-10T16:34:48+00:00", "budgets": { "cost_prose_8kib": { - "reference_ms": 26.8, + "reference_ms": 26.6, "expected_is_threat": false }, "cost_prose_64kib": { - "reference_ms": 182.2, + "reference_ms": 184.6, "expected_is_threat": false }, "cost_prose_256kib": { - "reference_ms": 717.9, + "reference_ms": 708.8, "expected_is_threat": false }, "cost_prose_1mib": { - "reference_ms": 1238.6, + "reference_ms": 1227.2, "expected_is_threat": false }, "cost_encoded_64kib": { @@ -298,19 +298,19 @@ "expected_is_threat": false }, "cost_binnoise_64kib": { - "reference_ms": 205.8, + "reference_ms": 200.6, "expected_is_threat": false }, "cost_threat_8kib": { - "reference_ms": 26.9, + "reference_ms": 26.4, "expected_is_threat": true }, "cost_threat_tail_256kib": { - "reference_ms": 714.5, + "reference_ms": 702.6, "expected_is_threat": true }, "cost_threat_mid_1mib": { - "reference_ms": 1242.5, + "reference_ms": 1224.9, "expected_is_threat": false } } diff --git a/conformance/guard-core-spec-4.1.0/php_events_xfail.json b/conformance/guard-core-spec-4.1.0/php_events_xfail.json index 5df40ef..effcc1e 100644 --- a/conformance/guard-core-spec-4.1.0/php_events_xfail.json +++ b/conformance/guard-core-spec-4.1.0/php_events_xfail.json @@ -1,35 +1,38 @@ { "spec_version": "4.1.0", - "_comment": "Documented divergences of the PHP event surface against the reference event_stream corpus (comparison block events_envelopes: expected keys only, volatile fields dropped recursively, absent fields recorded as null). Fail-closed: a failing case NOT listed here is red; a listed case that now passes is red (stale baseline); a case with no driver that is NOT listed is red. Root causes: (1) the reference's handler-level emitters (sus_patterns, ip_ban envelopes, security_headers, redis manager, ip extraction, decorators, dynamic-rule violation events) are not ported or are expressed as adapter seams without envelopes; (2) PHP block events ride the check pipeline bus (SecurityCheckPipeline::fireBlockHook) so their reasons are the on_block log-format strings and their metadata is the check-hook shape (check_name/trigger_info/status_code) instead of the reference's per-event metadata; (3) SecurityCheckPipeline::blockEventType maps only the eight pipeline check families, so blocks outside that map fall to the default penetration_attempt type. 4.3.2 corpus regen note: the reference generation environment does not have the optional guard_agent module installed, and the reference engine builds every middleware-bus SecurityEvent through guard_core._pydantic_plugin_mute.get_telemetry_model('SecurityEvent'), which imports guard_agent, so the construction fails closed and the reference capture harness recorded ZERO envelopes for every bus-emitted scenario; the corpus therefore pins expected: [] for them (verified live against the reference engine, which still captures 0 events for these cases). Two consequences, both applied in this ledger update: (a) the scenarios where the port emits nothing anyway now compare 0 against 0 and PASS, so five stale entries were removed (evt_behavioral_violation_usage_log, evt_ip_unbanned, evt_path_excluded_health, evt_pattern_detected_xss, evt_security_bypass_all) - their documented port gaps are unchanged but no longer produce a diff under this corpus revision; those passes ride on the corpus artifact, and a corpus regen with guard_agent installed will pin real envelopes again and resurface them as failures needing entries; (b) the scenarios where the port DOES emit (country_blocked, geo_lookup_failed) now diverge at count level against the zero pins and gained entries. If the corpus regenerates with guard_agent installed, every entry tied to a zero pin must be re-evaluated (stale entries are red by policy).", + "_comment": "Documented divergences of the PHP event surface against the reference event_stream corpus (comparison block events_envelopes: expected keys only, volatile fields dropped recursively, absent fields recorded as null). Fail-closed: a failing case NOT listed here is red; a listed case that now passes is red (stale baseline); a case with no driver that is NOT listed is red. Corpus regen v2 (engine 4.3.2 generated with the optional guard_agent module installed, Guard-Core/guard-core#151) pins 47 real envelopes across 38 non-empty cases, superseding the round-1 generation-environment artifact that pinned expected: [] for the bus-emitted scenarios. Ledger re-evaluation against the real pins: the two round-1 count-level entries (evt_country_blocked_geo, evt_geo_lookup_failed_download) are REMOVED as stale by policy (the port's envelopes now byte-match the real pins); the five entries removed in round 1 (evt_behavioral_violation_usage_log, evt_ip_unbanned, evt_path_excluded_health, evt_pattern_detected_xss, evt_security_bypass_all) RETURN because their documented port gaps produce real got-0-want-N divergences against the real pins; the remaining entries are re-reasoned from the round-1 count-level artifact wording to the actual envelope divergence (missing emitters, pipeline block-mapping shapes, and the trailing security_headers_applied envelope the port's pipeline emits and the reference does not). Root causes: (1) the reference's handler-level emitters (sus_patterns, ip_ban, behavioral_violation, security_headers, redis manager, ip extraction, csp report, several decorators) are not ported or are expressed as adapter seams without envelopes; (2) PHP block events ride the check pipeline bus (SecurityCheckPipeline::fireBlockHook), so their reasons are the on_block log-format strings and their metadata is the check-hook shape instead of the reference's per-event metadata; (3) SecurityCheckPipeline::blockEventType maps only the eight pipeline check families, so blocks outside that map fall to the default penetration_attempt type.", "cases": { - "evt_penetration_attempt_blocked": "The PHP pipeline classifies the body XSS block under the suspicious_activity check, so SecurityCheckPipeline::blockEventType maps it to suspicious_request with the check-hook metadata shape (fireBlockHook); the reference emits penetration_attempt with request_count/trigger_info metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (suspicious_request, security_headers_applied).", - "evt_penetration_attempt_passive": "PHP passive mode returns the request without a block response, so SecurityCheckPipeline::fireBlockHook never runs and the bus emits nothing for the block; the reference's passive dispatch emits a logged_only penetration_attempt. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 1 (security_headers_applied).", - "evt_ip_blocked_global_blacklist": "The PHP bus reason is the composed log format 'IP not allowed: - ' and the metadata is the check-hook shape (SecurityCheckPipeline::fireBlockHook); the reference envelope carries the bare detail reason with filter_type metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (ip_blocked, security_headers_applied).", - "evt_ip_banned_then_blocked": "The engine emits only the ip_blocked envelope: the ban-time event is an adapter-authored BanEventSink call (src/Ban/BanEventSink.php) carrying just (ip, duration, reason), so the corpus ip_banned envelope (handler ip_ban, metadata.duration) has no in-engine emitter. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (ip_blocked, security_headers_applied).", + "evt_penetration_attempt_blocked": "The PHP pipeline classifies the body XSS block under the suspicious_activity check, so SecurityCheckPipeline::blockEventType maps it to suspicious_request with the check-hook metadata shape (fireBlockHook), and the pipeline appends a trailing security_headers_applied envelope; the reference pins a single penetration_attempt envelope with request_count/trigger_info metadata (got 2, want 1).", + "evt_penetration_attempt_passive": "PHP passive mode returns the request without a block response, so SecurityCheckPipeline::fireBlockHook never runs and the bus emits only the headers-applied envelope (event_type security_headers_applied, empty ip, action headers_added); the reference's passive dispatch emits a logged_only penetration_attempt carrying the client IP.", + "evt_ip_blocked_global_blacklist": "The PHP bus reason is the composed log format 'IP not allowed: - ' and the metadata is the check-hook shape (SecurityCheckPipeline::fireBlockHook), and the pipeline appends a trailing security_headers_applied envelope; the reference pins a single ip_blocked envelope with the bare detail reason and filter_type metadata (got 2, want 1).", + "evt_ip_banned_then_blocked": "The engine emits only the ip_blocked envelope: the ban-time event is an adapter-authored BanEventSink call (src/Ban/BanEventSink.php) carrying just (ip, duration, reason), so the reference's leading ip_banned envelope (handler ip_ban, metadata.duration, action banned) has no in-engine emitter, and the pipeline appends a trailing security_headers_applied envelope.", "evt_ip_ban_failed_escalation": "No PHP seam: the corpus ipban_fault injection has no analogue (IpBanManager has no raisable ban hook), and no PHP source emits ip_ban_failed anywhere; the runner's driver throws for the corpus call.", - "evt_cloud_blocked_provider": "The pipeline emits the cloud_blocked envelope but no decorator_violation: route-level decorator events (handler middleware, decorator_type block_clouds) are not ported; the port's cloud block surfaces only through the check pipeline. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 4 (cloud_blocked, decorator_violation, cloud_blocked, security_headers_applied).", - "evt_https_enforced_redirect": "EventBus::sendHttpsViolationEvent builds the https_enforced envelope but no engine code calls it: the enforce_https redirect returns through the check pipeline, so the emit falls to the default block mapping (penetration_attempt) with check-hook metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (penetration_attempt, security_headers_applied).", - "evt_decorator_violation_auth_required": "No PHP seam for the decorator send_authentication_failed_event emitter; the PHP authentication_failed envelope comes only from the pipeline block mapping, which cannot carry injected decorator kwargs; the runner's driver throws for the corpus decorator_event call.", + "evt_ip_unbanned": "No in-engine emitter: the unban-time event is an adapter-authored BanEventSink call (src/Ban/BanEventSink.php) with no envelope, so the port emits 0 envelopes where the corpus pins 2 (ip_banned, ip_unbanned).", + "evt_behavioral_violation_usage_log": "Documented port gap (src/Behavior/BehaviorTracker.php, applyAction): the reference's behavioral_violation agent event is not modeled; the action dispatch logs only, so the port emits 0 envelopes where the corpus pins 1 (behavioral_violation).", + "evt_cloud_blocked_provider": "The port emits 4 envelopes (cloud_blocked, decorator_violation, cloud_blocked, security_headers_applied) where the reference pins 2 (cloud_blocked, decorator_violation): the pipeline surfaces the cloud block twice and appends the trailing headers-applied envelope the reference does not emit.", + "evt_https_enforced_redirect": "EventBus::sendHttpsViolationEvent builds the https_enforced envelope but no engine code calls it: the enforce_https redirect returns through the check pipeline, so the block falls to the default penetration_attempt mapping and the pipeline appends security_headers_applied; the reference pins a single https_enforced envelope (got 2, want 1).", + "evt_decorator_violation_auth_required": "The corpus decorator drive runs through the check pipeline, which maps the auth-required block to authentication_failed with check-hook metadata and appends security_headers_applied; the reference pins a single decorator_violation envelope carrying the injected decorator kwargs, which the pipeline block mapping cannot carry (got 2, want 1).", "evt_access_denied_decorator": "No PHP seam: the reference BaseSecurityDecorator send_access_denied_event emitter has no port; the bus carries only the require_https decorator pair; the runner's driver throws for the corpus decorator_event call.", "evt_authentication_failed_decorator": "No PHP seam for the decorator send_authentication_failed_event emitter; the PHP authentication_failed envelope comes only from the pipeline block mapping, which cannot carry injected decorator kwargs; the runner's driver throws for the corpus decorator_event call.", - "evt_content_filtered_max_size": "The request_size_content check blocks the oversized request with the reference reason ('Request size 64 exceeds limit: 10'), but SecurityCheckPipeline::blockEventType has no content-filtering mapping (the block falls to the default penetration_attempt) and no emitter carries the content_filtered decorator metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (penetration_attempt, security_headers_applied).", - "evt_country_blocked_geo": "Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes for this scenario because the reference generation environment lacks the optional guard_agent module (reference bus event construction fails closed, so the capture harness recorded zero); the port's geo_country_stub driver emits 1 country_blocked envelope through its own bus, so the runner sees got 1 want 0.", - "evt_geo_lookup_failed_download": "Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes for this scenario because the reference generation environment lacks the optional guard_agent module (reference bus event construction fails closed, so the capture harness recorded zero); the port's geo_download_failure driver emits 1 geo_lookup_failed envelope through its own bus, so the runner sees got 1 want 0.", + "evt_content_filtered_max_size": "The request_size_content check blocks the oversized request with the reference reason ('Request size 64 exceeds limit: 10'), but SecurityCheckPipeline::blockEventType has no content-filtering mapping (the block falls to the default penetration_attempt) and the pipeline appends security_headers_applied; the reference pins a single content_filtered envelope with decorator metadata (got 2, want 1).", "evt_csp_violation_report": "No PHP seam: SecurityHeadersPolicy computes header maps only; the reference security_headers_manager's validate_csp_report / csp_violation agent event is not ported; the runner's driver throws for the corpus csp_report call.", - "evt_custom_request_check_reject": "The custom_request check blocks with the injected 418 response, but SecurityCheckPipeline::blockEventType maps custom_request to the default penetration_attempt and the bus reason is the generic 'Request blocked'; the reference emits custom_request_check with response_status/check_function metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (penetration_attempt, security_headers_applied).", - "evt_emergency_mode_block": "The PHP emergency_mode check reason is the log-format '[EMERGENCY MODE] Access denied for IP ' with check-hook metadata; the reference envelope reason is '[EMERGENCY MODE] IP not in whitelist' with emergency metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (emergency_mode_block, security_headers_applied).", + "evt_custom_request_check_reject": "The custom_request check blocks with the injected 418 response, but SecurityCheckPipeline::blockEventType maps custom_request to the default penetration_attempt and the bus reason is the generic 'Request blocked', and the pipeline appends security_headers_applied; the reference pins a single custom_request_check envelope with response_status/check_function metadata (got 2, want 1).", + "evt_emergency_mode_block": "The PHP emergency_mode check reason is the log-format '[EMERGENCY MODE] Access denied for IP ' with check-hook metadata, and the pipeline appends a trailing security_headers_applied envelope; the reference pins a single emergency_mode_block envelope with reason '[EMERGENCY MODE] IP not in whitelist' and emergency metadata (got 2, want 1).", + "evt_path_excluded_health": "Excluded paths skip the request silently through the pipeline's exclusion scope; no path_excluded emitter exists in the port, which emits 0 envelopes where the corpus pins 1 (path_excluded).", + "evt_pattern_detected_xss": "SusPatterns::detect returns the detection result and emits no agent envelope; the reference sus_patterns_handler's pattern_detected emitter has no PHP analogue, so the port emits 0 envelopes where the corpus pins 1 (pattern_detected).", "evt_pattern_added_custom": "No PHP seam: SusPatterns carries no custom-pattern registry surface, so no pattern_added envelope exists; the runner's driver throws for the corpus add_pattern call.", "evt_pattern_removed_custom": "No PHP seam: SusPatterns carries no custom-pattern registry surface, so no pattern_removed envelope exists; the runner's driver throws for the corpus remove_pattern call.", - "evt_rate_limited_global": "The PHP rate-limit block reason is the composed log format 'Rate limit exceeded for IP: (N requests in Ws window)' and the emit rides the middleware handler with check-hook metadata; the reference envelope carries the bare 'Rate limit exceeded: ...' reason with a rate_limit handler and request_count metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (security_headers_applied, rate_limited).", - "evt_dynamic_rule_violation_endpoint": "The PHP endpoint-tier block emits rate_limited only; the reference's dynamic_rule_violation middleware emit for endpoint-specific limits has no PHP emitter. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (security_headers_applied, rate_limited).", + "evt_rate_limited_global": "The PHP rate-limit block reason is the composed log format 'Rate limit exceeded for IP: (N requests in Ws window)' and the emit rides the middleware handler with check-hook metadata, led by the trailing security_headers_applied envelope; the reference pins a single rate_limited envelope with the bare 'Rate limit exceeded: ...' reason, a rate_limit handler and request_count metadata (got 2, want 1).", + "evt_dynamic_rule_violation_endpoint": "The port emits the trailing security_headers_applied envelope ahead of rate_limited and has no dynamic_rule_violation middleware emit for endpoint-specific limits; the reference pins a single rate_limited envelope carrying the client IP and action request_blocked (envelope[0] got security_headers_applied, want rate_limited).", "evt_rate_limit_script_reloaded": "No envelope seam: the RateLimitHandler NOSCRIPT recovery reloads the script silently (src/RateLimit/RateLimitHandler.php) and its onScriptReloaded callback carries no envelope; the reference's rate_limit_script_reloaded agent event is not emitted; the runner's driver throws for the corpus rate_limit_script_reload call.", - "evt_dynamic_rules_update_apply": "DynamicRuleManager::sendRuleEvent emits through the middleware bus (handler middleware, reason 'Dynamic rules v', method SYSTEM), not the reference dynamic_rules handler envelope (reason 'Received/Applied updated rules ...', ip system, previous_version metadata). Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (dynamic_rule_updated, dynamic_rule_applied).", - "evt_emergency_mode_activated": "The PHP DynamicRuleManager emits dynamic_rule_updated/applied but has no emergency_mode_activated emitter; the emergency lockdown path logs only. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (dynamic_rule_updated, dynamic_rule_applied).", + "evt_dynamic_rules_update_apply": "DynamicRuleManager::sendRuleEvent emits through the middleware bus (handler middleware, method SYSTEM, non-null endpoint) while the reference pins the dynamic_rules handler envelope shape (reason 'Received updated rules v', null method and endpoint): envelope[0] reason got 'Dynamic rules corpus-rules v7' want 'Received updated rules corpus-rules v7'; the applied half of the pin matches.", + "evt_emergency_mode_activated": "The PHP DynamicRuleManager emits dynamic_rule_updated/applied but has no emergency_mode_activated emitter; the emergency lockdown path logs only, so the middle envelope of the reference's 3-envelope pin (dynamic_rule_updated, emergency_mode_activated, dynamic_rule_applied) is missing (got 2, want 3).", "evt_redis_connection_established": "No PHP seam: the port's RedisHandler has no manager-level agent surface; the reference RedisManager emits redis_connection from initialize; the runner's driver throws for the corpus redis_connect call.", "evt_redis_error_connection_failed": "No PHP seam: the port's RedisHandler has no manager-level agent surface; the reference RedisManager emits redis_error from a failed initialize; the runner's driver throws for the corpus redis_connect_error call.", - "evt_route_unresolved_strict": "No PHP emitter: routeResolutionStrict parses into SecurityConfig but no engine code consumes it, so the reference's route_unresolved emit has no analogue. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 1 (security_headers_applied).", + "evt_route_unresolved_strict": "No PHP emitter: routeResolutionStrict parses into SecurityConfig but no engine code consumes it, so the bus carries only the headers-applied envelope where the reference pins a single route_unresolved envelope with the client IP and action request_blocked.", + "evt_security_bypass_all": "PHP bypassed_checks short-circuits the pipeline silently; no security_bypass emitter exists in the port, which emits 0 envelopes where the corpus pins 1 (security_bypass).", "evt_security_headers_applied_default": "No driver and no emitter: the port's SecurityHeadersPolicy computes headers on demand with no agent surface (KNOWN_GAPS, security headers management); the reference's headers_applied event is not emitted.", - "evt_user_agent_blocked_global": "The PHP user-agent block reason is the composed log format 'Blocked user agent: ' with check-hook metadata; the reference envelope reason is \"User agent '' in global blocklist\" with filter_type metadata. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 2 (user_agent_blocked, security_headers_applied).", - "evt_suspicious_request_untrusted_proxy": "No PHP seam: extracting a client IP behind an untrusted proxy emits nothing in the port; the reference's ip_extraction spoofing seam in extract_client_ip has no analogue. Under the 4.3.2 corpus the divergence is count-level: the corpus pins 0 envelopes (reference bus event construction fails closed without the optional guard_agent module in the generation environment) while the port emits 1 (security_headers_applied)." + "evt_user_agent_blocked_global": "The PHP user-agent block reason is the composed log format 'Blocked user agent: ' with check-hook metadata, and the pipeline appends a trailing security_headers_applied envelope; the reference pins a single user_agent_blocked envelope with reason \"User agent '' in global blocklist\" and filter_type metadata (got 2, want 1).", + "evt_suspicious_request_untrusted_proxy": "No PHP seam: extracting a client IP behind an untrusted proxy emits nothing in the port; the bus carries only the headers-applied envelope where the reference pins a single suspicious_request envelope (ip 203.0.113.17, action spoofing_detected)." } }