Policy as Data: The missing Primitive in GRC Engineering

Compliance checks have lived inside scanner code for two decades, readable only by the tool that runs them. Treating policy as data is what makes continuous monitoring auditable and GRC engineering possible at all.

If you have ever automated a compliance scan, you have seen this. Thousands of lines of XCCDF XML. Tag soup, namespace noise, a <check-content> block somewhere in the middle that says what the scanner is actually doing.

SCAP is two decades of automated compliance checking in a format you cannot read, cannot easily change, and cannot separate from the tool that runs it. To read the check, you read a program. To trust the check, you trust the vendor who wrote the program.

There is another way. The check can just be data.

Calling it policy as data sounds small. It is the structural shift the rest of this piece is about.

What “Policy as Data” Actually Means

In the old model, the check is code. Whether a system passes or fails is decided by logic buried inside a scanner, written for that scanner, tangled together with how it runs. To read what the check is doing, you read a program. To change the check, you wait for a vendor release.

Policy as data takes that apart.

A policy describes what must be true. It does not specify how to check it. The intent becomes a piece of data, a readable file. The execution stays with the engine that reads the file.

Two consequences fall out of that split immediately.

First, a human can read the check. Not a developer reading scanner code, an actual reader: an auditor, an Authorizing Official, a security engineer. They open the file and read what the system is supposed to look like. They do not read a program.

Second, the check is no longer welded to any one tool. It lives in a repository. It diffs. It opens pull requests. It is the kind of thing two engineers can argue over a line of.

This is the move people are gesturing at when they say policy as code. Policy-as-code went part of the way: it took the check out of the GUI and put it in a domain-specific language, usually one tied to a particular runtime. Rego on OPA. Sentinel on Terraform. Useful, but the policy is still a small program. It still binds to the runtime that executes it.

Policy as data goes one step further. The policy is not a program in any language. It is a declarative structure that an engine reads to decide what to collect and what state to check against. The runtime is replaceable. The policy is not.

What an ESP Policy Looks Like

To make this concrete, here is a real policy. One file. A DISA STIG check for a Kubernetes cluster.

It enforces a single requirement: the kubelet configuration file must exist and be permissioned 0644 or tighter. STIG CNTR-K8-000890, mapped to NIST 800-53 CM-5(6).

The file opens with a META block. The policy declares its own intent: a unique identifier, the platform it targets, the criticality, and the line an assessor looks for first, the control mapping. That mapping lives inside the file, in machine-readable form, so the check and the documentation cannot drift apart over time. They are the same file.

Below that sits DEF, the definition. This is the check itself, built from two simple parts.

The OBJECT is the thing being inspected. Here, one file on the host: the kubelet configuration file at /var/lib/kubelet/config.yaml. The OBJECT only names what to look at. It holds no logic.

The STATE is the condition that thing must be in. Two requirements: the file must exist, and its permissions must be 0644.

OBJECT target
    path `/var/lib/kubelet/config.yaml`
OBJECT_END

STATE meta_check
    exists boolean = true
    permissions string = `0644`
STATE_END

Read it out loud and it is almost English. This file should be in this state. That is the entire check.

The two parts get tied together by a CRI / CTN block (the criterion type node). The CTN carries a type, file_metadata, that is bound to a fixed, predefined, whitelisted way of collecting that one kind of data. The policy itself contains no command. It cannot run an arbitrary script. It can only call a contract that already exists in the engine.

Before any of this runs, the file goes through the ESP compiler. The compiler treats the policy as untrusted input: seven passes, every reference resolved, every type checked, every limit enforced. If anything is wrong, the policy is rejected before a single endpoint is touched. What comes out the other side is a validated structure the engine can trust. Same policy, same system state, same result. Every run. No inference, no heuristics, no AI.

We walk the whole file top to bottom in the companion video, Policy as Data: Inside the ESP Engine.

What Policy as Data Unlocks

A readable, portable check is the prerequisite. Here is what it makes possible.

A New Discipline: GRC Engineering

GRC engineering is a phrase you hear more every year. What it names, in the same spirit as compliance-as-code and security-as-code, is GRC done with the discipline of software engineering: version control, peer review, continuous integration, change history, repeatable builds.

A check buried inside a vendor binary cannot participate in any of that. It is a tagged blob nobody on your team can read, let alone review.

A check that is a readable file lives in a repository. It diffs against last quarter’s version. It opens a pull request. A security engineer and the person who wrote the policy can argue over one line of it. The CI pipeline runs the policy through the compiler before merge, so the malformed ones never reach production.

Compliance content stops being a “we wrote a SOP” artifact and becomes a code-grade asset, with the same lifecycle as the rest of the platform. That is the move the term GRC engineering is naming, and policy as data is the primitive that makes it real.

Auditable Continuous Monitoring

Continuous monitoring runs the same checks forever. Its credibility is not any one snapshot. It is the methodology beneath every snapshot.

When the check lives inside scanner code, the team running ConMon cannot read its own methodology. The assessor cannot either. They read vendor marketing, then trust the binary.

When the check is a readable policy file, the team, the assessor, and the Authorizing Official all read the same file. The methodology is audited once. Every cycle after that inherits the audit.

Drift detection means something different at that point. The baseline is a known, written specification, not “whatever the scanner produced last time.” A configuration drift is a measurable deviation from a policy a human can point to.

This is what FedRAMP 20x is asking for when it tells assessors to evaluate the validation process itself. A process you cannot read is not a process. It is a vendor claim.

An Open-Source Methodology

ESP is open source. Apache 2.0. The language, the compiler, the engine, the rules. The repository is github.com/scanset/Endpoint-State-Policy. It is, plainly, an open-source alternative to SCAP and XCCDF, written for a world where the methodology has to be inspectable.

The “you do not have to trust us” promise only works when the methodology is open. Otherwise the trust just shifts vendors. An assessor who wants to verify how a result was produced pulls the repo and reads the compiler. The vendor stops being a black box.

The economics are also better. The methodology audit is a one-time cost. Once it is done, every subsequent scan inherits the audit for free. You stop re-auditing the method on every cycle and start auditing the things that change: the policies, the systems, the results.

One Language for Every Framework

The same ESP structure expresses a DISA STIG, a CIS Benchmark, a NIST 800-53 control, across every platform: Linux, Windows, Kubernetes, cloud APIs. The framework is a tag on the file, not a separate tool. This is what framework-agnostic compliance actually looks like in practice.

The example above makes it concrete. That kubelet policy carries DISA-STIG:CNTR-K8-000890,NIST-800-53:CM-5(6) in a single line of its META block. One check, two frameworks, one tag away from a third.

For a program running multiple frameworks (and most federal programs do), that collapses tool-per-framework into framework-per-tag. Three scanners become one engine. Three policy formats become one shape. Learn to read one policy and you can read all of them.

Why This Matters Under FedRAMP 20x

FedRAMP 20x moved the assessment surface. The assessor no longer reviews a compiled artifact package and trusts the methodology underneath it. They evaluate the validation process itself, including its logic and its reproducibility.

A scanner whose checks live in closed code is not a methodology you can show an assessor. It is a vendor claim with results attached.

Policy as data turns the methodology into a readable, auditable, version-controlled file. That is the only shape that holds up to the new assessment surface, and it is the only shape that lets the buyer answer the questions an assessor is now allowed to ask.


A check anyone can read. A method anyone can audit. A result that is the same every time.

Article #3 covered why the result must be containable. This one was about the first piece: the check itself becoming inspectable. The whole ESP file is walked top to bottom in the companion video. The engine is open source: github.com/scanset/Endpoint-State-Policy.

If you are building toward GRC engineering on a federal program, or trying to figure out what continuous monitoring is supposed to look like when an assessor actually has to read the methodology, that is a conversation worth having. Reach the ScanSet engineering team at contact@scanset.io.

Scroll to Top