ReviewOS

zig-utils/zig-waf

[WAF-13] Implement rule defaults, removals, updates, markers, and remote rules

#14
Closed chris opened this 9 days ago ·
9 days ago

Objective

Compile the complete ordered rule-configuration layer used by the stable ModSecurity 3.0.16 / Coraza 3.7.0 union: phase defaults, markers, local includes, authenticated remote rule sources, rule removals, action/target updates, duplicate policy, and explicit compatibility handling for missing update targets. All mutation occurs while building a candidate immutable plan; published plans and the request path remain read-only.

Behavioral contract

Ordered source assembly

  • Preserve one deterministic logical directive stream across builder strings, files, Include, IncludeOptional, and remote sources.
  • Local includes retain the WAF-10 canonical-root, cycle, depth, count, size, optional-glob, and source-provenance guarantees.
  • Candidate failure cannot alter the active Waf; publication is atomic after parsing, remote resolution, update application, and validation.

SecDefaultAction

  • Store at most one immutable default snapshot per phase and attach rules only to the snapshot for their effective phase.
  • Require an explicit valid phase and a non-block disruptive action.
  • Reject duplicate phase defaults with a related span to the first declaration.
  • Reject metadata/flow actions and t:none; preserve ModSecurity-compatible ordered transformation inheritance for other t: actions.
  • Rules without phase remain phase 2; a default for another phase never changes their phase.
  • Explicit per-rule actions override singleton defaults while repeatable actions retain specified ordering.

Markers

  • Compile SecMarker into ordered, addressable plan nodes for skipAfter by text or numeric spelling.
  • Preserve duplicate names in source order and resolve to the next reachable marker after the executing rule.
  • Diagnose unresolved static markers during compilation; dynamic macro-bearing references use bounded runtime lookup.
  • Markers execute no operator or action and appear in phase control flow without becoming inspectable rules.

Rule selection and removal

  • SecRuleRemoveById accepts bounded unsigned IDs and inclusive ranges.
  • SecRuleRemoveByTag and SecRuleRemoveByMsg use bounded zig-regex selectors, retaining required ModSecurity regex behavior while documenting Coraza equality-only differences.
  • Removing a chain head removes the chain; selecting a non-head member is diagnosed rather than creating a partial chain.
  • Removed rules leave phase/prefilter runtime indexes while source evidence retains the reason and directive span.

Target updates

  • SecRuleUpdateTargetById, ByTag, and ByMsg compile selectors once and apply additions/removals in directive order.
  • ID selectors support bounded lists and inclusive ranges.
  • Target parsing preserves collection, key, regex-key, count, and negation semantics.
  • Additions append in order; removals exclude matching prior targets without mutating unrelated targets.
  • Updates apply atomically to every selected chain under documented upstream-compatible ownership rules.

Action updates

  • SecRuleUpdateActionById supports bounded ID lists/ranges and parses its action list once.
  • id and phase changes are rejected.
  • Singleton actions replace the prior effective action of their family; repeatable actions append in source order.
  • A disruptive update replaces the prior disruptive action; non-disruptive-only updates preserve it.
  • Transformation updates honor ordered t:none reset semantics and rebuild precomputed pipelines/macros.

Missing targets, duplicates, and compatibility

  • Strict mode is default: duplicate external rule IDs, malformed selectors, missing explicitly named IDs, partial-chain mutations, and invalid updates fail with stable diagnostics and primary/related spans.
  • An explicit builder compatibility option may ignore only upstream-compatible rule-not-found update/removal cases and emits bounded structured warnings.
  • Compatibility mode never suppresses malformed syntax, unsafe remote policy, duplicate IDs, allocation/resource failures, or invariant violations.
  • No directive is silently accepted without an effect.

Authenticated remote rules

  • SecRemoteRules KEY HTTPS_URL resolves only during candidate construction through an injected async-capable fetch interface; transactions perform no network I/O.
  • URLs remain credential-free HTTPS and pass explicit host/IP/redirect policy, DNS-rebinding protection, timeout, byte, directive, and redirect limits.
  • The key is sent only in the designated authentication header and is redacted from diagnostics, logs, fingerprints, and telemetry.
  • Results record final URL, content digest, retrieval metadata, and provenance; identical pinned content may use a bounded cache.
  • Abort fails the candidate. Warn records a structured warning and continues without substituting stale or empty content unless an explicit pinned-cache policy allows it.
  • The transport interface is ready for WAF-42 zig-tls integration; tests use deterministic in-memory transports.

Public and plan interfaces

  • Immutable typed rule-selection/update IR with stable source spans and bounded counts/bytes.
  • Plan views for effective rules, removal evidence, marker resolution, and configuration warnings.
  • Builder limits for selectors, intervals, updates, expansion, markers, remote responses, redirects, and warnings.
  • Explicit strict/compatible missing-rule policy and remote host/redirect/cache decisions.
  • Stable diagnostics must not renumber WAF-PLAN-0101 through WAF-PLAN-0108.

Deliverables

  • Phase-scoped default validation and snapshots.
  • Marker execution-plan integration and static resolution.
  • ID interval parsing/indexing and removal application.
  • Tag/message zig-regex selectors and removals.
  • Target update compiler/application for ID/tag/message selectors.
  • Action update compiler/application with singleton/repeatable semantics.
  • Explicit missing-rule compatibility mode and structured warnings.
  • Authenticated bounded HTTPS remote-source loader and explicit no-stale cache policy.
  • Immutable effective-plan/evidence APIs and fingerprint coverage.
  • Focused allocation-failure, hostile-input, differential, fuzz, corpus, and benchmark tests.
  • Documentation and machine-readable compatibility evidence.

Acceptance criteria

  • Every scoped directive has positive, negative, ordering, allocation-failure, and resource-limit coverage.
  • Differential fixtures cover pinned ModSecurity and Coraza behavior; intentional union choices are named and executable.
  • CRS 4.28 setup/exclusion streams compile with no unexplained skips.
  • Candidate failures leave the active generation unchanged and caller-owned inputs valid.
  • Published plans require no update parsing, regex compilation, remote/filesystem/database access, or allocation on the request path.
  • Benchmarks record compile cost, plan size, and reload pause for CRS with and without exclusion/update overlays.
  • Focused tests, full zig build test, zig build check, corpus gates, formatting, and hosted CI pass on the exact closing SHA.
  • Work lands directly on main in small conventional commits with issue/commit/run links.

Relationships

Baselines and upstream references

  • ModSecurity 3.0.16: seclang-parser.yy, rules_exceptions.cc, default-action and config-update regressions.
  • Coraza 3.7.0: internal/seclang/directives.go, rule_parser.go, directive engine tests.
  • OWASP CRS 4.28.0: setup and rule-exclusion corpus.
  • Zig 0.17-dev locked through Pantry; no Git submodules.
9 days ago

Dependency WAF-10 #11 is complete at e72709c with hosted green evidence: https://github.com/zig-utils/zig-waf/actions/runs/29942330646. This issue is now unblocked on the bounded, owned SecLang frontend.

9 days ago

Unblocked by completion of #13 at 8ee46c3, qualified by Actions run 29950372566. WAF-13 implementation is now active; it will consume the ordered typed directive configuration and preserve compile-time-only policy mutation.

9 days ago

Published the first WAF-13 slice on main: 52bcd42 (feat(seclang): scope default actions by phase).

This replaces the incorrect global-last-default state with one immutable snapshot per phase, keeps unphased rules in phase 2, attaches only the matching phase snapshot, rejects duplicate phase defaults with a related source span, requires a non-block disruptive action, rejects metadata/flow actions and t:none, and adds stable diagnostics WAF-PLAN-0109 through 0111 without renumbering 0101–0108.

Local gates on the commit: 132/132 tests, zig build check, formatting, and git diff --check passed. Hosted CI is now running for this exact SHA.

9 days ago

Published reusable bounded ID selector infrastructure at a2fb7e2 (feat(seclang): compile bounded rule id selectors). It is shared groundwork for remove-by-ID, target-by-ID, and action-by-ID: canonical sorted interval sets, overlap/adjacency merging, full-u64 overflow checks, exact failure token coordinates, independent resource limits, binary-search matching, and allocation-failure safety. Local gates: 136/136 tests and zig build check passed.

9 days ago

Published compile-time SecRuleRemoveById application at 790743d (feat(seclang): apply remove-by-id at compile time). Valid selectors now filter immutable phase indexes, chain-head removal marks the entire chain, direct non-head selection fails as WAF-PLAN-0113 with a related rule span, and source-addressable removal evidence remains available for explain/replay. Structural compiler ABI advanced to 3.

Local qualification: 139/139 tests, zig build check, five-root pinned structural corpus (58 files, 925 directives, 761 rules, zero failures), and ordered CRS 4.28 configuration (30 files, 794 directives, 695 rules) passed.

9 days ago

Published ModSecurity-compatible regex removals at 319d12f (feat(seclang): apply regex rule removals). SecRuleRemoveByTag and SecRuleRemoveByMsg compile with production zig-regex once per operation, match explicit metadata, preserve directive ordering/evidence, reject invalid patterns as WAF-PLAN-0112, and never retain a matcher on the request path. Coraza case-sensitive equality remains a subset and is documented.

Qualification: 141/141 tests, zig build check, five-root pinned corpus (58 files/925 directives/761 rules), and ordered CRS 4.28 (30 files/794 directives/695 rules) passed.

9 days ago

Published marker execution-plan linking at 7c4a58c (feat(seclang): resolve skip-after markers in plans). Static skipAfter values resolve to the next same-name marker and first executable same-phase chain head; duplicates preserve source order. Macro-bearing values retain allocation-free runtime lookup. Cross-file targets remain explicit unresolved records in component plans, while final Waf.Builder validation reports WAF-PLAN-0114 and rejects publication until the ordered source assembly resolves them.

Qualification: 145/145 tests, zig build check, five-root pinned corpus (including CRS 950→959 cross-file flow), and ordered CRS 4.28 passed.

9 days ago

Published all three target-update selectors at e2cfd15 (feat(seclang): materialize rule target updates). ID intervals and tag/message zig-regex selectors now produce ordered immutable effective target ranges; additions/exclusions preserve collection, selector, count, and negation IR. Invalid syntax is WAF-PLAN-0115, non-head selection remains WAF-PLAN-0113, and max_target_expansion independently bounds overlay amplification.

Qualification: 147/147 tests, zig build check, five-root pinned corpus, and ordered CRS 4.28 passed.

9 days ago

Published SecRuleUpdateActionById at ebfe534 (feat(seclang): materialize rule action updates). Bounded ID lists/ranges select chain heads; disruptive/log/audit/same-name singleton families replace correctly; repeatable transformations/tags/setvar/ctl families append in order; id, phase, and chain are rejected as WAF-PLAN-0116; macros and t:none-aware transformation pipelines are rebuilt. max_action_expansion bounds overlay amplification.

Qualification: 149/149 tests, zig build check, five-root pinned corpus, and ordered CRS 4.28 passed.

9 days ago

Published explicit missing-rule policy at 3f0db0a (feat(seclang): enforce missing rule policy). Component plans retain every unmatched requested ID/range as a typed MissingRuleReference with directive and operation kind. Final Waf.Builder publication is strict by default (error.MissingRuleReference); callers must opt into .compatibility, and warnings remain immutable and fingerprinted. Syntax, duplicate, chain, marker, allocation, and resource errors are never suppressed.

Qualification: 151/151 tests, zig build check, five-root pinned corpus, and ordered CRS 4.28 passed.

9 days ago

Published the secure transport-neutral remote loader at ddaa19e (feat(seclang): add bounded remote rule loader). It enforces credential-free HTTPS, separate auth material, mandatory per-destination/address policy, response address evidence, status/final-URL/redirect/body limits, explicit Abort/Warn outcomes, no empty/stale substitution, BLAKE3 provenance, full ownership cleanup, and redacted typed warnings. OOM always propagates.

This completes the loader contract but not the issue checkbox yet: the next slice inserts accepted sources into the ordered parser registry at each SecRemoteRules position and records Warn outcomes. Local gates: 155/155 tests and zig build check.

9 days ago

Published ordered remote/local source assembly at 419f7db (feat(seclang): assemble remote rules inline). Accepted remote bytes become child sources at the exact SecRemoteRules span; compileTree now traverses both local includes and remote children in textual directive order. Plans retain source IDs, BLAKE3 digests, provenance spans, and Warn records. Remote/local child defaults, updates, chains, and markers can therefore cross source boundaries correctly. Structural fingerprints now use fixed markers for SecRemoteRules, SecHttpBlKey, and SecHashKey secrets.

Cache policy is explicitly no-stale: Warn produces no source, and no implicit cached/empty content is substituted. A future opt-in pinned-content cache can be added without changing this safety default. Qualification: 160/160 tests and zig build check passed.

9 days ago

Published d5068c9 (test(seclang): fuzz rule configuration overlays) on main. The plan fuzz oracle now validates removal provenance, phase-index exclusion, marker/resume bounds, and missing-reference evidence, with focused seeds for removals, target/action overlays, skip-after markers, unresolved references, and remote directives. Local gates: 160/160 tests and 10,000 deterministic plan-fuzz iterations.

9 days ago

Published 9bb352b (perf(seclang): benchmark rule overlays) on main. bench-plan now measures the same ruleset with and without concrete removal/target/action overlays, including compile time, plan bytes/delta, removal evidence, missing references, and baseline/overlay publication latency. Pinned CRS 4.28 SQLi benchmark: 73 rules, 77 overlay directives, 1 applied removal, 0 missing references, 289,232 overlay bytes (+1,663), 10 ns/reload observed on this run.

9 days ago

Published e0e667e (test(seclang): pin rule configuration differentials) and 7d903f0 (docs: record rule configuration qualification) on main. Four executable fixtures now pin phase-default, removal/target/action overlay, next-marker, and exact missing-reference semantics to ModSecurity 3.0.16, Coraza 3.7.0, and CRS 4.28.0 source cases. Embedded evidence validates compiler ABI 3, exact upstream commits, zero unexplained mismatches, corpus/fuzz counts, and baseline/overlay metrics. Local unit gate: 165/165; zig build check passes.

9 days ago

Issue-boundary local qualification on closing candidate 7d903f0: 165/165 unit tests; zig build check; 58-file five-root corpus (803,184 bytes, 925 directives, 761 rules); ordered CRS 4.28 (30 files, 771,763 bytes, 794 directives, 695 rules, fingerprint 86d51eedfe51c77b85de8ea9b453bc92063208d6b77733ea5f2f1ee64781856b); parser, plan, and directive 10,000-iteration fuzz gates; ReleaseFast CRS overlay benchmark (73 rules, 1 removal, 0 missing references, +1,663 plan bytes, baseline/overlay publication 11/10 ns observed). Worktree is clean. Awaiting exact-SHA hosted CI run 29954254127 before closure.

9 days ago

Closed by exact SHA 7d903f024bd042b360cbb85ba54d0bc179f1a06d. Hosted CI run 29954254127 passed the full 165-test suite, compile/check boundaries, five-root and ordered CRS corpora, stable inventory/configuration gates, all parser/plan/directive fuzz gates, and all benchmarks. All deliverables and acceptance criteria are satisfied with zero unexplained differential mismatches.

Sign in to comment on this issue.