Documentation
Runtime Enforcement of Validation Library
This document previously described an aspirational design — a mandatory
compile_with_validation() wrapper around a fictional @adguard/validation
package, with cryptographic signatures proving validation occurred. That
design was never built, and no compiler ever depended on @adguard/validation
or any other AdGuard-owned package. It has been replaced below with what
actually ships.
For the full per-language integration points, the opt-out flags, and the CI
gate, see docs/VALIDATION_ENFORCEMENT.md — this document only covers
the "how is bypassing actually prevented at runtime" question.
What actually enforces validation
There is no separate wrapper function, signature, or audit-log format. Enforcement lives directly in each compiler's own compilation pipeline, at the point where output is about to be reported as successful:
- Rust:
compile_rules()insrc/compilers/rust/core/src/compiler.rs— the function the shippedbloqr-compilerCLI calls — runs the validator against the just-written output before returningOk. - .NET:
BloqrCompilerService.RunAsyncCoreinsrc/compilers/dotnet/src/Bloqr.Compiler.Dotnet/Services/BloqrCompilerService.cscallsValidateOutputSyntaxAsyncas part of its own run, before returning a successfulCompilerResult. - TypeScript:
runCompiler()insrc/compilers/typescript/src/orchestration/compiler.tscallsrunRulesValidator()afterhostlistCompiler.compile()and before reporting success. - Python:
BloqrCompiler.compile()/compile_async()insrc/compilers/python/bloqr_compiler/compiler.pycall_run_rules_validator()the same way. - PowerShell:
Invoke-BloqrCompilerinsrc/compilers/powershell/BloqrCompiler/Public/Invoke-BloqrCompiler.ps1callsInvoke-RulesValidatorbefore constructing a success result.
In every case, the function that runs the validator returns a
can-continue/should-abort decision (or raises/throws), and that decision is
on the direct path to the value the compiler returns — there's no
intermediate "trust me, it validated" flag a caller could set without the
validator actually having run. The default, in all five, is fail closed:
a validator that can't be found or fails to run is treated the same as a
validator that found errors — the compilation aborts. The one way to change
that is the explicit allow_unvalidated_output-style flag documented in
docs/VALIDATION_ENFORCEMENT.md, which is off by default and logs a warning
whenever it's used.
Since #434, each of these call sites also selects an engine (dns or
browser) matching which artifact it's validating — the browser-syntax
artifact validates against the browser grammar, the DNS/server-side
artifact against the DNS grammar. This is a grammar choice, not a laxer
enforcement mode: both engines are still fail-closed by the same mechanism
described above, and picking the wrong engine for an artifact is a bug, not
a supported way to skip validation. See "Engine-aware validation" in
docs/VALIDATION_ENFORCEMENT.md for the full per-language API surface.
Preventing bypass at the call site
There is no bespoke ESLint rule, custom lint pass, or forged-signature
detection. Bypass is prevented more simply: the validator call is inside
the same function that produces the compiler's result, not a wrapper a
caller could choose to skip by calling something else instead. A caller
using BloqrCompiler/hostlistCompiler/Invoke-BloqrCompiler — the actual,
only public entry points each language ships — gets the validation check
whether they think about it or not. Someone could still delete or comment
out the validator call itself in a PR, which is why:
tools/check-validation-compliance.sh(seedocs/VALIDATION_ENFORCEMENT.mdfor what it checks) greps for both "the validator is invoked" and "the fail-closed opt-out symbol exists," and gates CI on both, for every language, on every PR that touchessrc/validation/**,src/compilers/**, orsrc/common/dotnet/**.- Each language's test suite includes explicit fail-closed-by-default
regression tests (e.g.
test_compile_options_default_is_fail_closedin Rust,RunAsync_WhenRulesValidatorUnavailable_FailsClosedByDefaultin .NET) that assert compilation aborts when the validator is unavailable or finds errors, with no handler registered.
That combination — CI grepping for the enforcement wiring itself, plus tests asserting the fail-closed behavior — is what stands in for the signature/audit-log mechanism this document originally described.