Suppression
A suppression rule: findings that match its scope are suppressed, now and in every later scan, until it expires. Fields, YAML, HCL and CLI samples, and how apply treats it.
A suppression rule: findings that match its scope are suppressed, now and in every later scan, until it expires. Every apply writes the suppression audit. Rules created from a VexSource are owned by that source and cannot be declared here.
apiVersion |
vulnetix.com/v1 |
kind |
Suppression |
| Group | Suppression |
| Status | Served. The config engine plans and applies it. |
| Objects per tenant | many, keyed by metadata.name |
| Token and role | An owner or admin session, or a vmt_ config management token with the owner or admin role that lists Suppression in its kinds. See Management tokens. |
| CEL fields | spec.when (finding): see CEL rule expressions |
| Terraform resource | vulnetix_suppression, imported by name (see Terraform and OpenTofu) |
| CLI | vulnetix config apply, CLI v3.157.0 and later. |
| Pinned schema | v1.15.0/Suppression.schema.json |
| Newest schema | latest/Suppression.schema.json |
| First released in | v1.1.0 |
| Print the built-in copy | vulnetix config schema Suppression |
Minimal
Only the fields the kind requires. From examples/Suppression/minimal.yaml.
YAML
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: lodash-prototype-pollution
spec:
type: risk_accepted
scope:
rule: CVE-2021-23337
HCL (resource vulnetix_suppression)
# Only the arguments the kind requires. From config-schemas examples/Suppression/minimal.yaml.
resource "vulnetix_suppression" "lodash_prototype_pollution" {
name = "lodash-prototype-pollution"
scope = {
rule = "CVE-2021-23337"
}
type = "risk_accepted"
}
CLI (applies it)
export VULNETIX_CONFIG_TOKEN=vmt_... # a config management token
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: lodash-prototype-pollution
spec:
type: risk_accepted
scope:
rule: CVE-2021-23337
EOF
Advanced
Every field the kind supports, with a when expression where it takes one. From examples/Suppression/advanced.yaml.
YAML
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: lodash-template-not-reachable
labels:
team: payments
annotations:
ticket: https://jira.example.com/browse/SEC-1234
spec:
type: false_positive
reason: lodash.template is never called with user input; the build only renders static templates.
justification: vulnerable_code_not_in_execute_path
scope:
category: sca
rule: CVE-2021-23337
target: pkg:npm/lodash
purl: pkg:npm/lodash@4.17.20
path: web/package-lock.json
line: 1204
lineRange: 1204-1210
repository: acme/storefront
branch: main
expires: 2027-06-30T00:00:00Z
when: severity != 'critical' && !kev && repo == 'acme/storefront'
HCL (resource vulnetix_suppression)
# Every argument the kind supports. From config-schemas examples/Suppression/advanced.yaml.
resource "vulnetix_suppression" "lodash_template_not_reachable" {
name = "lodash-template-not-reachable"
expires = "2027-06-30T00:00:00Z"
justification = "vulnerable_code_not_in_execute_path"
reason = "lodash.template is never called with user input; the build only renders static templates."
scope = {
branch = "main"
category = "sca"
line = 1204
line_range = "1204-1210"
path = "web/package-lock.json"
purl = "pkg:npm/lodash@4.17.20"
repository = "acme/storefront"
rule = "CVE-2021-23337"
target = "pkg:npm/lodash"
}
type = "false_positive"
when = "severity != 'critical' && !kev && repo == 'acme/storefront'"
labels = {
team = "payments"
}
annotations = {
ticket = "https://jira.example.com/browse/SEC-1234"
}
}
CLI (applies it)
export VULNETIX_CONFIG_TOKEN=vmt_... # a config management token
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: lodash-template-not-reachable
labels:
team: payments
annotations:
ticket: https://jira.example.com/browse/SEC-1234
spec:
type: false_positive
reason: lodash.template is never called with user input; the build only renders static templates.
justification: vulnerable_code_not_in_execute_path
scope:
category: sca
rule: CVE-2021-23337
target: pkg:npm/lodash
purl: pkg:npm/lodash@4.17.20
path: web/package-lock.json
line: 1204
lineRange: 1204-1210
repository: acme/storefront
branch: main
expires: 2027-06-30T00:00:00Z
when: severity != 'critical' && !kev && repo == 'acme/storefront'
EOF
Fields
Paths use dots for nesting and [] for the items of a list. Required means required whenever the parent object is present. A default is what the server uses when the field is left out.
| Field | Type | Required | Default | Allowed values | Description |
|---|---|---|---|---|---|
apiVersion |
string | yes | exactly vulnetix.com/v1 |
The document format version. Every kind in this release is vulnetix.com/v1. | |
kind |
string | yes | exactly Suppression |
Always Suppression. | |
metadata |
object | yes | no other keys | ||
metadata.name |
string | yes | pattern ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ |
The object’s name, unique per kind within a tenant. It is the natural key: plan, apply, export and the Terraform provider all address an object by kind and name. | |
metadata.org |
string | no | format uuid |
The tenant (organization UUID) the document belongs to. When set, plan and apply refuse to run against any other tenant. | |
metadata.labels |
object | no | map of string to string (length 0 to 256); keys match ^[a-z0-9]([a-z0-9._/-]{0,61}[a-z0-9])?$ |
Free key/value labels. Stored with the object, never interpreted by the server. | |
metadata.annotations |
object | no | map of string to string (length 0 to 4096) | Free key/value notes for people and tools, such as a ticket link. | |
spec |
object | yes | no other keys | ||
spec.type |
string | yes | false_positive, wont_fix, risk_accepted, mitigated, deferred |
The decision: false_positive (the finding is wrong), wont_fix, risk_accepted, mitigated (a control outside the code covers it) or deferred. | |
spec.reason |
string | no | Why, for the people who review suppressions. | ||
spec.justification |
string | no | component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist |
Why the product is not affected, in the OpenVEX vocabulary, for VEX documents minted from this rule. | |
spec.scope |
object | yes | no other keys | What the rule matches. It must name at least one of rule, target or path; a rule that names none would match everything. Every field given must match. | |
spec.scope.category |
string | no | sast, secrets, iac, container, sca, license, malware, crypto, ai |
The finding category. crypto and ai are inventory components (a CBOM algorithm, certificate, key or library; an AIBOM agent, SDK, model or service), which match on target. | |
spec.scope.rule |
string | no | length 1 to any | The vulnerability or rule id: a CVE or other advisory id, or a SAST, IaC or secrets rule id. | |
spec.scope.target |
string | no | length 1 to any | An inventory component’s identity: an algorithm SPDX id, a certificate subject, a purl or a model name. | |
spec.scope.purl |
string | no | pattern ^pkg:[a-z][a-z0-9.+-]*/.+ |
Only findings in this package. Without a version, every version. | |
spec.scope.path |
string | no | length 1 to any | A file path, relative to the repository root. | |
spec.scope.line |
integer | no | >= 1 | A line in path. | |
spec.scope.lineRange |
string | no | length 1 to any | A range of lines in path. | |
spec.scope.repository |
string | no | pattern ^[^\s/]+(/[^\s/]+)+$ |
A repository by its full name, owner/name as the forge shows it. Nested groups (group/subgroup/name) are allowed. | |
spec.scope.branch |
string | no | length 1 to any | ||
spec.expires |
string | no | format date-time, or format date |
When the rule stops applying. Left out, it never expires; reports flag it once it goes unreviewed for the RemediationPolicy suppressionReviewMaxAgeDays. | |
spec.when |
string | no | length 1 to 4096 | An extra condition a finding must meet, over the finding variables. It fails open: a finding the expression fails on is not suppressed, and a rule that cannot be evaluated covers nothing and is logged. |
Rules
Conditions the schema enforces beyond each field’s own type. vulnetix config validate and the engine’s plan report a document that breaks one.
| On | Rule |
|---|---|
spec.scope |
At least one of rule, target or path is set. |
CEL variables
spec.when is a CEL expression in the finding context, returning bool. It is evaluated by the suppression engine, over the findings a rule’s anchors have already matched. Fails open. A finding the expression errors on is not suppressed. See where each expression is evaluated. The functions, limits and error codes are on CEL rule expressions.
| Variable | Type | Bound | Description |
|---|---|---|---|
id |
string |
256 chars | The advisory identifier, such as CVE-2024-3094 or a GHSA ID. |
aliases |
list(string) |
64 items of 256 chars | Other identifiers the advisory is known by. |
severity |
string |
64 chars | Lower case: critical, high, medium, low, or empty when unknown. |
cvss |
double |
CVSS base score, 0 to 10. 0 when unscored. | |
epss |
double |
EPSS probability, 0 to 1. | |
epssPercentile |
double |
EPSS percentile, 0 to 1. | |
kev |
bool |
Listed in any known-exploited catalogue: CISA KEV, VulnCheck KEV or EU KEV. | |
exploitMaturity |
string |
64 chars | The highest exploit tier, as the CLI --exploits gate grades it: poc, active, weaponized, or empty when none is known. |
exploitCount |
int |
Number of public exploits known. | |
reachability |
string |
64 chars | Lower case reachability verdict: direct, transitive, semantic, unreachable, or empty when not assessed. |
reachable |
bool |
Derived. True when reachability is direct, transitive or semantic, the same set the findings page counts as reachable. |
|
internetExposed |
bool |
The package is observed on internet-facing infrastructure. | |
eol |
bool |
The package version is end of life. | |
malicious |
bool |
The package is known malware. | |
fixAvailability |
string |
64 chars | available, partial, no_fix, or empty when unknown. |
direct |
bool |
The package is a direct dependency, not a transitive one. | |
purl |
string |
2048 chars | Package URL of the affected package, with version. |
ecosystem |
string |
64 chars | Package ecosystem, such as npm, pypi, golang, maven. |
cwe |
list(string) |
64 items of 64 chars | CWE identifiers, upper case with prefix: CWE-79. |
repo |
string |
512 chars | Repository full name, owner/name. |
branch |
string |
512 chars | Branch the finding was scanned on. |
path |
string |
1024 chars | File the finding came from, such as the manifest or lock file, relative to the repository root. |
labels |
map(string, string) |
50 keys, keys and values of 256 chars | The key/value labels of the repository the finding is in, as set on the repository’s asset record or in a RepositorySettings document: labels['tier'] == 'prod'. Empty when the repository has none, and in a runtime that does not know the repository’s asset record, so 'tier' in labels is false there. |
owner |
string |
512 chars | Who owns the repository: a team name, a user name or an email address, as set on its asset record. Empty when none is set. |
criticality |
string |
64 chars | How much the business depends on the repository: low, medium, high or critical. Empty when none is set, so criticality in ['high', 'critical'] is false for an unclassified repository. |
now |
timestamp |
The time of evaluation. Callers may fix it; otherwise it is the clock when Eval runs. |
Applying this kind
- Command.
vulnetix config apply -f <file>; the CLI line above says from which release. Plan first withvulnetix config plan: it shows each object as create, update, delete or no change, with aplanHashthat apply then holds to. Apply writes in one transaction. - Manager. Each object records the manager that applied it (
terraform,consoleorgitops:<repo>).--prunedeletes only objects its own manager owns. See The manager and the config engine. - Status. Applied, Unchanged or Deleted, with no other status fields. The phases the engine reports are explained under status phases.
How apply treats it
- The justification (the OpenVEX vocabulary), the expiry and
when(stored asexpr) are stored on the suppression. - A rule owned by a VEX document or by a
VexSourcecannot be changed or deleted through the engine. whenis evaluated by the suppression engine, after the rule’s anchors have matched; see where eachwhenruns.
Terraform and OpenTofu
The resource is vulnetix_suppression in the vulnetix/resolve provider. Its arguments are the spec in snake case, beside name, labels and annotations from metadata. The Minimal and Advanced HCL are above. The provider’s own reference is vulnetix_suppression.
Import an object that already exists by its name:
# Import an existing Suppression by its name (metadata.name).
terraform import vulnetix_suppression.lodash_prototype_pollution lodash-prototype-pollution
Install, authentication and how state is read: Terraform and OpenTofu.
More examples
examples/Suppression/false-positive.yaml from Vulnetix/config-schemas:
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/Suppression.schema.json
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: lodash-cve-2021-23337
spec:
type: false_positive
reason: template() is never called with user input in this service.
justification: vulnerable_code_not_in_execute_path
scope:
category: sca
rule: CVE-2021-23337
purl: pkg:npm/lodash
repository: acme/payments-api
expires: 2027-06-30
when: '!internetExposed'
examples/Suppression/sast-path.yaml from Vulnetix/config-schemas:
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/Suppression.schema.json
apiVersion: vulnetix.com/v1
kind: Suppression
metadata:
name: test-fixtures-secrets
spec:
type: risk_accepted
reason: Fixture keys in test data are fake.
scope:
category: secrets
path: testdata/fixtures/keys.pem
repository: acme/payments-api
branch: main
Check a file with vulnetix config validate -f <file>. See Validate documents. In an editor, start the file with the schema comment.