Repository navigation
Releases: Guard-Core/guard-core
Releases · Guard-Core/guard-core
Release list
v4.3.3
v4.3.3 (2026-10-12)
Parity release (v4.3.3)
A parity release with no functional change: the engine moves to 4.3.3 so all five core engines (Python, TypeScript, Rust, Go, PHP) ship the same version.
The release workflow now fails before publishing unless the release tag matches the pyproject.toml version.
The deptry configuration now lists run_fixtures (the specs fixture tooling module) as first party, which clears the DEP001 report on specs/fixtures/tools/generate_cost_cases.py.
v4.3.2
v4.3.1
v4.3.1 (2026-10-06)
The route-id ownership release: factory-built endpoints get their own route ids and behavioral counters, and the cost-parity corpus joins the CI-enforced parity contract (v4.3.1)
Fixed
- Endpoints built by one decorator factory share one RouteConfig, last one wins (guard-core #140, #141, @HardMax71). The route id was
module.qualname, so endpoints built by one decorator factory shared a single RouteConfig and the last decorator call silently won._get_route_idnow reuses a function's_guard_route_idwhen present, otherwise dedups with#2,#3and so on, and_ensure_route_configstamps the id on the function right away so the next decorator in a stack finds it.functools.wrapswrappers join the config of the function they wrap. - Route-id ownership validation (guard-core #142). A stamped
_guard_route_idis honored only within the decorator instance that bound it, or for wrappers of the same route via the__wrapped__chain - a colliding id stamped by a different decorator instance on a same-qualname sibling can no longer select that sibling's config. - Behavioral counters are per-function for factory-built endpoints (guard-core #142).
BehavioralProcessor.get_endpoint_idnow prefersstate.guard_route_idover the legacymodule.qualnameguard_endpoint_id, so the usage/return counters and pattern state stop mixing across same-qualname siblings on all four adapters (the adapters already stampguard_route_id; no adapter changes needed). Decorated endpoints' hashed Redis counter keys change shape, so pre-existing counters age out through their TTLs.
Added
- The cost-parity corpus: the 9-case cost_bodies suite with self-relative scan-cost ceilings - large-body verdict parity across the truncation cap plus self-relative scan-cost ceilings, CI-enforced on every engine. The family's verdict-and-cost parity contract is now machine-checked on every push (guard-core #142, and the engine/lane PRs in the four sibling repos).
Test
- The lazy-init timing assertion carries load headroom (a saturated runner measured 0.538s against the old 0.5s bound; rerun green), and run_fixtures.py no longer drops pattern_safety suites into the detect checker.
v4.3.0
v4.3.0 (2026-10-01)
The safety-corpus release: the pattern-safety chain, the event stream and the Redis interop surface are pinned by the corpus, and boot validation can be cached (v4.3.0)
Added
- The corpus is now the full safety-and-surface parity suite: 327 cases across 20 suites (guard-core #132, #134).
specs/fixtures/cases/grows from 219 cases in 17 suites to 327 cases in 20 with three new kinds, every expected value produced by executing the reference engine and kept only when decisive and stable across repeated oracle runs.safety_gates.json(kindpattern_safety, 94 cases, #132) pinsPatternCompiler.validate_pattern_safetyacross nine decision classes in both modes: the three dangerous backtracking constructs, compile failures, all five structural detectors (nested unbounded, adjacent broad, unreachable terminator, literal absorb, ambiguous optional tail), pure over-budget extrapolation, and the safe verdict; cases pinsafeplus a stablereason_classtoken and never the host-measured numeric reason text, and the probe-timeout class is excluded by design because it is host-speed dependent.event_stream.json(kindevents, 39 cases, #134) pins all 39 event types' full envelopes through a capturing agent handler with the volatile fields (idempotency_key,timestamp, response/execution timings) dropped recursively per the index comparison contract, plus one honest xfail (decoding_errorguards stdlib decoders that never raise onstr).redis_interop.json(kindredis_interop, 14 cases, #134) pins the byte-level Redis surface of section 08 per key family: exact keys with the sha256 composition, exact static values, zset member/score shapes, canonicalized JSON blobs, and TTL presence plus value.specs/fixtures/tools/generate_safety_gates.pyships the bounded generator (harvested pool via the fast test-strings mode, curated per-class triggers via both modes, eight workers, a 90 second per-call alarm) alongside the existing pipeline generator; both new generators replay byte-identical. detection_pattern_validation_cache_path: an opt-in disk cache for the pattern-validation cost verdict (guard-core #135, #136). When set,PatternValidationCachepersistsvalidate_pattern_safety's empirical cost-verdict outcome (probe synthesis plus timed subprocess probes) keyed by pattern, flags, and engine version, so a process boot reuses prior certifications instead of re-timing every custom pattern; the cheap deterministic layers (dangerous constructs, compile check, structural detectors) always re-run, explicittest_stringsvalidation bypasses the cache, entries produced by a different engine version are ignored and overwritten, writes are atomic (tempfile plusos.replace), and a corrupt or unreadable cache starts empty with a warning instead of failing the boot. Wired throughPatternCompiler(validation_cache=...), the suspatterns detection state builder and the dynamic-rule application paths. Left unset (the default) every validation stays fully empirical. The motivating incident: on a host with degraded subprocess spawn latency, startup validation of ~100 patterns turned into minutes of killable-subprocess timeouts and wholesale fail-closed rejection.
Fixed
- The corpus tool harnesses pass full-workspace mypy (guard-core #137). The events and redis-interop generators' duck-typed doubles (GuardRequest-shaped pipeline stubs, monkeypatched handler attributes, the aiohttp
ClientSessionreplacement, lambdas assigned onto manager seams) carry the narrow ignore codes mypy requests, and the hex-escape attack-indicator regex parity was re-verified against the reference's two-character escaped-backslash form. specs/tooling is ruff-format stable under the pinned hook versions (guard-core #138): the safety-gates generator's 10 line-normalization diffs are landed so pre-commit's ruff-format hook stops failing every open PR.
v4.2.0
v4.2.0 (2026-09-27)
The parity release: the conformance corpus tracks the engine release (v4.2.0)
Added
- The conformance corpus is the 4.2.0 parity surface (guard-core #124, #125).
specs/fixtures/now carries the full corpus inside the reference repo: 219 cases across 17 suites at spec 4.1.0. The 12 detect suites (184 cases) regenerate byte-identical against the vendored expectations (zero detect drift) and five newkind=pipelinesuites (35 cases) pin the pipeline-level parity surface through the realSecurityCheckPipelineover a redis-free middleware stub: ip control (whitelist/blacklist/exempt_ips precedence, blacklist beats exemption, geo country block and allowlist, route ip/UA restrictions), rate limits (global 429 withRetry-After, endpoint tier independence, route tiers, exempt skip, passive mode), detection response (the 400 contract body, custom error responses,logged_onlyevents, on_block payloads, route detection exclusions), headers and CORS (default/custom/disabled security headers on error responses, origin echo, wildcard plus credentials block) and behavioral rules (global return_pattern rules driving the in-memory behavior ban with passive-mode suppression).run_fixtures.pyreplays both suite kinds and the generator records kind and consumers per suite so the rust detect-only runner and the go/php/ts pipeline runners load the right files; the corpusengine_versionstamp now tracks this release, so219 cases match engine 4.2.0is a truthful statement about the shipping engine.
v4.1.0 (2026-09-26)
Exempt_ips skip-list, recon leading-separator gate, raw-view recon scan and the cross-engine interop harness (v4.1.0)
Added
exempt_ips: a skip-list for trusted automated clients (guard-core #117, #118). IPs and CIDRs listed in the new field skip rate limiting, the user-agent check and per-route@block_cloudsexactly as a whitelist match does, without restricting anyone else. An exempt match sets the request's exempt flag only after the blacklist and dynamic-ban checks pass, so the blacklist, dynamic bans, per-route IP rules, penetration detection (including its violation counting and auto-ban contribution) and the globalblock_cloud_providerslist still apply to exempt IPs; thewhitelistdeny path is byte-identical to before; and no new deny path is introduced anywhere. Entries validate fail-closed at construction exactly likewhitelist(IPv4, IPv6 and IPv4-mapped forms), a/0entry warns that it exempts every address, the field participates in the sensitive-output redaction, and the live-smoke suite gained an access-control scenario for it.- Cross-engine interop harness (guard-core #120, #122, #123).
interop/now carries the tooling that keeps the Go, PHP, Rust and TypeScript engines verdict-identical to this one: the Redis-state phases (rate-limit state written by one engine and read across the others), binary-body and body-surface detect vector suites with per-vector contexts, a five-family body-extraction differential over hundreds of generated bodies, middleware-level spot checks (an attack smuggled inside a binary upload's printable islands must block and?system=SAPmust pass in every family), and the exempt_ips state phase (an exempt client stays unthrottled over one shared Redis state while a non-exempt client is blocked at the same crossing, and blacklist beats exemption). Raw reports land ininterop/reports/.
Fixed
- Bare query and body values such as
?system=SAP,?mode=defaultorREADME.mdno longer count as recon probes (guard-core #115, #116). Whole-value recon path rows whose leading path separator is optional (the extension-path row plus the product/config/doc rows such asdefault,sap,actuator,cgi-bin,README.md,credentials.json) required the matched value to start with/or\when scanned inquery_paramandrequest_bodycontexts (embedded JSON leaves included);url_pathandunknowncontexts are unchanged. Separator-prefixed probes (/default.asp,/actuator/health,\README.md) still detect and still feed violation counting and auto-ban; the set of affected rows is derived from the pattern table by the same optional-separator anchor rule the engine ports copy. - Backslash-prefixed recon probes such as
\defaultwere invisible in every configured pipeline. The preprocessing pipeline's LDAP hex escape decoder folds\desequences into single characters before the pattern tables run, so a query or body value like\defaultarrived at the recon rows asÞfaultand matched nothing; only the deprecated legacy unconfigured singleton (no preprocessor, raw values) saw them. The recon-category rows are now also scanned against the signal-preserving raw view (unicode normalization only, backslashes intact) in addition to the processed views, via a newDETECTION_RECON_RAW_VIEW_PATTERN_SOURCESset derived from the pattern table; the #116 leading-separator gate applies to raw-view matches exactly as everywhere else, so bare words stay innocent in query and body contexts, separator-prefixed probes detect again, url_path and unknown contexts are unchanged, and a row matching both the processed and the raw view on the same text is counted once instead of doubling the threat score. This is the reference fix for the cross-engine divergence documented in the 2026-09-25 detection audit; the Go, PHP, Rust and TypeScript ports copy the same view-membership change.
v4.1.0
v4.1.0 (2026-09-26)
Exempt_ips skip-list, recon leading-separator gate, raw-view recon scan and the cross-engine interop harness (v4.1.0)
Added
exempt_ips: a skip-list for trusted automated clients (guard-core #117, #118). IPs and CIDRs listed in the new field skip rate limiting, the user-agent check and per-route@block_cloudsexactly as a whitelist match does, without restricting anyone else. An exempt match sets the request's exempt flag only after the blacklist and dynamic-ban checks pass, so the blacklist, dynamic bans, per-route IP rules, penetration detection (including its violation counting and auto-ban contribution) and the globalblock_cloud_providerslist still apply to exempt IPs; thewhitelistdeny path is byte-identical to before; and no new deny path is introduced anywhere. Entries validate fail-closed at construction exactly likewhitelist(IPv4, IPv6 and IPv4-mapped forms), a/0entry warns that it exempts every address, the field participates in the sensitive-output redaction, and the live-smoke suite gained an access-control scenario for it.- Cross-engine interop harness (guard-core #120, #122, #123).
interop/now carries the tooling that keeps the Go, PHP, Rust and TypeScript engines verdict-identical to this one: the Redis-state phases (rate-limit state written by one engine and read across the others), binary-body and body-surface detect vector suites with per-vector contexts, a five-family body-extraction differential over hundreds of generated bodies, middleware-level spot checks (an attack smuggled inside a binary upload's printable islands must block and?system=SAPmust pass in every family), and the exempt_ips state phase (an exempt client stays unthrottled over one shared Redis state while a non-exempt client is blocked at the same crossing, and blacklist beats exemption). Raw reports land ininterop/reports/.
Fixed
- Bare query and body values such as
?system=SAP,?mode=defaultorREADME.mdno longer count as recon probes (guard-core #115, #116). Whole-value recon path rows whose leading path separator is optional (the extension-path row plus the product/config/doc rows such asdefault,sap,actuator,cgi-bin,README.md,credentials.json) required the matched value to start with/or\when scanned inquery_paramandrequest_bodycontexts (embedded JSON leaves included);url_pathandunknowncontexts are unchanged. Separator-prefixed probes (/default.asp,/actuator/health,\README.md) still detect and still feed violation counting and auto-ban; the set of affected rows is derived from the pattern table by the same optional-separator anchor rule the engine ports copy. - Backslash-prefixed recon probes such as
\defaultwere invisible in every configured pipeline. The preprocessing pipeline's LDAP hex escape decoder folds\desequences into single characters before the pattern tables run, so a query or body value like\defaultarrived at the recon rows asÞfaultand matched nothing; only the deprecated legacy unconfigured singleton (no preprocessor, raw values) saw them. The recon-category rows are now also scanned against the signal-preserving raw view (unicode normalization only, backslashes intact) in addition to the processed views, via a newDETECTION_RECON_RAW_VIEW_PATTERN_SOURCESset derived from the pattern table; the #116 leading-separator gate applies to raw-view matches exactly as everywhere else, so bare words stay innocent in query and body contexts, separator-prefixed probes detect again, url_path and unknown contexts are unchanged, and a row matching both the processed and the raw view on the same text is counted once instead of doubling the threat score. This is the reference fix for the cross-engine divergence documented in the 2026-09-25 detection audit; the Go, PHP, Rust and TypeScript ports copy the same view-membership change.
v4.0.5
Printable-run islands release.
Fixed
- Large binary file uploads still produced pattern matches whose rate grew with file size: a binary-dense multipart file-part payload (binary artifact characters filling at least a fifth of it) is now reduced to its printable runs before scanning, keeping only runs that reach the new
detection_binary_min_run_lengthfield (default 16) and scanning each retained run as its own scan value, so no pattern can match across the binary bytes that separate two runs. Compressed data produces almost no 16-character printable runs, so a large archive yields no scannable content and the false-positive rate stops growing with file size (observed with a 373 MB zip containing an Acrobat installer matching the LDAP paren-breakout pattern). - Text uploads, short and mostly-text payloads stay below the artifact ratio and keep their full scan; text parts without a filename are never reduced; whole-body fallback scans are never reduced, so raw-body signature coverage (pickle opcodes at scan-window boundaries, UTF-16/32 payloads routed through the wide-encoding preprocessor, null-byte LDAP shapes) stays intact; file names, multipart field names, and part headers are scanned regardless; and text genuinely embedded in a binary-dense upload (padded webshell code runs, scripts inside PDFs, stored paths in archives) forms runs past the threshold and is still detected.
See CHANGELOG.md for details.
v4.0.4
v4.0.4 (2026-09-23)
Binary-noise follow-up: SQLi comment-terminator gate, console-safe detection logs, depth-capped display redaction (v4.0.4)
Fixed
- PDF and text-decoded binary uploads were flagged as SQLi. The SQLi comment-terminator pattern source (an apostrophe followed by a whitespace run before
--or#at end of line) matched byte runs such as 0x27 0x0A 0x2D 0x2D inside binary-decoded content (PDF comment lines, compressed streams). The source had no semantic validator and was absent fromNOISE_PRONE_PATTERN_SOURCES, so the 4.0.3 binary-density gate did not cover it. It is now gated: matches in binary-dense regions are discarded, ASCII-region matches still detect, and the truthful-registry test gains an exemption for trigram-shaped sources that cannot occur in pure random noise. - Detection log lines could raise UnicodeEncodeError on Windows cp1252 consoles (observed on Python 3.14) when raw body previews carried non-ASCII bytes.
_sanitize_for_lognow emits pure ASCII:\xNNfor surrogate-escaped bytes,\uXXXXfor every other non-ASCII character. - Deep-nested JSON in headers produced huge half-redacted log values. When the JSON display-redaction depth cap trips, the whole value now collapses to
[REDACTED](detection and body scans keep their capped-subtree semantics).
v4.0.3
v4.0.3 (2026-09-21)
Binary upload false positives: the sus-pattern engine no longer blocks text-decoded binary bodies (v4.0.3)
Fixed
- Every binary file upload was blocked and its client IP auto-banned. A multipart file or zip upload decodes to text full of artifact bytes, produced 3 to 7 spurious pattern matches on random data from a small family of low-specificity shell-source heuristics (backtick pairs, dollar substitutions, quote splice, glob wildcards, template fragments), crossed the anomaly threshold, and banned a real user for uploading zips. Matches from those noise-prone patterns, and only those, are now discarded when the window of 64 characters on each side of the match holds 4 or more binary artifact characters (control characters other than tab, newline and carriage return, DEL, Latin-1/Latin-Ext-A artifact bytes outside a small text allowlist, surrogateescape bytes, and the Unicode replacement character). The check is an O(1) prefix-sum difference built once per scanned string in both the async and the sync tree, applied after the existing candidate rejection validators, with a frozen registry and a noise-corpus honesty test keeping the set minimal. Signature patterns are never gated, so real attacks hidden in binary padding (padded webshells, pickle opcode streams, base64-fragmented multipart parts) are still caught, while pure text, accented European text and all non-Latin scripts keep unchanged behavior.
v4.0.2
Bounded built-in pattern matchers, ReDoS validator hardening, and a load-scaled verdict deadline (v4.0.2)
Fixed
- Several built-in suspicious-content matchers were quadratic. The file-upload, template-engine, XML/XXE, pickle-global, cmd-injection, and load-file builtins now run as bounded scan-window matchers with the same detection language, verified by differential regressions and bounded timing checks in both trees.
- The ReDoS validator missed a quadratic shape. Quantified classes now pair across any run of atoms that can match empty, character sets are exact interval sets over the full code space, membership is decided by the regex engine on the compiled node, and builtins are certified with the compile flags they ship with.
- The sqli comment-terminator builtin was quadratic on a quote followed by whitespace.
'\s*[\);]*\s*--is now'\s*(?:[\);]+\s*)?--, the same language, linear on that input. - The validator's own probe timing could exceed its wall-clock budget. Probes below the noise floor take a single sample, a stride sample bounds exploding enumerations, and the cmd_injection backtick builtin validates in about six seconds instead of twenty-two.
- The verdict deadline was a fixed 40 seconds regardless of host load. It now scales by the measured load factor, floored at idle-host behavior and ceiled at 240 seconds, so a genuinely quadratic pattern is verdicted on a loaded runner instead of rejected with a timeout.
Changed
- Anomaly detection skips the variance computation when the latest execution time is at or below the rolling average.
- CI and test instrumentation. The
redos_timinggate runs as four shards with one assembled PR comment, the ecosystem gate uploads its pytest output regardless of outcome, the sync check propagates generated-code check failures, CPU-bound budgets measure minimum process CPU time, and guard_core logger levels are restored after every test.
What's Changed
- fix(detection): class-intersection probe pairs across empty-capable atoms; linear sqli comment terminator; CPU-time test budgets; logger level isolation by @rennf93 in #98
Full Changelog: 4.0.1...4.0.2