Skip to content

JailPolicy

A jail policy: the ordered rules vulnetix jail evaluates for a repository over everything ever reported for it (vulnerabilities past their SLA. Fields, YAML, HCL and CLI samples, and how apply treats it.

Checked against config-schemas@v1.15.0, terraform-provider-resolve@82cb23c, cli@v4.9.1

A jail policy: the ordered rules vulnetix jail evaluates for a repository over everything ever reported for it (vulnerabilities past their SLA, dependencies past end of life, migrations that have not landed, hygiene categories that regressed). A policy without repository is the tenant’s default; a policy with one is a per-repository override that replaces the default outright. Separate from QualityGate, which judges a single scan.

apiVersion vulnetix.com/v1
kind JailPolicy
Group Scanners and gates
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 JailPolicy in its kinds. See Management tokens.
CEL fields spec.rules[].when (finding): see CEL rule expressions
Terraform resource vulnetix_jail_policy, imported by name (see Terraform and OpenTofu)
CLI vulnetix config apply, CLI v3.157.0 and later.
Pinned schema v1.15.0/JailPolicy.schema.json
Newest schema latest/JailPolicy.schema.json
First released in v1.1.0
Print the built-in copy vulnetix config schema JailPolicy

Minimal

Only the fields the kind requires. From examples/JailPolicy/minimal.yaml.

YAML

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: org-default
spec: {}

HCL (resource vulnetix_jail_policy)

# Only the arguments the kind requires. From config-schemas examples/JailPolicy/minimal.yaml.

resource "vulnetix_jail_policy" "org_default" {
  name = "org-default"
}

CLI (applies it)

export VULNETIX_CONFIG_TOKEN=vmt_...   # a config management token
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: org-default
spec: {}
EOF

Advanced

Every field the kind supports, with a when expression where it takes one. From examples/JailPolicy/advanced.yaml.

YAML

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: payments-api
  labels:
    team: payments
  annotations:
    ticket: https://tracker.example.com/SEC-1980
spec:
  repository: acme/payments-api
  description: Payments ships with no overdue criticals, no live secrets, and moment.js on its way out.
  enabled: true
  enforcementMode: enforce
  defaultStalenessDays: 7
  onStale: fail
  rules:
    - kind: VULN
      label: No reachable critical npm finding past its SLA
      enabled: true
      category: SCA
      toolName: vulnetix
      ecosystem: npm
      severityFloor: critical
      flags: [reachable, hasFix]
      aggregate: COUNT_PAST_SLA
      operator: GT
      thresholdLow: 0
      onBreach: BLOCK
      stalenessDays: 3
      onStale: warn
      ratchet: false
      when: "fixAvailability == 'available' && (kev || epss >= 0.1)"
    - kind: GOAL
      label: Off moment.js
      targetPattern: pkg:npm/moment@*
      aggregate: COUNT
      operator: GT
      thresholdLow: 0
      deadline: 2027-03-31
      ratchet: true
      onBreach: WARN
    - kind: HYGIENE
      label: Secrets stay between 0 and 2
      category: SECRETS
      aggregate: COUNT
      operator: OUTSIDE
      thresholdLow: 0
      thresholdHigh: 2
      onStale: pass

HCL (resource vulnetix_jail_policy)

# Every argument the kind supports. From config-schemas examples/JailPolicy/advanced.yaml.

resource "vulnetix_jail_policy" "payments_api" {
  name                   = "payments-api"
  default_staleness_days = 7
  description            = "Payments ships with no overdue criticals, no live secrets, and moment.js on its way out."
  enabled                = true
  enforcement_mode       = "enforce"
  on_stale               = "fail"
  repository             = "acme/payments-api"
  rules = [
    {
      aggregate      = "COUNT_PAST_SLA"
      category       = "SCA"
      ecosystem      = "npm"
      enabled        = true
      flags          = ["reachable", "hasFix"]
      kind           = "VULN"
      label          = "No reachable critical npm finding past its SLA"
      on_breach      = "BLOCK"
      on_stale       = "warn"
      operator       = "GT"
      ratchet        = false
      severity_floor = "critical"
      staleness_days = 3
      threshold_low  = 0
      tool_name      = "vulnetix"
      when           = "fixAvailability == 'available' && (kev || epss >= 0.1)"
    },
    {
      aggregate      = "COUNT"
      deadline       = "2027-03-31"
      kind           = "GOAL"
      label          = "Off moment.js"
      on_breach      = "WARN"
      operator       = "GT"
      ratchet        = true
      target_pattern = "pkg:npm/moment@*"
      threshold_low  = 0
    },
    {
      aggregate      = "COUNT"
      category       = "SECRETS"
      kind           = "HYGIENE"
      label          = "Secrets stay between 0 and 2"
      on_stale       = "pass"
      operator       = "OUTSIDE"
      threshold_high = 2
      threshold_low  = 0
    },
  ]
  labels = {
    team = "payments"
  }
  annotations = {
    ticket = "https://tracker.example.com/SEC-1980"
  }
}

CLI (applies it)

export VULNETIX_CONFIG_TOKEN=vmt_...   # a config management token
vulnetix config apply -f - --manager gitops:acme/platform --auto-approve <<'EOF'
# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: payments-api
  labels:
    team: payments
  annotations:
    ticket: https://tracker.example.com/SEC-1980
spec:
  repository: acme/payments-api
  description: Payments ships with no overdue criticals, no live secrets, and moment.js on its way out.
  enabled: true
  enforcementMode: enforce
  defaultStalenessDays: 7
  onStale: fail
  rules:
    - kind: VULN
      label: No reachable critical npm finding past its SLA
      enabled: true
      category: SCA
      toolName: vulnetix
      ecosystem: npm
      severityFloor: critical
      flags: [reachable, hasFix]
      aggregate: COUNT_PAST_SLA
      operator: GT
      thresholdLow: 0
      onBreach: BLOCK
      stalenessDays: 3
      onStale: warn
      ratchet: false
      when: "fixAvailability == 'available' && (kev || epss >= 0.1)"
    - kind: GOAL
      label: Off moment.js
      targetPattern: pkg:npm/moment@*
      aggregate: COUNT
      operator: GT
      thresholdLow: 0
      deadline: 2027-03-31
      ratchet: true
      onBreach: WARN
    - kind: HYGIENE
      label: Secrets stay between 0 and 2
      category: SECRETS
      aggregate: COUNT
      operator: OUTSIDE
      thresholdLow: 0
      thresholdHigh: 2
      onStale: pass
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 JailPolicy Always JailPolicy.
metadata object yes no other keys metadata.name is stored as the policy’s name.
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.repository string no pattern ^[^\s/]+(/[^\s/]+)+$ The repository this policy overrides the default for. Leave it out for the tenant default; a tenant has at most one default and one policy per repository.
spec.description string no
spec.enabled boolean no true
spec.enforcementMode string no enforce enforce, warn enforce applies each rule’s onBreach. warn downgrades every breach to a warning, which disarms the jail without a CLI release.
spec.defaultStalenessDays integer no 7 0 to 365 How old the evidence a rule reads may be, in days, for rules that do not set stalenessDays.
spec.onStale string no fail fail, warn, pass What a rule does when the evidence it needs is stale or absent, for rules that do not set their own. fail is the default: a gate that passes on missing evidence is worse than no gate.
spec.rules array of object no The rules, in order. Every enabled rule is evaluated on every run; order decides presentation and which rule gives the headline reason, and never short-circuits. Rules are matched to stored rules by label, so labels must be unique within a policy.
spec.rules[].kind string yes VULN, EOL, GOAL, HYGIENE VULN: vulnerability findings. EOL: end-of-life dependencies. GOAL: a migration target, selected by targetPattern. HYGIENE: findings from the other scanner categories.
spec.rules[].label string yes length 1 to any The rule’s name in jail output and the console, and its key within the policy.
spec.rules[].enabled boolean no true
spec.rules[].category string no SCA, SAST, SECRETS, IAC, OCI, LICENSE, MALWARE Only findings from this scanner category. A HYGIENE rule without one draws from SECRETS, LICENSE, IAC, OCI and MALWARE.
spec.rules[].toolName string no length 1 to any Only findings reported by this tool.
spec.rules[].ecosystem string no length 1 to any Only dependencies from this package ecosystem.
spec.rules[].severityFloor string no low, medium, high, critical Only findings at or above this severity.
spec.rules[].flags array of string no items unique; each item: kev, malicious, hasFix, reachable, direct Every flag listed must hold for a finding to count. kev: in a known-exploited catalogue. malicious: known malware. hasFix: a fixed version exists. reachable: the vulnerable code is called. direct: a direct dependency.
spec.rules[].targetPattern string no length 1 to any GOAL only, and required there: a path.Match glob over the identifier of each fact (a purl, a package name, a rule id).
spec.rules[].aggregate string yes COUNT, COUNT_PAST_SLA, MAX_AGE_DAYS, PERCENT COUNT: how many. COUNT_PAST_SLA: how many are past the remediation window of the tenant’s RemediationPolicy (VULN only). MAX_AGE_DAYS: the age of the oldest, in days. PERCENT: the share of the population, 0 to 100.
spec.rules[].operator string yes GT, GTE, LT, LTE, EQ, NEQ, BETWEEN, OUTSIDE How the aggregate is compared with the thresholds. BETWEEN and OUTSIDE use both thresholds, inclusive.
spec.rules[].thresholdLow number yes The threshold, or the lower bound for BETWEEN and OUTSIDE.
spec.rules[].thresholdHigh number no The upper bound for BETWEEN and OUTSIDE; required by them. It must not be below thresholdLow.
spec.rules[].deadline string no format date-time, or format date GOAL only. Until this time a breach is downgraded to a warning, whatever onBreach says.
spec.rules[].ratchet boolean no false GOAL only. Breach when the value rose since the last assessment of the same rule on the same branch, so a migration holds its ground before the deadline. With no earlier assessment the value is recorded and the rule passes.
spec.rules[].onBreach string no BLOCK BLOCK, WARN
spec.rules[].stalenessDays integer no 0 to 365 Overrides the policy’s defaultStalenessDays for this rule.
spec.rules[].onStale string no fail, warn, pass Overrides the policy’s onStale for this rule.
spec.rules[].when string no length 1 to 4096 An extra condition each fact must meet to be counted, over the finding variables. A when that does not compile, or that uses a finding variable the facts of the rule kind do not carry yet, skips the rule with a warning. A fact it fails on leaves the rule indeterminate (verdict indeterminate, CLI exit 3). The factory gate fails closed instead: the rule breaches.

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.rules[] When operator is one of BETWEEN, OUTSIDE: thresholdHigh is required.
spec.rules[] When aggregate is COUNT_PAST_SLA: kind must be VULN.
spec.rules[] When kind is GOAL: targetPattern is required. Otherwise: ratchet must be false and deadline is not allowed.

CEL variables

spec.rules[].when is a CEL expression in the finding context, returning bool. It is evaluated by the jail evaluator in the VDB API, which vulnetix jail and vulnetix scan --jail call, and by the Software Factory gate node for its COUNT rules. In the VDB API a rule whose when does not compile, or uses a variable its rule kind lacks, is skipped with a warning, and a fact the expression errors on makes the rule indeterminate (vulnetix jail exits 3). In the factory gate the rule fails closed: it breaches, labelled “when could not be evaluated”. 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.

Jail evaluates when over the facts it already holds, and those do not carry every finding variable. A rule whose when names a variable its kind does not fill in is skipped with a warning when the jail gate runs, rather than counting against a zero value:

Rule kind Variables when may use
VULN id, severity, cvss, kev, malicious, reachable, fixAvailability, direct, purl, ecosystem, labels, owner, criticality, now
EOL id, severity, eol, direct, purl, ecosystem, labels, owner, criticality, now
HYGIENE id, severity, cvss, labels, owner, criticality, now
GOAL id, labels, owner, criticality, now

Applying this kind

  • Command. vulnetix config apply -f <file>; the CLI line above says from which release. Plan first with vulnetix config plan: it shows each object as create, update, delete or no change, with a planHash that apply then holds to. Apply writes in one transaction.
  • Manager. Each object records the manager that applied it (terraform, console or gitops:<repo>). --prune deletes 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

  • A policy is addressed by metadata.name. The header fields merge: a field the document leaves out keeps its value.
  • spec.repository is the scope, so leaving it out means the tenant default.
  • spec.rules, when present, is the whole ordered list. Rules are matched to stored rules by label and updated in place, which keeps each rule’s id (exemptions and the assessment history cite it). A label the document drops is deleted, and renaming a label is a delete and a create. Labels must be unique within a policy.
  • Plan runs the console’s policy and rule validators, so a policy the console refuses cannot be applied as code. A when that does not compile is reported with its rule and position.

Terraform and OpenTofu

The resource is vulnetix_jail_policy 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_jail_policy.

Import an object that already exists by its name:

# Import an existing JailPolicy by its name (metadata.name).
terraform import vulnetix_jail_policy.org_default org-default

Install, authentication and how state is read: Terraform and OpenTofu.

More examples

examples/JailPolicy/org-default.yaml from Vulnetix/config-schemas:

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: org-default
spec:
  description: Nothing ships with overdue criticals, retired runtimes or leaked secrets.
  enforcementMode: enforce
  defaultStalenessDays: 7
  onStale: fail
  rules:
    - kind: VULN
      label: No critical past its SLA
      severityFloor: critical
      aggregate: COUNT_PAST_SLA
      operator: GT
      thresholdLow: 0
    - kind: VULN
      label: Reachable KEV findings
      flags: [kev, reachable]
      aggregate: COUNT
      operator: GT
      thresholdLow: 0
      when: epss >= 0.1
    - kind: EOL
      label: Retired runtimes
      aggregate: MAX_AGE_DAYS
      operator: GT
      thresholdLow: 30
      onBreach: WARN
    - kind: HYGIENE
      label: No live secrets
      category: SECRETS
      aggregate: COUNT
      operator: GT
      thresholdLow: 0
      stalenessDays: 1

examples/JailPolicy/repo-goal.yaml from Vulnetix/config-schemas:

# yaml-language-server: $schema=https://github.com/Vulnetix/config-schemas/releases/latest/download/JailPolicy.schema.json
apiVersion: vulnetix.com/v1
kind: JailPolicy
metadata:
  name: payments-api
spec:
  repository: acme/payments-api
  enforcementMode: warn
  rules:
    - kind: GOAL
      label: Off moment.js
      targetPattern: pkg:npm/moment@*
      aggregate: COUNT
      operator: GT
      thresholdLow: 0
      deadline: 2027-03-31
      ratchet: true
    - kind: VULN
      label: High findings stay between 0 and 5
      severityFloor: high
      aggregate: COUNT
      operator: OUTSIDE
      thresholdLow: 0
      thresholdHigh: 5

Check a file with vulnetix config validate -f <file>. See Validate documents. In an editor, start the file with the schema comment.

Edit this pageSources: config-schemas/schemas/JailPolicy.schema.json, config-schemas/schemas/common.schema.json, config-schemas/examples/JailPolicy/minimal.yaml, config-schemas/examples/JailPolicy/advanced.yaml, config-schemas/examples/JailPolicy/org-default.yaml, config-schemas/examples/JailPolicy/repo-goal.yaml, config-schemas/cel/variables.go, terraform-provider-resolve/examples/resources/vulnetix_jail_policy/minimal.tf, terraform-provider-resolve/examples/resources/vulnetix_jail_policy/advanced.tf, vdb-site/docs/platform-iac-engine.md, vdb-site/api/internal/handler/config_engine_kinds.go, vdb-site/api/internal/config/jail.go, vdb-api/internal/handler/jail_when.go
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.