Control Schema Reference
WAF++ controls are machine-readable YAML files. They define what is checked, how it is checked, and what evidence is expected. This page documents the complete schema structure for control authors and contributors.
File Conventions
-
Storage location:
modules/controls/controls/ -
Filename:
WAF-{PILLAR}-{NNN}.yml(e.g.WAF-SOV-010.yml,WAF-COST-040.yml) -
Encoding: UTF-8
-
Language: English (control files), independent of the language of narrative pages
Top-Level Fields
id: # string – unique control ID (WAF-SOV-010)
title: # string – short, active title ("Data Residency Policy Defined")
pillar: # string – pillar name (sovereign | cost | security | reliability | operations | architecture | governance)
status: # string – active | deprecated | draft
severity: # string – critical | high | medium | low
category: # string – categorization within the pillar (e.g. "data-residency", "cost-allocation")
type: # list – [governance, configuration, automation, process, architecture]
tags: # list – free tags for search and filtering
description: # > – What this control requires (normative, e.g. "MUST carry tags")
rationale: # > – Why this control exists (risk justification)
threat: # list – Concrete threats when not compliant
regulatory_mapping: # list – mapping to compliance frameworks (see below)
implementation_guidance: # list – concrete implementation steps (checklist)
maturity_levels: # map – 5 maturity levels (level_1 to level_5)
evidence: # map – required + optional evidence types
checks: # list – automated and manual check steps (see below)
references: # map – links to narrative pages and related controls
severity
| Value | Meaning |
|---|---|
|
Immediate security or compliance risk. No deploy without remediation. |
|
Significant risk. Short-term remediation required (max. 2 weeks). |
|
Improvement needed. Address medium-term (next quarter). |
|
Best-practice deviation. Fix opportunistically. |
type
A control can have multiple types:
| Value | Meaning |
|---|---|
|
Organizational requirement (policies, processes, documentation) |
|
Technical configuration in IaC or cloud services |
|
Requires automated checking or enforcement |
|
Recurring process (e.g. monthly review) |
|
Architectural decision at system or platform level |
regulatory_mapping
Maps the control to one or more compliance frameworks.
regulatory_mapping:
- framework: "GDPR"
controls:
- "Art. 44 – General principle for transfers"
- "Art. 46 – Transfers subject to appropriate safeguards"
- framework: "BSI C5:2020"
controls:
- "OPS-04 – Data management"
- framework: "ISO 27001:2022"
controls:
- "A.5.12 – Classification of information"
- framework: "EUCS (ENISA)"
controls:
- "DSP-01 – Data classification"
- framework: "GAIA-X"
controls:
- "Sovereign Cloud – Data location requirements"
Supported frameworks: GDPR, BSI C5:2020, ISO 27001:2022, EUCS (ENISA), GAIA-X, FinOps Foundation, ISO 20000, Internal Governance.
maturity_levels
Five maturity levels with description and measurable criteria.
maturity_levels:
level_1:
description: "Ad hoc – no structured controls"
criteria:
- "First measurable condition at level 1"
level_2:
description: "Standardized – manually implemented"
criteria:
- "..."
level_3:
description: "Integrated – automated and checked in CI"
criteria:
- "..."
level_4:
description: "Sovereign – fully auditable"
criteria:
- "..."
level_5:
description: "Optimized – continuous improvement"
criteria:
- "..."
Each level builds on the previous one. Level 3+ generally requires automated checking (wafpass or equivalent).
evidence
Defines the evidence types expected during an audit.
evidence:
required:
- kind: "governance"
description: "Policy document, version-controlled."
- kind: "iac"
description: "Terraform configuration implementing the requirement."
optional:
- kind: "architecture"
description: "Architecture diagram with data flows."
- kind: "process"
description: "Minutes of the last review."
- kind: "config"
description: "Export from cloud console or inventory tool."
Allowed kind Values
| kind | Typical evidence |
|---|---|
|
Policy documents, ADRs, decision registers (version-controlled in Git) |
|
Terraform configuration, Helm charts, Kustomize manifests |
|
Architecture diagrams, data flow diagrams, network topologies |
|
Meeting minutes, review reports, audit logs, calendar entries |
|
Cloud console screenshots, CLI outputs, inventory exports, API responses |
checks
A list of check steps.
Each check is either automated (executable by wafpass) or manual.
checks:
- id: "waf-sov-010.tf.aws.provider-region-explicit" # unique check ID
engine: "terraform" # terraform | manual | api
provider: "aws" # aws | azurerm | google | any
automated: true # true | false
severity: "critical" # critical | high | medium | low
title: "AWS provider must have 'region' explicitly set"
description: >
Brief description of what this check verifies and why.
scope:
block_type: "provider" # resource | provider | variable | terraform | module
provider_name: "aws" # only for block_type: provider
resource_types: # only for block_type: resource
- "aws_s3_bucket"
assertions:
- attribute: "region"
op: "attribute_exists"
message: "AWS provider block is missing explicit 'region' attribute."
on_fail: "violation"
remediation: >
Set 'region' explicitly in all AWS provider blocks.
example:
compliant: |
provider "aws" {
region = var.aws_region
}
non_compliant: |
provider "aws" {
# No region set
}
ID Convention for Checks
{control-id}.{engine}.{provider}.{short-description}
│ │ │ │
│ │ │ └── kebab-case, max 40 characters
│ │ └─────────────── aws | azurerm | google | any | manual
│ └──────────────────────── terraform | manual | api
└──────────────────────────────────── e.g. waf-sov-010 (lowercase)
Example: waf-cost-010.tf.aws.compute-mandatory-tags
engine
| Value | Meaning |
|---|---|
|
Check is run against HCL2 Terraform files (wafpass) |
|
Manual review required; |
|
Check requires live query of cloud API (wafpass does not yet support this) |
scope
Defines which Terraform blocks are checked:
| block_type | Additional fields | Description |
|---|---|---|
|
|
Terraform |
|
|
Terraform |
|
– |
All |
|
– |
The |
|
– |
All |
assertions
Each assertion defines a single condition that a Terraform block must satisfy. All assertions of a check must pass for the check to be considered PASS.
assertions:
- attribute: "tags" # attribute path (dot-notation for nested values)
op: "key_exists" # operator (see table below)
key: "cost-center" # for key_exists
expected: "COST" # for equals, not_equals, in, not_in, greater_than_or_equal, less_than_or_equal
pattern: "^eu-" # for matches, not_matches (regex)
fallback_attribute: "region" # for attribute_exists_or_fallback
message: "Resource is missing the mandatory 'cost-center' tag."
Assertion Fields
| Field | Required | Description |
|---|---|---|
|
Yes |
Attribute path in the Terraform block. Dot-notation for nested values: |
|
Yes |
Operator (see table below). |
|
Only for |
Key name within a map (e.g. tag key). |
|
Operator-dependent |
Expected value. For |
|
Only for |
Regular expression (Python re syntax). |
|
Only for |
Alternative attribute checked when the primary is missing. |
|
No |
Error message on FAIL. Displayed in wafpass output. If omitted, the engine generates a default message. |
Operator Reference
| Operator | Required fields | Checks |
|---|---|---|
|
|
Attribute present and not |
|
|
Primary or fallback attribute present |
|
|
Attribute present, not |
|
|
Attribute value == |
|
|
Attribute value != |
|
|
Attribute value is in the allowed list |
|
|
Attribute value is not in the prohibited list |
|
|
Attribute value is |
|
|
Attribute value is |
|
|
Numeric attribute value >= |
|
|
Numeric attribute value ⇐ |
|
|
Attribute value matches the regex pattern |
|
|
Attribute value does not match the regex pattern |
|
|
Key |
|
– |
Terraform block of the specified type exists (scope match sufficient) |
|
|
Another resource references this block (e.g. flow log references VPC) |
|
|
Attribute value does not contain the substring |
references
Links the control to the narrative documentation.
references:
narrative: "modules/pillar-sovereign/pages/controls.adoc#WAF-SOV-010"
best_practice: "modules/pillar-sovereign/pages/best-practices/bp-data-residency.adoc"
related_controls:
- "WAF-SOV-020"
- "WAF-SOV-030"
Complete Example Control
The following example shows a minimally complete control containing all required fields and directly evaluable by wafpass:
id: WAF-COST-010
title: "Cost Allocation Tagging Enforced"
pillar: "cost"
status: "active"
severity: "high"
category: "cost-allocation"
type: ['governance', 'configuration']
tags: ['tagging', 'cost-allocation', 'finops']
description: >
All cloud resources MUST carry mandatory cost allocation tags:
cost-center, owner, environment, and workload.
rationale: >
Without consistent tagging, cloud costs cannot be allocated to workloads or teams.
Manual tagging is unreliable; IaC-enforced tags are the only sustainable approach.
threat:
- "Unallocated cloud spend making workload cost ownership impossible"
- "Budget overruns not attributable to specific teams"
regulatory_mapping:
- framework: "FinOps Foundation"
controls:
- "Inform Phase – Cost Allocation and Showback"
implementation_guidance:
- "Define a mandatory tag taxonomy: cost-center, owner, environment, workload."
- "Use a shared Terraform module to output the complete tag map."
- "Enforce tagging in CI/CD with wafpass --fail-on critical."
maturity_levels:
level_1:
description: "Ad-hoc tagging, no standard taxonomy"
criteria:
- "Some resources are tagged but no consistent taxonomy exists"
level_2:
description: "Taxonomy documented, manual tagging"
criteria:
- "Mandatory tag taxonomy is documented and version-controlled"
level_3:
description: "IaC-enforced with CI gate"
criteria:
- "Shared mandatory-tags module is used by all IaC deployments"
- "CI pipeline checks tag compliance on every PR"
level_4:
description: "Automated remediation and chargeback"
criteria:
- "Automated remediation tags untagged resources within 24 hours"
level_5:
description: "Full cost allocation with automated attribution"
criteria:
- "100% cost allocation to workloads and teams"
evidence:
required:
- kind: "governance"
description: "Tag taxonomy document (version-controlled)."
- kind: "iac"
description: "Shared mandatory-tags Terraform module."
optional:
- kind: "process"
description: "Monthly tagging compliance report."
checks:
- id: "waf-cost-010.tf.aws.compute-mandatory-tags"
engine: "terraform"
provider: "aws"
automated: true
severity: "high"
title: "AWS resources must have all mandatory cost allocation tags"
description: >
Compute and data resources must carry cost-center, owner, environment, and workload tags.
scope:
block_type: "resource"
resource_types:
- "aws_instance"
- "aws_s3_bucket"
- "aws_lambda_function"
assertions:
- attribute: "tags"
op: "key_exists"
key: "cost-center"
message: "Resource is missing the mandatory 'cost-center' tag."
- attribute: "tags"
op: "key_exists"
key: "owner"
message: "Resource is missing the mandatory 'owner' tag."
- attribute: "tags"
op: "key_exists"
key: "environment"
message: "Resource is missing the mandatory 'environment' tag."
- attribute: "tags"
op: "key_exists"
key: "workload"
message: "Resource is missing the mandatory 'workload' tag."
on_fail: "violation"
remediation: >
Use the shared mandatory-tags module. Set tags = module.tags.tags on every resource.
example:
compliant: |
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.medium"
tags = {
cost-center = "fintech-platform"
owner = "payments-team"
environment = "production"
workload = "payment-service"
}
}
non_compliant: |
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.medium"
tags = {
Name = "payment-app"
# Missing: cost-center, owner, environment, workload
}
}
references:
narrative: "modules/pillar-cost/pages/controls.adoc#WAF-COST-010"
best_practice: "modules/pillar-cost/pages/best-practices/bp-tagging.adoc"
related_controls:
- "WAF-COST-020"
- "WAF-COST-030"
Submitting a New Control
-
Assign an ID – next free number in the pillar (e.g.
WAF-COST-110); numbers are not reused. -
Create the YAML – create the file according to the schema above, fill in all required fields.
-
Validate automated checks – test locally:
wafpass check ./tests/fixtures/ --controls WAF-COST-110 -
Create the narrative page – create the matching
.adocfile in the pillar module and link it inreferences.narrative. -
Open a pull request – submit control YAML + narrative page together. The PR title should contain the control ID.
Further Reading
-
WAF++ PASS – CLI Reference – how controls are executed
-
Controls Catalog – all existing controls
-
Assessment Methodology – controls in the assessment context