Skip to content

Releases: Guard-Core/guard-core-go

v4.4.1

Choose a tag to compare

@rennf93 rennf93 released this 19 Oct 09:50
Immutable release. Only release title and notes can be modified.
4709728

v4.4.1 (2026-10-19)

The quiet spec DEBUG logs release (v4.4.1)

Fixed

  • The spec-DEBUG logs no longer print by default (#110, #111). The no-rules, no-geolocation and loopback-exempt country verdicts (spec 02), the short-circuit Request blocked by <check> line (spec 03, still silenced per check by muted_check_logs) and the untrusted-proxy spoof line for private and loopback peers were written unconditionally on every request. They are now emitted with log/slog at debug level, matching the reference's DEBUG semantics, so they are hidden at the default level. Call slog.SetLogLoggerLevel(slog.LevelDebug) to see them. The blocked-by line now carries check, path and method as slog attributes.

v4.4.0

Choose a tag to compare

@rennf93 rennf93 released this 15 Oct 20:39
Immutable release. Only release title and notes can be modified.
f9c4836

v4.4.0 (2026-10-15)

The reference-alignment release: a per-request scan time budget, the configured threat score threshold, client identity canonicalization, the reference IP list precedence and the dynamic-rules overlay (v4.4.0)

Added

  • Detection.MaxScanSeconds: a per-request cap on regex scan CPU time (#90). The budget spans every field of one request (URL path, params, headers and body values) and every pattern, and is checked between patterns in each regex view pass and before each runtime registry pattern. Default 10.0, valid range 0.1 to 60, zero means the default, NaN and infinities are rejected. Once spent, the remaining patterns and values are not scanned and the request is blocked with a pattern_timeout threat named scan_time_budget_exhausted (category custom, weight 1.0), so padding a request with filler cannot skip detection. The clock is per-thread CPU time (clock_gettime(CLOCK_THREAD_CPUTIME_ID) through golang.org/x/sys/unix on linux, darwin and freebsd), with a wall-clock fallback elsewhere. golang.org/x/sys moves from an indirect to a direct dependency at the same version. A bare Detect call outside a request has no budget.
  • ExcludedSemanticDetectionHeaders: an opt-in semantic-only header exclusion (#88). Header names listed here (case-insensitive, default empty) skip the semantic analyzer only and regex scanning is unchanged. It exists on the global config and on RouteConfig, and the route set merges with the global set.

Fixed

  • Detection.ThreatScoreThreshold is honored (#92). The field was declared but never read, and the engine decided on a hard-coded 1.0. A request is now flagged when the regex anomaly score reaches the configured threshold or any semantic threat exists. Validate rejects values outside 0 to 10, and zero means the default 1.0 (a literal 0.0 is not expressible in Go because it would flag every request).
  • Client identity matches the reference (#99). The identity is canonicalized like the reference: an IPv4-mapped address (::ffff:a.b.c.d), brackets and zones no longer defeat an IPv4 blacklist, whitelist, exempt or emergency entry, or a ban stored for the IPv4 form. The resolved identity is stored on every path, including no trusted proxies and a unix peer. A mapped peer is no longer trusted by an IPv4 trusted-proxy CIDR, a malformed trusted-proxy CIDR makes the whole scan untrusted, and HTTPS enforcement applies the same raw-address rule.
  • Unresolved client address, bypass response pass and rate-limit autoban match the reference (#100). A request with no connection address whose identity stays unresolved is answered 403 Client address could not be determined under FailSecure (on-block hook check name client_address_unresolved) and continues as unknown otherwise. ProcessResponse returns early on an all-checks bypass route outside passive mode, so it collects no metrics and runs no return rules. Rate-limit violations feed EnableRateLimitAutoBan under the shared rate_limit category, trying ThreatBanConfig["rate_limit"] before the flat threshold.
  • IP list precedence follows the reference (#102). A configured global or route whitelist decides alone, and the blacklist is consulted only when no whitelist is configured. A configured route whitelist skips the global lists for the request.
  • Dynamic rule versions apply over the base config, not over the previous version (#96). The retained base snapshot is restored before each new version or different rule is applied, a failed apply rolls back to the previous state, and expiry is unchanged. Emergency mode no longer compounds the AutoBanThreshold reduction per version, and suspicious_patterns the registry already holds are skipped. The global rate limit log line no longer prints a pointer address.
  • Passive-mode suspicious logs keep the reason, and the Cookie header is scanned one name=value pair at a time (#88). A passive line without a trigger reads Suspicious activity detected with its reason instead of the penetration wording. Only spaces around a pair are trimmed, empty pairs are skipped, and each pair counts against the value budget, so an ordinary logged-in cookie header no longer scores as command separators while a payload inside one cookie is still detected.
  • A data race in BoundBodyRead and a load-dependent reach-probe verdict (#94). The slot channel is kept in a local instead of being re-read from the package variable, and the reach-probe samples and load factor are timed on the scan CPU clock, so a busy host no longer flips a safe pattern to unsafe.
  • A value whose threats were all filtered out no longer ends the request scan as clean (GHSA-w9j2-2m3v-5hcc). The detection scan used to return no threat as soon as one value's threats were all dropped by the enabled category set or a route's skipped categories, so a later value (the body, a header, a query parameter) was never scanned and the request passed. A filtered value now falls through to the next value. A semantic-only threat is now reported under its attack type, a result with no categorized threat is reported as uncategorized, and a nil enabled set means every category.

Changed

  • Behavior to know before upgrading. A request that spends Detection.MaxScanSeconds of regex CPU time is now blocked with a pattern_timeout threat instead of being scanned to the end. A set Detection.ThreatScoreThreshold now changes verdicts. Adapters that hand the engine a request with an empty remote address now get a 403 under FailSecure, so set the remote address in the adapter. IPv4 list entries now match clients that connect as ::ffff:a.b.c.d, and an IPv4 CIDR no longer trusts a mapped peer as a proxy. A route whitelist now overrides the route blacklist and skips the global lists, and a global whitelist now overrides the global blacklist, so a deployment that relied on the blacklist beating the whitelist must move the entry. A dynamic rule version no longer carries over the previous version's changes, so a field a version does not set returns to the base value. A passive-mode log match on the old penetration wording needs updating.
  • A request whose first scanned value matches only a disabled or excluded category, and whose later value carries an enabled attack, is now blocked instead of passing.

Docs

  • The configuration and detection pages document the new options, the threshold semantics and the Cookie behavior, and no longer call dynamic rules, agent telemetry, CORS, country lists or global behavior rules unported (#88, #90, #92, #100).

Chore

  • The weekly lint runs on Go 1.26.x, the sponsor button points at the maintainer's Sponsors page, and the mkdocs site_url and README docs links point at the live site root (#82, #84, #85, #86).

v4.3.3

Choose a tag to compare

@rennf93 rennf93 released this 12 Oct 22:52
Immutable release. Only release title and notes can be modified.
944eab7

v4.3.3 (2026-10-12)

Parity release (v4.3.3)

A parity release with no functional change: the engine is tagged 4.3.3 so all five core engines (Python, TypeScript, Rust, Go, PHP) ship the same version.


v4.3.2

Choose a tag to compare

@rennf93 rennf93 released this 09 Oct 07:39
Immutable release. Only release title and notes can be modified.
v4.3.2
2fce792

v4.3.2 (2026-10-09)

The adapter-parity release: the ReDoS static-safety trio, the sus-patterns runtime registry, the custom_response_modifier response pass, the engine websocket guard, the fifteen SecurityConfig knobs with the performance-monitor wiring, and the lifecycle and state surface the adapters drive

guard-core-go v4.3.2 mirrors the guard-core 4.3.2 release: the reference's ReDoS static-safety chain ports verdict-exact onto the engine and takes over the pattern_safety corpus (the registry retires), the sus-patterns runtime registry lands with its telemetry and dynamic-rules application, engine-produced responses gain the custom_response_modifier pass, the websocket guard the adapters expose ports at the engine with suspicious counts shared with the HTTP pipeline, the remaining reference SecurityConfig surface arrives (fifteen knobs plus the performance-monitor wiring), and the lifecycle/state surface the Go adapters drive (manager exports, is_initialized, the cross-instance middleware state registry, MarkInitialized/AgentStats) closes the rows the fastapi-guard parity work left open. The dependency train clears the stdlib-adjacent x/sys exposure, and the post-transfer metadata sweep repoints the repo at the Guard-Core org.

Added

  • The ReDoS static-safety trio, with the pattern_safety corpus going registry-free (#58): a Python-re-compatible pattern parser (guardcore/redos_parser.go + redos_escape.go, sre_parse's group numbering and octal-vs-group-reference disambiguation mirrored) feeding the ordered structural prefilters (redos_structure.go, redos_terminator.go, redos_ambiguous.go), the dangerous-construct gate with the reference's three regexes verbatim (redos_prefilters.go), the reach-probe cost machinery (probe synthesis, interval algebra, class-intersection units, adversarial literal runs, redos_reach_probe.go and friends), stray verification against the compiled engine under a MatchTimeout and the cost arbiter (size ladders, growth-ratio extrapolation to the content cap, load-factor normalization, the measured-override branch) with the fail-closed equivalent of the reference's killable subprocess, and ValidatePatternSafety (redos_compiler.go) reproducing validate_pattern_safety's chain order. go_safety_xfail.json is deleted and its 54 cases come off the registry with matching verdicts and reason classes (94/94 pass, 0 divergences, and the runner fails on a re-added baseline file); a CodeQL-flagged uint32-to-rune narrowing in parseHexEscape is bound-checked before conversion.
  • The sus-patterns runtime registry, pattern events and dynamic-rules application (#59): the SusPatternsManager runtime registry (the reference module singleton as the package default) with safety-validated AddPattern/RemovePattern (custom entries dedupe and persist through the patterns/custom Redis key), the Redis-persisted custom restore on InitializeRedis, DetectPatternMatch (redacted regex identity, the semantic: prefix, the unknown fallback), and the manager's Detect running the shipped corpus scan plus the runtime registry with the reference pattern_detected envelope; SecurityEvent gains the pattern_matched column, the pipeline's penetration detection rides the manager so runtime patterns participate, and the dynamic-rules suspicious_patterns application is real (every pattern runs the registry's safety-validated add, rejections log with the reference honesty). The three sus-patterns event xfails leave the registry (6 to 3, the rest being host error-text renderings).
  • The custom_response_modifier response pass (#60): SecurityConfig.CustomResponseModifier runs the user callback over engine-produced responses before they leave the middleware (a panicking callback is contained with the reference custom_response_modifier raised message and the unmodified response goes out; a nil result keeps the incoming response), wired at the reference call graph's sites (createErrorResponse/errorResponse last on every pipeline error response, the https_enforcement redirect with no header pass, the custom_request check's blocking response in blocking mode), with Engine.ModifyResponse exposing the pass to the adapters for pass-through composition.
  • The websocket handshake guard with shared suspicious counts (#61): Engine.GuardWebSocket runs the reference _run_websocket_checks sequence (identity resolution, the fail-secure unknown-address close, the ban probe, the is_ip_allowed verdict with the whitelist gate and its country-stage skip, the ws-endpoint rate limit, the penetration detection pass) with the six reference close reasons (WebSocketCloseReason, 1008 policy violations and 1013 try-again-later); the engine retains the pipeline's suspiciousCountStore so the websocket detection pass and the HTTP suspicious_activity check key the same per-IP registry, the detection check runs with an inert event stream exactly like the reference's None-handler bus, and Redis containment mirrors _guarded_redis_call (fail-open rate-limit arm, surfaced errors reaching the fail-secure 1013 close).
  • The fifteen SecurityConfig knobs and the performance-monitor wiring (#62): the five redis tuning knobs (socket connect/read timeouts as tri-state pointers, health check interval, pool cap, retries), body_read_timeout + sync_body_read_max_concurrent (every engine-side body read raced against the timeout under a package-level slot budget, the reference daemon-thread join semantics), log_country_check_level (the global country stage's verdict lines), agent_strict + on_error (the agent_init and geoip hook stages with panic containment), detection_pattern_validation_cache_path (the disk-backed cache over the empirical cost-verdict arm, keyed by pattern and flags with a pinned engine version), and the per-request scan budgets (detection_max_scan_values, detection_max_scan_chars, detection_max_json_depth, the _scan_value_budget parity with once-per-request client-named warnings and the JSON walk serializing too-deep containers back to text).
  • The lifecycle and state surface the adapters drive (#63, #64, #65, #66): the manager surface exports (InMemoryCloudIPStore.Clear/RedisCloudIPStore.Clear, GeoIPManager.EntryCount, RedisManager.GetConnection/SafeOperation/Incr/Exists with the redis_error emission on contained failures), GeoIPManager.IsInitialized (the pure predicate that never triggers the lazy load), the cross-instance middleware state registry (the _middleware_state.py port: Engine.Initialize adopts a registered warm state for its (config, decorator) key instead of re-initializing and registers itself after a successful startup, failed startups register nothing, and the websocket detection pass resolves the registered stack's suspicious-counts store before its own), and MarkInitialized/AgentStats with the optional AgentStatsProvider seam (a warmed engine makes Initialize a no-op; agentless answers {enabled: false, degraded}, a wired handler merges its stats dict).

Changed

  • Dependencies: github.com/redis/go-redis/v9 to v9.23.0 (#67), carrying go.uber.org/atomic v1.12.0 and golang.org/x/sys v0.48.0, which clears on the engine side the stdlib-adjacent x/sys exposure the adapters clear through the x/net v0.60.0 bumps; govulncheck stays clean.
  • Post-transfer metadata sweep (7b862bc): repo URLs, the docs site, issue-template links, the ecosystem-gate matrix, mkdocs repo fields, SECURITY advisory links, the CoC contact and the FUNDING github entry point at the Guard-Core org, and .github/CODEOWNERS (* @rennf93) lands so the master code-owner ruleset is enforced. Module paths, Go imports, and the CHANGELOG history line are deliberately unchanged (the canonical import path rename is a separate operator decision; old URLs keep resolving via GitHub redirects).

v4.3.1

Choose a tag to compare

@rennf93 rennf93 released this 07 Oct 01:30
v4.3.1
056c785

v4.3.1 (2026-10-07)

The family-parity release: route-id ownership with per-route behavioral counters, the trusted-proxy XFF chain walk, agent event enrichment, the composite/OTel/Logfire agent handlers, structured JSON logging and the cost-parity detection loop

guard-core-go v4.3.1 tracks the guard-core 4.3.1 engine release from the consumer side: the route-id ownership contract from @HardMax71's report lands (every decorated endpoint owns its route id, and behavioral counters key on it), the trusted-proxy identity resolution walks the X-Forwarded-For chain exactly like the reference instead of keying every verdict on the proxy IP, the agent stream gains the reference enrichment tier and the pluggable composite/OTel/Logfire handlers with structured JSON logging, and the detection loop carries the cross-engine cost-parity program (literal prefilter, rune-API scan migration, fail-closed cost ceilings) with verdicts pinned unchanged.

Added

  • Agent event enrichment: the reference enrichment tier (guardcore/enricher.go, #56): the eight guard.* wire keys (project id, service name, deployment environment, threat score, rule id/version, behavior correlation key plus the recent event count), ThreatScoreFor's deterministic per-type map (default 20), the 300s behavior-correlation window and the sha256(ip|service|bucket)[:16] correlation key, with the enrichment order mirroring the reference strategy chain (identity, threat score, rule correlation, behavior correlation) and events without a metadata map shipping unenriched like enrich_event's early return; CompositeAgentHandler gains the reference's enricher parameter (filter, enrich, then fan out), SecurityConfig the EnableEnrichment/AgentProjectID/OtelServiceName/OtelResourceAttributes block with the reference validation, the installed engine stream wraps the raw handler in the enriching composite with the live DynamicRuleManager as the rule-correlation matcher, and redaction is untouched (the bus still redacts at event build; enrichment only adds derived non-sensitive guard.* keys) (#56).
  • The composite, OTel and Logfire agent handlers plus the adaptation seam (#55): CompositeAgentHandler fans out to N sinks behind the EventFilter mute lists with per-child failure isolation (a failing sink is logged, never propagated), start-degradation tracking (Started/Degraded/FailedHandlers), Stop/Flush fan-out, dynamic rules resolved from the first child that provides them and all-children health; OtelHandler renders security events as guard.event.<type> spans with the guard.* attribute set and W3C traceparent linkage and maps metrics onto the guard.request.duration histogram and the guard.request.count/guard.error.count counters, speaking OTLP/HTTP JSON directly (guardcore keeps its three-direct-dependency rule and owns no global provider state); LogfireHandler emits events as spans and metrics as guard.metric.<type> structured log records over OTLP toward the Logfire API with the reference's already-configured guard reproduced as a per-process claim; and AgentHandlerFunc is the interface-mismatch alignment (guard-agent-go's context-carrying SendEvent and its own SecurityEvent wire type wrap through the seam onto the engine stream unchanged) (#55).
  • Structured JSON logging with the setup_custom_logging parity surface (guardcore/logging_json.go, #55): JsonFormatter renders the reference record shape one JSON object per line (timestamp in the Python asctime format, level, logger, message) while the reference text line stays the default, and SetupCustomLogging(logFile, logFormat) (re)installs the guardcore stream with own-handler replacement semantics, console+file fan-out, the unwritable-path fallback and the restoreStandardLogging escape hatch; SecurityConfig.LogFormat (text|json, validated) and LogFile wire through NewEngine before any manager is built, mirroring the reference middleware's construction-time setup_custom_logging call (#55).
  • The cost-parity detection program (guardcore/literal_prefilter.go, cost_budget_test.go + the 9-case cost_bodies corpus suite, #54): the tempered-loop anchored recon families skip when none of their alternation literals occur in the text (a match-impossible condition, so verdicts cannot change; the extractor is unit-tested against the full pattern table to fire only on the intended shapes), the base64 scan loops migrate to the rune API (convert once, scan runes; the string API re-converted the remaining suffix to runes on every match attempt, quadratic across a scan), and the corpus's self-relative scan-cost ceilings are enforced fail-closed (K=5 x the 8 KiB anchor's linear projection + 250 ms floor; a workload over ceiling fails and an entry listed as over ceiling that returns under it fails too). Measured: 8 KiB 54.1 -> 20.0 ms, 64 KiB 1100 -> 142 ms, 256 KiB 12253 -> 710 ms, 1 MiB 138586 -> 4626 ms; verdicts 193/193 unchanged; the full conformance+cost run drops from 635 s to 25.8 s (#54).

Fixed

  • Route-id ownership and per-route behavioral counters (reported by @HardMax71 in guard-core #140; the reference fix shipped in guard-core #141/#142 and 4.3.1): endpoints built by one decorator factory (or one handler-wrapper table) share one RouteConfig only when they share a route id - the Go registry grants every Routes.Register(routeID, ...) call its own RouteConfig instance, so there is no shared-config last-wins overwrite to fix on this side, and the adapter contract (RequestState.GuardRouteID, the adapters' WithRouteID helpers) pins one id per decorated endpoint. GetEndpointID now resolves exactly like the reference get_endpoint_id: the request's route id wins, then the runtime-provided guard_endpoint_id extra, else METHOD:redacted-path - previously the route id was never read, so behavioral usage/return counters keyed on METHOD:path and collapsed across routes sharing a path shape (#141, #142).
  • The trusted-proxy XFF chain walk (guardcore/ipextraction.go, the ip_extraction.py parity, #55): the reference resolves the client identity through X-Forwarded-For when the direct peer is a configured trusted proxy; this port shipped peer-address-only resolution, so behind a load balancer bans, rate limits, geo rules and behavioral keys all keyed on the proxy IP, and TrustedProxyDepth was validated but never read. Faithful port: CIDR-aware trusted-peer matching (mapped forms resolve through the prefix, bare entries match textually like the reference), the depth-selected entry counting from the right with the right-to-left walk taking over when the depth over-counts hops, chain-shorter-than-depth/malformed/all-trusted chains keeping the peer, the untrusted-peer spoof arm (handler-direct suspicious_request, handler ip_extraction, action spoofing_detected, the redacted display chain at debug level for private/loopback peers), the four once-per-process drift warnings carrying the reference texts, and Engine.Check resolving and caching the identity into RequestState.ClientIP before the pipeline so every consumer (rate limits, bans, geo, behavioral keys, events, block hooks) keys on the resolved client (#55).

v4.3.0

Choose a tag to compare

@rennf93 rennf93 released this 01 Oct 20:12
v4.3.0
4c48eb2

v4.3.0 (2026-10-01)

The corpus-parity release: the pattern_safety, events and redis_interop corpus kinds run against the engine, the spec 12 event stream lands, and the geo country verdict moves to the manager

guard-core-go v4.3.0 tracks the guard-core 4.3.0 safety-corpus release from the consumer side: the conformance runner now consumes every corpus suite kind the reference publishes, the engine's observable event surface is ported and validated against the events corpus (32 of 38 cases reproduce the reference envelopes end to end, the remaining 6 carry narrowed fail-closed baseline reasons), the detection performance monitor, the security-headers Redis cache and the RE2 pattern translation ledger complete the spec parity wave, and the coverage and staticcheck gates are wired fail-closed into CI.

Added

  • The security event bus, metrics collector and dynamic-rules agent pipeline (spec 12's observable event stream) (guardcore/events.go + eventswiring.go, #42): the full 39-event plus 3-metric vocabulary pinned by a vocabulary test including the two documented asymmetries (behavioral_violation, emergency_mode_activated); the reference envelope (timestamp, event_type, ip_address with the unknown fallback, country via the geo resolver, redacted user_agent and endpoint, method, response_time, decorator_type, rule_type, handler_name middleware, metadata with traceparent/tracestate forwarding) with the route-vs-global https_violation dispatch; bus-routed events gated by agent_enable_events and the event mute filter while the handler-direct emitters (ip_ban, rate_limit, behavior, dynamic_rules, cloud, ipinfo) bypass both exactly like the reference handlers, send failures logged and never raised; and the MetricsCollector trio (response_time, request_count, error_rate on >= 400) under agent_enable_metrics. The four detection-engine anomaly/callback names are owned by the performance monitor port with no duplicate declarations (#41). A review-pass fidelity wave then aligned every emission the corpus pins: ip_blocked (filter_type banned) and ip_ban_failed carry the ip_address kwarg, cloud_blocked announces the provider and network metadata with the route-tier block_clouds decorator_violation, the user-agent check splits the route decorator_violation from the global user_agent_blocked reason, the tiered rate-limit events quote the tier's configured limit with the reference route/geo/endpoint reason strings instead of the tripping count, dynamic-rules deliveries validate the reference ge=1 constraints on auto_ban_threshold/auto_ban_duration whole (an out-of-range delivery is rejected with the previous rules left active and an unusable last-known snapshot is skipped in hydration), handler-direct events bypass the mute filter, emergency_mode_block carries the reference event reason, and custom_request_check carries check_function (#42).
  • The detection-engine performance monitor (guardcore/performance_monitor.go, #41): a stdlib-only, concurrency-safe port of the reference monitor family (monitor.py, monitor_types.py, monitor_anomalies.py, monitor_reporting.py). The constructor clamps exactly like PerformanceMonitor.__init__ (anomaly_threshold [1, 10], slow_pattern_threshold [0.01, 10], history_size [100, 10000], max_tracked_patterns [100, 5000], anomaly_emission_cooldown [1, 3600] seconds, min_samples_for_anomaly [10, 1000]); the anomaly trio is the reference's (timeout, slow execution suppressed for timeout samples, and the statistical z-score over the rolling window with the n-1 denominator where the current sample joins the window before the check, one sample able to trip slow plus statistical); RecordMetric parity covers the 100-rune pattern-name truncation with the ...[truncated] suffix, negative-input clamping, timeout samples counted but never admitted to the timing window, first-inserted eviction at max_tracked_patterns and the history/window ring caps; and callbacks receive anomalies with the pattern name run through the ported _redact_pattern_source (escape-decoding, non-token stripping, sensitive-name collapse to [REDACTED]) (#41).
  • The security-headers Redis cache (guardcore/securityheaders_cache.go, #43): the reference SecurityHeadersCacheMixin plus the manager's headers TTL cache. The SecurityHeadersManager holds the resolved header-configuration state with the bounded TTL cache (1000 entries, 300s per-entry expiry, the reference cache-key grammar default / path_ + sha256(lower(path))[:16] with the per-instance prefix) and GetHeaders mirroring get_headers (disabled yields no headers, a hit returns the stored set, a miss computes through the shared responseHeaders builder). The cross-worker sync contract: LoadCachedConfig loads the csp/hsts/custom blocks independently with per-entry custom-header validation (failures log and keep the current state) and CacheConfiguration persists every non-empty block as JSON with the 86400s TTL; a loaded configuration overrides the bound state so the next cache miss picks it up, and Reset purges the cache, restores the bound default, deletes the security_headers:* keys and detaches the handler (#43).
  • The RE2 pattern translation ledger (conformance/pattern_ledger.go + the generated pattern_ledger.json, #40): the specs' default regex-engine direction (RE2 plus recorded translations, the corpus as referee). Every distinct regex pattern firing in the vendored spec 4.1.0 detect corpus (71 patterns) lands in exactly one status: as_is (46, compiles unchanged with stdlib regexp), translated (3, the recorded \Z to \z rewrite with verified_on corpus cases whose recorded match and position the translation reproduces on the raw input) or unmatchable (22, the RE2-blocking constructs with a documented detection limit per entry, not silence). The referee tests recompute every status from the live corpus and fail on an unexpected status change, verify the translated entries still reproduce their corpus evidence, and guard against vacuous passes (#40).
  • The conformance runner consumes the full corpus: the pattern_safety, events and redis_interop kinds (#48): the vendored suites refresh to the reference 4.2.0 stamp with every existing detect and pipeline case byte-identical, and the three new kinds gain runners that drive the real engine and compare per the index comparison blocks. pattern_safety (safety_gates.json, 94 cases) maps the reference verdicts where the Go surface reaches: 29 safe cases pass compile plus bounded execution over the corpus test strings, 11 of 12 compile_failed cases reproduce exactly, and the structural/dangerous-construct/over_budget classes ride the fail-closed baseline with the class divergence tied to the Go source. events (event_stream.json, 39 cases) drives a recording agent handler over the real bus and performance monitor (12 cases reproduced end to end when the runner landed, flipped to 31 by the closures below and 32 by the geo verdict). redis_interop (redis_interop.json, 14 cases) drives the real handlers against live Redis with a loud skip without REDIS_HOST: 10 cases produce byte-identical keys, types, TTL semantics, values and member shapes. All three baselines are fail-closed in the established style: an unbaselined failure is red, a baselined case that starts reproducing the reference verdict is red (stale), and a baselined case that never ran is red. Engine changes the corpus forced, kept minimal: the dynamic rules last-known snapshot dump normalizes nil collections to the pydantic model defaults (restoring the spec 08 byte-equality contract, pinned by a unit test), the exported lifecycle seams GeoIPManager.SetDownloadEndpoint and CloudManager.SetRangeFetcher replace the corpus harnesses' monkeypatch points with unit coverage, and the detect runner and pattern ledger skip non-detect kinds by schema probe (#48).
  • The events surface closed to reference parity (#50): the events runner moves from 12 to 31 of 38 passing, the remaining seven carrying narrowed per-case reasons in the fail-closed baseline. New surface, each mapped against its reference emitter: the SecurityHeadersEventsMixin announces security_headers_applied on cache misses for non-empty paths with a handler attached and ValidateCSPReport gates on the three required fields, redacts the report fields and emits csp_violation (both ride SetAgentHandler and fail soft); RedisManager.Initialize emits redis_connection / redis_error handler-direct with the credential-redacted URL and error_type metadata; the pipeline emits security_bypass on the all-checks bypass, path_excluded on the exclusion matcher (once per path per the reference 300s TTL cache), content_filtered on the request-size and content-type arms, and the untrusted-proxy suspicious_request (spoofing_detected, handler ip_extraction); the identity-violation escalation is ported (a global IP deny that carries an attack payload records its categories in the shared registry, applies the threshold ban with per-category threat entries first then the flat total, and emits the Identity violation escalated penetration_attempt, while a ban failure emits ip_ban_failed and aborts the announcement exactly like the reference exception path, with SetBanFault as the exported deterministic seam); and envelope parity lands (the decorator_type/rule_type kwargs stay in metadata beside the promoted columns per the reference _build_event, behavioral_violation reports the rule action with the rule_type column, rate_limited rides every tripped tier with the full middleware columns and the display_config limit, the route auth denial carries the raw auth reason, and custom_request_check quotes the configured ...
Read more

v4.2.0

Choose a tag to compare

@rennf93 rennf93 released this 27 Sep 18:43
v4.2.0
fdf1556

v4.2.0 (2026-09-27)

The parity release: full parity with guard-core 4.2.0, validated against the conformance corpus (spec 4.1.0)

guard-core-go v4.2.0 is the parity release for the guard-core 4.2.0 engine release: every behavior surface that shipped since v4.1.0 is ported and validated against the shared conformance corpus (guard-core-spec-4.1.0, 219 cases across the five pipeline-kind suites). Note on the 4.1.0 family tags: the 4.1.0 engine tags across the family were a version-accuracy error (the tagged trees did not carry the full advertised surface) and were yanked/unpublished; v4.2.0 is the accurate parity baseline. ### Added - Behavior rules: the reference behavioral surface runs instead of fail-closed rejecting global_behavior_rules (guard_core handlers/behavior_handler.py, core/behavioral/processor.py): SecurityConfig.GlobalBehaviorRules/BehaviorScanResponseBody/BehaviorMaxResponseBodyInspectBytes, per-route BehaviorRules, the BehaviorTracker sliding windows over Redis with the shared key layout, ApplyAction ban/log/throttle/alert dispatch with passive-mode logging and the 3600s ban fallback, usage/return/global-return pattern rules (status:/json:/regex:/substring, correlate_with_detection threshold halving over the shared suspicious-count store), and the new Engine.ProcessResponse for adapters' response-side return rules (#26). - Geo lifecycle and events: the reference IPInfoManager lifecycle (bearer-authenticated download of the free country_asn database with three attempts and 1s/2s/4s backoff, atomic temp-file write, Redis database cache copy with the max-age TTL served before any download, mtime-based staleness, fail-soft initialize/refresh) plus country_blocked, geo_lookup_failed, database_download_failed and the route-level decorator_violation events with reference attribution fields, fired through the new OnGeoEvent hook (#27). - Per-route detection exclusions: RouteConfig.ExcludedDetectionHeaders/Params/BodyFields, EnabledDetectionCategories and DetectionScanBody plus the global DetectionScanBody, resolved through the reference _resolve_* shape (headers always merge over the hardcoded defaults), and the suspicious_activity check applies the per-route enable_suspicious_detection verdict (#28). ### Fixed - 429 responses carry Retry-After from the tier window that tripped, and the rate-limit check resolves the route tier (RouteConfig.RateLimit/RateLimitWindow) alongside the endpoint and global tiers like the reference (#29). - CORS response surface corrections: normal responses compose Access-Control-Allow-Methods/Headers and the reference's 3600 Max-Age, and the wildcard plus credentials combination is accepted at construction and downgraded at policy resolution (credentials dropped, wildcard answers without the allow-credentials header) matching the reference _compute_cors_config (#29, #30). - Passive-mode on_block hook payloads carry the reference log-format reasons with an empty trigger_info, report a null status_code, and the ip_security/deny paths fire inline in passive mode like the reference (#30). ### Conformance - The spec 4.1.0 corpus is vendored (guard-core-spec-4.1.0/cases, byte-identical to the guard-core specs fixtures) with a fail-closed xfail baseline runner (TestPipelineConformance); the 26 baseline entries shipped with the corpus adoption are all cleared: pipeline gate 35 passed, 0 failed, 0 xfail, 0 config divergences (#29, #30).

v4.0.4

Choose a tag to compare

@rennf93 rennf93 released this 23 Sep 23:20
v4.0.4
375d0b9

Full parity with guard-core 4.0.4, binary-noise gates, and the /v4 module path (v4.0.4)

Breaking Changes

  • Import paths now end in /v4. The module path is github.com/rennf93/guard-core-go/v4 and the release tag is v4.0.4; Go modules reject a v4+ tag unless the module path carries the /v4 suffix, so the migration is mandatory for this release to be fetchable. Update every import from github.com/rennf93/guard-core-go/guardcore to github.com/rennf93/guard-core-go/v4/guardcore (and the same for the examples/... internal packages). Installation is now go get github.com/rennf93/guard-core-go/v4@v4.0.4. The earlier v0.1.0 tag was a burned pre-release snapshot of the port and remains on the un-suffixed path.

Added

  • Full check parity with the Python guard-core 4.0.4 engine: 17/17 security checks. The port now covers the complete check catalogue of the Python engine, including the 4.0.x additions.
  • Binary-noise gates for detection, including the SQLi comment-terminator binary gate. Pattern sources prone to firing inside text-decoded binary bodies (PDFs, compressed streams) are registered in a noise-prone registry and gated by binary-density analysis, while genuine ASCII-region matches still detect. The SQLi comment-terminator source joins the registry in this release (parity with upstream guard-core f5d53ca5): the pattern's apostrophe-plus-whitespace-before--- shape routinely matched 0x27 0x0A 0x2D 0x2D-style byte runs inside binary content.
  • Corpus harmonization with guard-core 4.0.3/4.0.4. The conformance corpus tracks the Python engine's fixtures at spec 4.0.3/4.0.4, including binary-body vectors, so the two engines are validated against the same detection expectations.
  • Interop harness at 82/82. The cross-implementation runner (go test -tags interop) executes the Python-engine fixture corpus against the Go engine with full agreement.

Changed

  • Makefile harmonized with the Python guard family. install, test (unit plus -tags integration in docker), lint (gofmt check plus go vet), bump-version (via .github/scripts/bump_version.py, which scaffolds this changelog) and clean match the conventions used across the Python guard repos.