Customize Policies with Overlays
Import an existing cnspec policy and override selected checks or policy settings without copying the base policy
A policy overlay lets you adapt an existing policy without copying and maintaining the entire policy. The overlay is a small policy that:
- Imports a base policy.
- Overrides only the checks or policy settings that your organization needs to change.
All other content continues to come from the base policy. When the base policy gains new checks or documentation, the overlay inherits those updates.
Use an overlay when you need to enforce a stricter check, put a base check into preview, deactivate a check that does not apply to your environment, or use a different scoring system.
How an overlay works
An overlay uses two types of policy groups:
- An
importgroup adds the base policy to the overlay. - An
overridegroup references checks from the imported policy and supplies the values that should change.
cnspec resolves the import and overrides into one effective policy at scan time. It does not change the base policy itself.
Create an overlay
The following self-contained bundle defines a base SSH policy and an overlay for a more restrictive production environment:
policies:
# The policy that provides the original checks.
- uid: example-linux-baseline
name: Example Linux baseline
version: 1.0.0
scoring_system: highest impact
tags:
mondoo.com/category: security
mondoo.com/platform: linux
require:
- provider: os
groups:
- title: SSH configuration
filters:
- mql: asset.family.contains("unix")
checks:
- uid: sshd-max-auth-tries
- uid: sshd-use-pam
- uid: sshd-port
# The policy to assign for the production environment.
- uid: production-linux-baseline
name: Production Linux baseline
version: 1.0.0
scoring_system: highest impact
tags:
mondoo.com/category: security
mondoo.com/platform: linux
require:
- provider: os
groups:
# Import every check from the base policy.
- type: import
policies:
- uid: example-linux-baseline
# Change only the checks that differ in production.
- type: override
title: Production requirements
filters:
- mql: asset.family.contains("unix")
checks:
- uid: sshd-max-auth-tries
title: Limit unsuccessful SSH authentication attempts
action: modify
mql: sshd.config.params["MaxAuthTries"] <= 3
impact: 90
- uid: sshd-use-pam
action: preview
- uid: sshd-port
action: deactivate
queries:
- uid: sshd-max-auth-tries
title: Limit unsuccessful SSH authentication attempts
mql: sshd.config.params["MaxAuthTries"] <= 4
impact: 70
- uid: sshd-use-pam
title: Enable PAM for SSH
mql: sshd.config.params["UsePAM"] == "yes"
impact: 60
- uid: sshd-port
title: Use the default SSH port
mql: sshd.config.params["Port"] == 22
impact: 30The effective production-linux-baseline policy:
- Requires no more than three authentication attempts instead of four and raises the check's impact to 90.
- Runs and reports the PAM check without including it in the score.
- Does not run the SSH port check.
- Inherits every other field and any future checks from
example-linux-baseline.
Assign the overlay policy instead of separately maintaining a production copy of the base policy.
You do not need to assign the base policy as well; the overlay already runs its checks. If both are assigned, each overridden check still runs once, with the override applied, and both policies report that same result. You simply see two policy entries on the asset instead of one.
Choose an override action
Each overridden check or data query references the original item by uid or mrn and uses an action to describe the change. Put scored checks under checks and data queries under queries in the override group.
| Action | Behavior |
|---|---|
modify | Replaces the fields supplied in the override and inherits fields that are omitted. Use it to change MQL or impact. |
preview | Runs and reports the check but excludes its result from scoring. |
deactivate | Prevents the check from running. |
activate | Explicitly activates the check. |
To learn about preview behavior and remediation deadlines, read Preview Checks.
You can override check fields such as mql, impact, title, documentation, filters, properties, and variants. On a policy reference in an import group, you can override action, impact, or scoring_system:
- type: import
policies:
- uid: example-linux-baseline
scoring_system: bandedWhen an override supplies a collection such as filters, props, tags, or variants, that
collection replaces the inherited value; cnspec does not append the new entries. Include every
entry that the effective check still needs.
Reference policy content outside the bundle
The first example uses UIDs because the base policy, overlay, and checks are in the same bundle. When the base policy already exists outside the overlay bundle, such as a Mondoo policy or a policy uploaded to your space, import it by its full MRN:
policies:
- uid: overlay-test
name: Overlay test
version: 1.0.0
tags:
mondoo.com/category: security
mondoo.com/platform: aws,cloud
require:
- provider: aws
groups:
# Import the base policy by MRN. It is not part of this bundle.
- type: import
policies:
- mrn: //policy.api.mondoo.app/policies/mondoo-aws-security
# Override one of its checks. Reference the check by UID.
- type: override
title: corrected checks
filters:
- mql: asset.platform == "terraform-plan"
checks:
- uid: mondoo-aws-security-kms-key-no-public-access-terraform-plan
action: modify
impact: 10
mql: |
true
scoring_system: highest impactUpload it and assign it to the space:
cnspec policy upload overlay-test.mql.yamlThe import group takes an MRN. A uid there means a policy defined in this same bundle.
Override checks take a UID. cnspec resolves the UID against the scopes of the policies your policy imports, so it lands on the check those policies own. You can also name a check by its full MRN if you prefer to be explicit:
- type: override
checks:
- mrn: //policy.api.mondoo.app/queries/CHECK_UID
action: modify
impact: 90Use the identifiers from the base policy. You can download the policy to inspect its policy and check UIDs and MRNs.
Do not declare the base policy's checks in the bundle's top-level queries: block in order to
reference them. A top-level entry defines a check, so an entry carrying a Mondoo-owned MRN is
rejected at upload with query mrn ... is not in correct scope of owner mrn .... An override
group only references a check, which is why it can name content your space does not own.
cnspec must be able to resolve every imported policy and overridden check. cnspec policy lint
and offline scans have no access to Mondoo Platform, so they cannot see a policy imported by MRN.
To validate an overlay entirely locally, keep the base content and overlay in the same bundle
file.
Limit where or how long an override applies
Add asset filters to an override group to apply it only to matching assets. Add valid.until to stop applying the override after a date:
- type: override
title: Ubuntu exception through October 2026
filters:
- mql: asset.platform == "ubuntu"
valid:
until: 2026-10-01
checks:
- uid: sshd-use-pam
action: previewUntil October 1, 2026, the PAM check is in preview for Ubuntu assets. After that date, the override no longer applies and the imported check returns to its base behavior.
Validate and scan an overlay
If the base policy and overlay are in one file, lint and scan that bundle:
cnspec policy lint production-overlay.mql.yaml
cnspec scan local --policy-bundle production-overlay.mql.yamlYou can scan a directory when a bundle is split across files:
cnspec scan local --policy-bundle ./policiescnspec policy lint validates each file independently. To lint cross-file references locally, first combine the base content and overlay into one self-contained bundle file.
In a self-contained bundle, linting reports an error when an override targets a check the bundle does not define:
override targets check 'sshd-prot', which this bundle does not define
(the policy imports no other policy that could own it)When your policy imports a policy by MRN, linting cannot check the override targets: it has no access to Mondoo Platform and so cannot see which checks that policy owns. Those targets are validated when you upload the overlay, and an override that resolves to nothing is rejected there:
override targets check 'CHECK_UID', which is not defined in this bundle
and is not owned by any imported policyKeep overlays maintainable
- Keep the overlay small. Override only fields that intentionally differ from the base policy.
- Use stable UIDs or MRNs. If an identifier changes in the base policy, update the overlay reference.
- Avoid multiple matching overrides for the same field. Prefer one authoritative override so the effective behavior remains clear.
- Lint and test the overlay after updating the base policy.
Next steps
- To run checks without affecting scores, read Preview Checks.
- To make thresholds configurable instead of replacing MQL, read Define Properties.
- To validate, upload, and assign an overlay policy, read Manage Policies.