Severity Normalization Across IaC Scanners
Each tool ships its own severity scale. To aggregate findings across Checkov + tfsec + Terrascan + kubesec + others, map to the 5-level normalized scale used in schemas/finding.json.
Normalized scale
critical → high → medium → low → info
Per-tool mapping
| Tool | Native scale | → critical | → high | → medium | → low | → info |
|---|---|---|---|---|---|---|
| Checkov | Derived from BC policy metadata (severity string) | CRITICAL | HIGH | MEDIUM | LOW | INFO |
| Checkov (no severity) | Many built-in rules ship without severity | — | default | — | — | — |
| tfsec | CRITICAL / HIGH / MEDIUM / LOW | CRITICAL | HIGH | MEDIUM | LOW | — |
| Terrascan | HIGH / MEDIUM / LOW | — | HIGH | MEDIUM | LOW | — |
| KICS | HIGH / MEDIUM / LOW / INFO / TRACE | — | HIGH | MEDIUM | LOW | INFO/TRACE |
| kubesec | Score (-∞ … +∞) — use advise/critical tags | critical advise | negative score | score = 0 | positive score | — |
| kube-linter | Severity absent; use check category + own policy | Policy-defined | Policy-defined | Policy-defined | Policy-defined | — |
| Polaris | danger / warning / ignore | — | danger | warning | — | ignore |
| cfn-nag | FAIL / WARN | — | FAIL | WARN | — | — |
| cfn-lint | E (error) / W (warning) / I (info) | — | E (sec-related) | W (sec-related) | W (non-sec) | I |
| Trivy config | CRITICAL / HIGH / MEDIUM / LOW / UNKNOWN | CRITICAL | HIGH | MEDIUM | LOW | UNKNOWN |
Rules of thumb
- Checkov rules without native severity — default to
highonly when the rule name clearly implies unauthenticated exposure / secret leakage / broad IAM; otherwisemedium. - kubesec scores — any rule tagged
critical→critical; negative total score with nocriticalrule →high; positive score but failing advice →low. - Terrascan has no
CRITICAL— re-rank TerrascanHIGHtocriticalonly when the same check fires in Checkov/tfsec as CRITICAL. Otherwise keephigh. - cfn-nag
FAIL— treat ashighunless the rule text describes public exposure (thencritical). - kube-linter / Polaris — severity is policy-defined; the team's
.kube-linter.yaml/ Polaris config must pin severities or dedup will be noisy.
Cross-tool dedup key
When the same resource is flagged by multiple scanners, prefer:
(iac_file, resource_type, resource_name, rule_category)Keep the HIGHEST normalized severity. Retain per-tool rule_ids in a duplicates[] array so remediation can cite all applicable fixes.
Category buckets (for dedup / reporting)
encryption— at-rest / in-transitiam— policies, roles, trust, wildcardsnetwork— SGs, NACLs, NSGs, public IPs, ingress/egresslogging— CloudTrail, flow logs, diagnostic settings, audit logssecrets— hardcoded creds, key rotation, Key Vaultresource_hygiene— tagging, versioning, lifecycle, backupsupply_chain— image pins, provenance, dependency sourcesruntime_hardening— pod security context, readOnlyRootFilesystem, privileged