npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
NIST CSF 2.0
Overview
Trivy (by Aqua Security) is a comprehensive, open-source security scanner that finds vulnerabilities (CVEs), misconfigurations (IaC), secrets, software licenses, and software supply-chain weaknesses across a wide range of targets: container images, filesystems, Git repositories, virtual machine images, Kubernetes clusters, and SBOM documents. It is widely adopted as a "shift-left" gate in CI/CD pipelines because it is fast, runs as a single static binary, requires no agent, and supports machine-readable output formats (JSON, SARIF, CycloneDX, SPDX) for integration with code-scanning dashboards.
Trivy bundles four primary scanners that can be toggled with --scanners:
- vuln — OS package and language-dependency vulnerability detection (CVE matching against the Trivy vulnerability DB).
- misconfig — Infrastructure-as-Code and configuration misconfiguration detection (Terraform, CloudFormation, Kubernetes manifests, Dockerfile, Helm) using built-in and custom Rego policies.
- secret — Hard-coded secret/credential detection (API keys, tokens, private keys).
- license — Software license identification and policy enforcement.
This skill covers building a Trivy-based scanning workflow that gates a CI/CD pipeline: scanning images before push, scanning IaC before apply, generating and re-scanning SBOMs, and failing builds on policy violations. Detecting these weaknesses defends against the MITRE ATT&CK technique T1525 (Implant Internal Image), where adversaries plant malicious or vulnerable images in a registry to be deployed across the environment.
When to Use
- When integrating vulnerability and misconfiguration scanning into a CI/CD pipeline as a quality/security gate before images are pushed or infrastructure is applied.
- When auditing container images in a registry for known CVEs prior to deployment.
- When validating Terraform, CloudFormation, Kubernetes, Dockerfile, or Helm IaC for security misconfigurations.
- When generating an SBOM (CycloneDX/SPDX) for supply-chain transparency and later re-scanning that SBOM for newly disclosed CVEs.
- When scanning a running Kubernetes cluster for vulnerable workloads and misconfigured RBAC/resources.
- When enforcing license compliance policy on dependencies.
Prerequisites
- A Linux/macOS/Windows host or CI runner with network access to download the Trivy vulnerability database.
- Docker (optional) if scanning local images by name or using the containerized Trivy.
- Install Trivy (Aqua Security official methods):
# Debian/Ubuntu (APT repository)
sudo apt-get install -y wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy
# RHEL/CentOS (YUM repository), macOS (Homebrew), and install script
brew install trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin
# Containerized usage (no install)
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:latest image python:3.10-alpine
# Verify
trivy --version- For air-gapped or rate-limited environments, pre-download the DB with
trivy image --download-db-onlyand the Java/policy bundles as needed.
Objectives
- Scan a container image for OS and language vulnerabilities and fail the build above a severity threshold.
- Scan IaC (Terraform/Kubernetes/Dockerfile) for misconfigurations.
- Detect hard-coded secrets in a repository or filesystem.
- Generate a CycloneDX or SPDX SBOM and re-scan it for vulnerabilities.
- Emit SARIF for GitHub code scanning and JSON for programmatic gating.
- Wire Trivy into a CI/CD pipeline with
--exit-codeto enforce policy.
MITRE ATT&CK Mapping
| Technique ID | Name | Tactic | Relevance |
|---|---|---|---|
| T1525 | Implant Internal Image | Persistence | Trivy detects vulnerable or malicious images/layers and embedded secrets before they are implanted in a registry and propagated to running workloads. |
Workflow
1. Scan a container image for vulnerabilities
Run a vulnerability-only scan of a registry image, restricting to high/critical findings and ignoring CVEs with no available fix:
trivy image \
--scanners vuln \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--format table \
python:3.10-alpineScan an image saved as a tarball (useful when the image is built but not yet pushed):
docker save myorg/app:1.4.0 -o app.tar
trivy image --input app.tar --severity CRITICAL --format json --output app-vulns.json2. Enable multiple scanners on an image
Run vulnerability, misconfiguration, secret, and license scanners together:
trivy image \
--scanners vuln,misconfig,secret,license \
--severity MEDIUM,HIGH,CRITICAL \
myorg/app:1.4.0Scan the image's embedded config (Dockerfile-equivalent build history and history secrets):
trivy image --image-config-scanners misconfig,secret myorg/app:1.4.03. Scan Infrastructure-as-Code (misconfiguration)
Scan a directory of Terraform / Kubernetes / Dockerfile / Helm / CloudFormation for misconfigurations using the config target:
# Scan a Terraform / IaC directory
trivy config \
--severity HIGH,CRITICAL \
--format table \
./infra
# Scan with custom Rego policies and a specific policy namespace
trivy config \
--config-policy ./policies \
--policy-namespaces user \
./infraAlternatively use the fs (filesystem) target with the misconfig scanner explicitly:
trivy fs --scanners misconfig,secret --severity HIGH,CRITICAL ./infra4. Scan a repository and detect secrets
Scan a local working tree (or remote repo) for vulnerabilities in lockfiles and hard-coded secrets:
# Local filesystem (dependencies + secrets)
trivy fs --scanners vuln,secret --severity HIGH,CRITICAL .
# Remote Git repository
trivy repository --scanners vuln,secret https://github.com/myorg/myrepo5. Generate and re-scan an SBOM
Produce a CycloneDX SBOM from an image, then scan the SBOM itself for vulnerabilities (so a stored SBOM can be re-evaluated as new CVEs are disclosed):
# Generate CycloneDX SBOM
trivy image --format cyclonedx --output sbom.cdx.json myorg/app:1.4.0
# Generate SPDX SBOM
trivy image --format spdx-json --output sbom.spdx.json myorg/app:1.4.0
# Re-scan the SBOM for vulnerabilities later
trivy sbom --severity HIGH,CRITICAL sbom.cdx.json6. Emit SARIF for code-scanning dashboards
Produce SARIF for GitHub Advanced Security / code scanning ingestion:
trivy image \
--format sarif \
--output trivy-results.sarif \
--severity HIGH,CRITICAL \
myorg/app:1.4.07. Gate the CI/CD pipeline with exit codes
Use --exit-code 1 so the pipeline step fails when findings at or above the chosen severity are present. Separate the "report everything" run (exit 0) from the "enforce" run (exit 1):
# 1) Informational report (never fails the build)
trivy image --severity LOW,MEDIUM,HIGH,CRITICAL --exit-code 0 --format table myorg/app:1.4.0
# 2) Enforcement gate (fails build on HIGH/CRITICAL with a fix available)
trivy image \
--severity HIGH,CRITICAL \
--ignore-unfixed \
--exit-code 1 \
--format json --output gate.json \
myorg/app:1.4.0Example GitHub Actions step using the official action:
- name: Run Trivy image scan (gate)
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myorg/app:1.4.0'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
ignore-unfixed: true
exit-code: '1'8. Manage false positives and the DB
Suppress accepted-risk findings with a .trivyignore file and keep the DB current:
# .trivyignore — one CVE/AVD/secret rule ID per line
echo "CVE-2023-12345" >> .trivyignore
echo "AVD-AWS-0089" >> .trivyignore
# Refresh DBs explicitly (useful for caching layers in CI)
trivy image --download-db-only
trivy image --download-java-db-only
# Scan a Kubernetes cluster (summary report)
trivy k8s --report summary --severity HIGH,CRITICAL clusterTools and Resources
| Tool / Resource | Purpose | Link |
|---|---|---|
| Trivy | Core scanner CLI | https://github.com/aquasecurity/trivy |
| Trivy Documentation | Official docs (targets, scanners, flags) | https://trivy.dev/latest/docs/ |
| trivy-action | GitHub Actions integration | https://github.com/aquasecurity/trivy-action |
| Trivy Operator | In-cluster Kubernetes continuous scanning | https://github.com/aquasecurity/trivy-operator |
| Trivy vulnerability DB | OSS vulnerability data source | https://github.com/aquasecurity/trivy-db |
| CycloneDX | SBOM standard emitted by Trivy | https://cyclonedx.org/ |
Validation Criteria
- Trivy installed and
trivy --versionreports a valid version. - Container image scanned for vulnerabilities with severity filtering applied.
- Image scanned with multiple scanners (vuln, misconfig, secret, license).
- IaC directory scanned with
trivy configand misconfigurations reviewed. - Secret scanning run against the repository.
- CycloneDX/SPDX SBOM generated and successfully re-scanned with
trivy sbom. - SARIF output produced for the code-scanning dashboard.
- CI/CD gate fails the build on HIGH/CRITICAL findings via
--exit-code 1. -
.trivyignoreconfigured for accepted-risk findings with documented justification.
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 2
api-reference.md2.9 KB
Trivy — Command and Flag Reference
Scan Targets (subcommands)
| Command | Target | Example |
|---|---|---|
trivy image |
Container image (registry/tar) | trivy image alpine:3.19 |
trivy fs |
Local filesystem / project dir | trivy fs ./ |
trivy repository (repo) |
Git repository (local or remote URL) | trivy repo https://github.com/org/repo |
trivy config |
IaC / config misconfiguration | trivy config ./infra |
trivy sbom |
Existing SBOM (CycloneDX/SPDX) | trivy sbom sbom.cdx.json |
trivy kubernetes (k8s) |
Live Kubernetes cluster | trivy k8s --report summary cluster |
trivy vm |
VM image (AMI/EBS/VMDK) | trivy vm ami:ami-0123 |
trivy rootfs |
Extracted root filesystem | trivy rootfs /mnt/rootfs |
Key Flags
| Flag | Description |
|---|---|
--scanners vuln,misconfig,secret,license |
Select which scanners to run |
--severity LOW,MEDIUM,HIGH,CRITICAL |
Filter results by severity |
--exit-code <n> |
Exit code when matching results are found (gating) |
--ignore-unfixed |
Suppress vulnerabilities with no fixed version |
--format table|json|sarif|cyclonedx|spdx-json |
Output format |
--output <file> |
Write report to file |
--input <file> |
Scan an image tar instead of a registry ref |
--vuln-type os,library |
Limit vulnerability detection scope |
--image-config-scanners misconfig,secret |
Scan image build config/history |
--config-policy <dir> |
Custom Rego misconfig policy directory |
--policy-namespaces <ns> |
Rego policy namespaces to evaluate |
--download-db-only |
Pre-download vulnerability DB (caching/air-gap) |
--download-java-db-only |
Pre-download Java index DB |
--skip-dirs / --skip-files |
Exclude paths from scan |
--ignorefile <path> |
Path to .trivyignore (default .trivyignore) |
Output Formats
| Format | Use |
|---|---|
table |
Human-readable console (default) |
json |
Programmatic gating / parsing |
sarif |
GitHub code scanning / IDE ingestion |
cyclonedx |
CycloneDX SBOM |
spdx-json |
SPDX SBOM (JSON) |
github |
GitHub dependency snapshot |
.trivyignore Format
# One ID per line; supports CVE, AVD (misconfig), and secret rule IDs
CVE-2023-12345
AVD-AWS-0089
generic-api-keyGitHub Actions (trivy-action)
- uses: aquasecurity/trivy-action@master
with:
scan-type: 'image' # image | fs | config | repo | sbom
image-ref: 'myorg/app:1.4.0'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'HIGH,CRITICAL'
ignore-unfixed: true
exit-code: '1'External References
- Trivy Docs: https://trivy.dev/latest/docs/
- Configuration reference: https://trivy.dev/latest/docs/configuration/
- Misconfiguration scanning: https://trivy.dev/latest/docs/scanner/misconfiguration/
- SBOM: https://trivy.dev/latest/docs/supply-chain/sbom/
standards.md1.4 KB
Standards and References — Scanning IaC and Images with Trivy
NIST CSF 2.0
| ID | Name | Rationale |
|---|---|---|
| ID.RA-01 | Asset vulnerabilities are identified and recorded | Trivy enumerates CVEs, misconfigurations, and embedded secrets across images, IaC, and SBOMs, recording them for risk assessment and remediation tracking. |
MITRE ATT&CK
| Technique ID | Name | Tactic | Rationale |
|---|---|---|---|
| T1525 | Implant Internal Image | Persistence | Scanning images and SBOMs before they reach a registry detects vulnerable, secret-laden, or malicious layers an adversary could implant to persist across deployments. |
Supporting Frameworks and Standards
- CIS Benchmarks — Trivy
misconfigchecks align with CIS Docker/Kubernetes hardening guidance. - CycloneDX / SPDX — SBOM output formats supported for supply-chain transparency.
- SARIF — Static Analysis Results Interchange Format for code-scanning dashboards.
- OWASP Top 10 (A06: Vulnerable and Outdated Components) — Trivy directly addresses outdated/vulnerable dependency detection.
Official Resources
- Trivy: https://github.com/aquasecurity/trivy
- Trivy Docs: https://trivy.dev/latest/docs/
- Trivy DB: https://github.com/aquasecurity/trivy-db
- trivy-action: https://github.com/aquasecurity/trivy-action
- Aqua Security: https://www.aquasec.com/products/trivy/
Scripts 1
agent.py6.0 KB
#!/usr/bin/env python3
"""Trivy scanning helper.
Wraps the Trivy CLI to scan container images, filesystems/IaC, and SBOMs,
parse the JSON results, summarise findings by severity, and exit non-zero
when findings at or above a chosen threshold are present (CI/CD gating).
Requires the `trivy` binary on PATH. See https://github.com/aquasecurity/trivy
"""
import argparse
import json
import shutil
import subprocess
import sys
from collections import Counter
from datetime import datetime, timezone
SEVERITY_ORDER = ["LOW", "MEDIUM", "HIGH", "CRITICAL"]
def ensure_trivy() -> str:
"""Return the trivy executable path or exit with guidance."""
path = shutil.which("trivy")
if not path:
print("[!] trivy not found on PATH. Install: "
"https://trivy.dev/latest/getting-started/installation/",
file=sys.stderr)
sys.exit(2)
return path
def build_command(trivy: str, args: argparse.Namespace) -> list:
"""Construct the trivy command line for the requested target."""
cmd = [trivy, args.target]
cmd += ["--scanners", args.scanners]
if args.severity:
cmd += ["--severity", args.severity]
if args.ignore_unfixed:
cmd.append("--ignore-unfixed")
# Always request JSON so this helper can parse + gate deterministically.
cmd += ["--format", "json"]
# exit-code 0 here; gating is enforced in Python after parsing.
cmd += ["--exit-code", "0"]
cmd.append(args.subject)
return cmd
def run_scan(cmd: list, timeout: int) -> dict:
"""Execute trivy and return the parsed JSON report."""
try:
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
except subprocess.TimeoutExpired:
print(f"[!] Trivy scan timed out after {timeout}s", file=sys.stderr)
sys.exit(3)
if proc.returncode not in (0,):
# Trivy writes scan results to stdout; real errors go to stderr.
if not proc.stdout.strip():
print(f"[!] Trivy failed (rc={proc.returncode}): {proc.stderr.strip()}",
file=sys.stderr)
sys.exit(proc.returncode)
try:
return json.loads(proc.stdout)
except json.JSONDecodeError:
print("[!] Could not parse Trivy JSON output.", file=sys.stderr)
print(proc.stderr.strip(), file=sys.stderr)
sys.exit(4)
def summarise(report: dict) -> tuple:
"""Return (severity Counter, list of finding dicts) from a Trivy report."""
counts = Counter()
findings = []
for result in report.get("Results", []) or []:
target = result.get("Target", "")
for vuln in result.get("Vulnerabilities", []) or []:
sev = (vuln.get("Severity") or "UNKNOWN").upper()
counts[sev] += 1
findings.append({
"type": "vuln",
"target": target,
"id": vuln.get("VulnerabilityID"),
"pkg": vuln.get("PkgName"),
"installed": vuln.get("InstalledVersion"),
"fixed": vuln.get("FixedVersion"),
"severity": sev,
})
for mis in result.get("Misconfigurations", []) or []:
sev = (mis.get("Severity") or "UNKNOWN").upper()
counts[sev] += 1
findings.append({
"type": "misconfig",
"target": target,
"id": mis.get("ID"),
"title": mis.get("Title"),
"severity": sev,
})
for sec in result.get("Secrets", []) or []:
sev = (sec.get("Severity") or "UNKNOWN").upper()
counts[sev] += 1
findings.append({
"type": "secret",
"target": target,
"id": sec.get("RuleID"),
"title": sec.get("Title"),
"severity": sev,
})
return counts, findings
def gate(counts: Counter, threshold: str) -> bool:
"""Return True if any finding is at or above threshold severity."""
if threshold not in SEVERITY_ORDER:
return False
idx = SEVERITY_ORDER.index(threshold)
blocking = SEVERITY_ORDER[idx:]
return any(counts.get(s, 0) > 0 for s in blocking)
def main() -> None:
parser = argparse.ArgumentParser(description="Trivy scan + CI/CD gate helper")
parser.add_argument("subject", help="Image ref, directory, repo URL, or SBOM file")
parser.add_argument("--target", default="image",
choices=["image", "fs", "config", "repo", "sbom"],
help="Trivy scan target subcommand")
parser.add_argument("--scanners", default="vuln",
help="Comma list: vuln,misconfig,secret,license")
parser.add_argument("--severity", default="HIGH,CRITICAL",
help="Severities to include")
parser.add_argument("--ignore-unfixed", action="store_true",
help="Skip vulns with no fix available")
parser.add_argument("--gate", default="", choices=["", *SEVERITY_ORDER],
help="Exit non-zero if findings >= this severity")
parser.add_argument("--timeout", type=int, default=600, help="Scan timeout (s)")
parser.add_argument("--output", help="Write JSON summary to file")
args = parser.parse_args()
trivy = ensure_trivy()
cmd = build_command(trivy, args)
print(f"[*] {datetime.now(timezone.utc).isoformat()} running: {' '.join(cmd)}")
report = run_scan(cmd, args.timeout)
counts, findings = summarise(report)
print("\n=== TRIVY FINDINGS BY SEVERITY ===")
for sev in reversed(SEVERITY_ORDER):
print(f" {sev:<9}: {counts.get(sev, 0)}")
print(f" TOTAL : {sum(counts.values())}")
if args.output:
with open(args.output, "w", encoding="utf-8") as fh:
json.dump({"counts": dict(counts), "findings": findings}, fh, indent=2)
print(f"[+] Summary written to {args.output}")
if args.gate and gate(counts, args.gate):
print(f"[!] GATE FAILED: findings at or above {args.gate}", file=sys.stderr)
sys.exit(1)
print("[+] Scan complete.")
if __name__ == "__main__":
main()