Battle-tested patterns for eliminating false positives and false negatives in OVAL-based host configuration / security baseline checks.
These patterns come from authoring and operating 1,000+ custom OVAL rules across a
large multi-cloud fleet (Linux, Windows, container, DBMS, and middleware platforms),
where naive rule definitions produced tens of thousands of bogus findings. Each pattern
below documents the root cause of a class of false results and the minimal structural
fix, with runnable example definitions in examples/.
All examples are generic and vendor-neutral. No proprietary content, credentials, or internal data is included.
OVAL evaluates criteria trees of *_test objects against a host. A check that looks
correct on the authoring machine often misfires at fleet scale because:
- the target software isn't installed on most hosts,
- a
filepathpattern over-matches backup/rotated files, - a
file_testis run on an image/snapshot where the filesystem isn't mounted, - a Windows
registry_testreads a key path that doesn't resolve on the live system, - a negated existence check produces a contradictory "evidence" leaf.
The result is the same: a rule that is technically valid but operationally useless because it FAILs (or PASSes) on thousands of hosts for the wrong reason.
| # | Pattern | Symptom | Fix |
|---|---|---|---|
| 1 | Not-installed → PASS wrapping | Software absent ⇒ config file missing ⇒ ALL FAIL across fleet | Wrap real check in OR (NOT platform-indicator-exists) |
| 2 | Backup-file anchor | .*/app\.conf also matches app.conf.bak, .rpmsave, .260401 |
Anchor the literal extension with $ |
| 3 | file_test → textfilecontent on images | file_test returns nothing on VM image/snapshot ⇒ false FAIL |
Use textfilecontent54 content checks, not file_test, for image-mountable paths |
| 4 | Windows registry ControlSet | CurrentControlSet path unreadable by scanner ⇒ false FAIL |
Check ControlSet001 (and OR siblings) instead of CurrentControlSet |
| 5 | SUID/negation clarity | AND(none_exist, exists+flag=false) emits a self-contradicting leaf |
Single negated criterion: "if vulnerable-state exists ⇒ fail" |
| 6 | Directory vs file permission | Directory-mode check rejects valid 0644 files / engine ignores dirs |
Test the file mode, separate dir and file objects |
The single highest-impact fix. If a rule checks the configuration of software that may not be present, an absent config file must evaluate to PASS, not FAIL.
<criteria operator="OR">
<!-- Branch A: platform is NOT installed -> rule passes vacuously -->
<criteria operator="AND" comment="platform not installed">
<criterion negate="true" comment="indicator file absent">
<!-- references a file_test that is TRUE when the indicator exists -->
</criterion>
</criteria>
<!-- Branch B: platform IS installed -> run the real compliance check -->
<criteria operator="AND" comment="actual configuration check">
<!-- ...the original criteria... -->
</criteria>
</criteria>Pick a stable indicator per platform — the main binary or canonical config that is
guaranteed present iff the software is installed (e.g. /usr/sbin/nginx,
/etc/mysql/, the service unit file). See
examples/01_not_installed_pass.xml for a
complete, annotated definition.
- Browse
examples/— each.xmlis a minimal, self-contained OVAL definition fragment demonstrating one pattern, heavily commented. - Read
PATTERNS.mdfor the long-form rationale, the decision flow for "which test type do I use", and a pre-ship checklist for new rules.
These are authoring patterns, not a scanner. They apply to any OVAL-consuming engine
(workload scanners, CNAPP host-configuration rules, oscap, etc.).
MIT — use freely.