Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
Overview
Grype is an open-source vulnerability scanner from Anchore that inspects container images, filesystems, and SBOMs for known CVEs. It leverages Syft-generated SBOMs to match packages against multiple vulnerability databases including NVD, GitHub Advisories, and OS-specific feeds.
When to Use
- When conducting security assessments that involve scanning container images with grype
- When following incident response procedures for related security events
- When performing scheduled security testing or auditing activities
- When validating security controls through hands-on testing
Prerequisites
- Docker or Podman installed
- Grype CLI installed (
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin) - Syft CLI (optional, for SBOM generation)
- Network access to pull vulnerability databases
Core Commands
Install Grype
# Install via script
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Verify installation
grype version
# Install via Homebrew (macOS/Linux)
brew install grypeScan Container Images
# Scan a Docker Hub image
grype nginx:latest
# Scan from Docker daemon
grype docker:myapp:1.0
# Scan a local archive
grype docker-archive:image.tar
# Scan an OCI directory
grype oci-dir:path/to/oci/
# Scan a Singularity image
grype sif:image.sif
# Scan a local directory / filesystem
grype dir:/path/to/projectOutput Formats
# Default table output
grype alpine:3.18
# JSON output for pipeline processing
grype alpine:3.18 -o json > results.json
# CycloneDX SBOM output
grype alpine:3.18 -o cyclonedx
# SARIF output for GitHub Security tab
grype alpine:3.18 -o sarif > grype.sarif
# Template-based custom output
grype alpine:3.18 -o template -t /path/to/template.tmplFiltering and Thresholds
# Fail if vulnerabilities meet or exceed a severity
grype nginx:latest --fail-on critical
# Show only fixed vulnerabilities
grype nginx:latest --only-fixed
# Show only non-fixed vulnerabilities
grype nginx:latest --only-notfixed
# Filter by severity
grype nginx:latest --only-fixed -o json | jq '[.matches[] | select(.vulnerability.severity == "High")]'
# Explain a specific CVE
grype nginx:latest --explain --id CVE-2024-1234Working with SBOMs
# Generate SBOM with Syft then scan
syft nginx:latest -o spdx-json > nginx-sbom.json
grype sbom:nginx-sbom.json
# Scan CycloneDX SBOM
grype sbom:bom.jsonConfiguration File (.grype.yaml)
# .grype.yaml
check-for-app-update: false
fail-on-severity: "high"
output: "json"
scope: "squashed" # or "all-layers"
quiet: false
ignore:
- vulnerability: CVE-2023-12345
reason: "False positive - not exploitable in our context"
- vulnerability: CVE-2023-67890
fix-state: unknown
db:
auto-update: true
cache-dir: "/tmp/grype-db"
max-allowed-built-age: 120h # 5 days
match:
java:
using-cpes: true
python:
using-cpes: true
javascript:
using-cpes: falseCI/CD Integration
# GitHub Actions
- name: Scan image with Grype
uses: anchore/scan-action@v4
with:
image: "myregistry/myapp:${{ github.sha }}"
fail-build: true
severity-cutoff: high
output-format: sarif
id: scan
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.scan.outputs.sarif }}# GitLab CI
container_scan:
stage: test
image: anchore/grype:latest
script:
- grype ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA} --fail-on high -o json > grype-report.json
artifacts:
reports:
container_scanning: grype-report.jsonDatabase Management
# Check database status
grype db status
# Manually update vulnerability database
grype db update
# Delete cached database
grype db delete
# List supported database providers
grype db listKey Vulnerability Sources
| Source | Coverage |
|---|---|
| NVD | CVEs across all ecosystems |
| GitHub Advisories | Open source package vulnerabilities |
| Alpine SecDB | Alpine Linux packages |
| Amazon Linux ALAS | Amazon Linux AMI |
| Debian Security Tracker | Debian packages |
| Red Hat OVAL | RHEL, CentOS |
| Ubuntu Security | Ubuntu packages |
| Wolfi SecDB | Wolfi/Chainguard images |
Best Practices
- Pin image tags - Always scan specific digests, not
latest - Fail on severity - Set
--fail-on highorcriticalin CI gates - Use SBOMs - Generate SBOMs with Syft for reproducible scanning
- Suppress false positives - Use
.grype.yamlignore rules with documented reasons - Scan all layers - Use
--scope all-layersto catch vulnerabilities in intermediate layers - Automate database updates - Keep the vulnerability database current in CI runners
- Compare scans - Track vulnerability count over time for regression detection
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.7 KB
API Reference: Scanning Container Images with Grype
Grype CLI Commands
| Command | Description |
|---|---|
grype <image> |
Scan a container image |
grype <image> -o json |
JSON output for parsing |
grype <image> -o sarif |
SARIF output for GitHub Security |
grype <image> --fail-on critical |
Exit non-zero on severity |
grype <image> --only-fixed |
Show only fixable vulns |
grype sbom:<file> |
Scan a pre-generated SBOM |
grype dir:<path> |
Scan a local directory |
grype db status |
Check vulnerability DB status |
grype db update |
Update vulnerability database |
Input Sources
| Source | Syntax | Description |
|---|---|---|
| Registry | grype nginx:latest |
Pull from registry |
| Docker daemon | grype docker:myapp:1.0 |
Local Docker image |
| Archive | grype docker-archive:image.tar |
Saved tar archive |
| OCI dir | grype oci-dir:path/ |
OCI layout directory |
| SBOM | grype sbom:bom.json |
CycloneDX/SPDX SBOM |
| Directory | grype dir:/path/ |
Filesystem scan |
Severity Levels
| Level | CVSS Range | Action |
|---|---|---|
| Critical | 9.0 - 10.0 | Immediate remediation |
| High | 7.0 - 8.9 | Fix before deployment |
| Medium | 4.0 - 6.9 | Plan remediation |
| Low | 0.1 - 3.9 | Accept or fix later |
| Negligible | 0.0 | Informational |
Python Libraries
| Library | Version | Purpose |
|---|---|---|
subprocess |
stdlib | Execute grype CLI |
json |
stdlib | Parse JSON output |
pathlib |
stdlib | File path handling |
References
- Grype GitHub: https://github.com/anchore/grype
- Anchore Scan Action: https://github.com/anchore/scan-action
- Syft SBOM Generator: https://github.com/anchore/syft
standards.md2.5 KB
Standards and References - Container Image Scanning with Grype
Industry Standards
NIST SP 800-190: Application Container Security Guide
- Section 4.1: Image vulnerabilities - Recommends scanning images for known vulnerabilities before deployment
- Section 4.2: Image configuration defects - Covers misconfigurations in container images
- Recommends integrating vulnerability scanning into CI/CD pipelines
CIS Docker Benchmark v1.6
- Rule 4.1: Ensure a user for the container has been created
- Rule 4.6: Add HEALTHCHECK instruction to the container image
- Rule 4.9: Ensure that COPY is used instead of ADD
- Rule 4.10: Ensure secrets are not stored in Dockerfiles
NIST SP 800-53 Rev 5
- RA-5: Vulnerability Monitoring and Scanning
- SI-2: Flaw Remediation
- CM-6: Configuration Settings
- SA-11: Developer Security Testing and Evaluation
OWASP Container Security
- VS-001: Vulnerability Scanning - Scan container images for known vulnerabilities
- VS-002: SBOM Generation - Generate and maintain software bill of materials
- VS-003: Base Image Selection - Use minimal, trusted base images
Vulnerability Databases
| Database | URL | Update Frequency |
|---|---|---|
| NVD (National Vulnerability Database) | https://nvd.nist.gov/ | Continuous |
| GitHub Advisory Database | https://github.com/advisories | Continuous |
| OSV (Open Source Vulnerabilities) | https://osv.dev/ | Continuous |
| Alpine SecDB | https://secdb.alpinelinux.org/ | Daily |
| Debian Security Tracker | https://security-tracker.debian.org/ | Daily |
CVSS Scoring Reference
| Severity | CVSS v3.1 Score | Recommended Action |
|---|---|---|
| Critical | 9.0 - 10.0 | Block deployment, immediate remediation |
| High | 7.0 - 8.9 | Block deployment in production |
| Medium | 4.0 - 6.9 | Track and remediate within SLA |
| Low | 0.1 - 3.9 | Accept risk or remediate in next cycle |
| None | 0.0 | Informational |
Compliance Mappings
PCI DSS v4.0
- Requirement 6.3.1: Identify and manage security vulnerabilities
- Requirement 6.3.3: Update system components to address known vulnerabilities
SOC 2
- CC7.1: To meet its objectives, the entity uses detection and monitoring procedures to identify changes to configurations that result in the introduction of new vulnerabilities
FedRAMP
- RA-5(2): Update the vulnerabilities scanned within every 30 days prior to a new scan
- RA-5(5): Implement privileged access authorization for vulnerability scanning activities
workflows.md4.0 KB
Workflow - Container Image Scanning with Grype
Phase 1: Environment Setup
Install Grype and Syft
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Install Syft for SBOM generation
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Verify
grype version
syft versionConfigure Grype
# Create config directory
mkdir -p ~/.grype
# Create configuration file
cat > ~/.grype/.grype.yaml <<EOF
check-for-app-update: false
fail-on-severity: "high"
db:
auto-update: true
cache-dir: "/tmp/grype-db"
max-allowed-built-age: 120h
ignore:
# Add known false positives here
[]
EOFPhase 2: Image Scanning Workflow
Step 1 - Generate SBOM
syft ${IMAGE_REF} -o spdx-json > sbom.spdx.json
syft ${IMAGE_REF} -o cyclonedx-json > sbom.cdx.jsonStep 2 - Run Vulnerability Scan
# Scan directly
grype ${IMAGE_REF} -o json > vulnerability-report.json
# Or scan from SBOM (faster for repeated scans)
grype sbom:sbom.spdx.json -o json > vulnerability-report.jsonStep 3 - Evaluate Results
# Count by severity
cat vulnerability-report.json | jq '.matches | group_by(.vulnerability.severity) | map({severity: .[0].vulnerability.severity, count: length})'
# List critical and high findings
cat vulnerability-report.json | jq '[.matches[] | select(.vulnerability.severity == "Critical" or .vulnerability.severity == "High") | {id: .vulnerability.id, severity: .vulnerability.severity, package: .artifact.name, version: .artifact.version, fix: .vulnerability.fix.versions}]'Step 4 - Gate Decision
# Automated gate check
grype ${IMAGE_REF} --fail-on high
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ]; then
echo "GATE FAILED: High or Critical vulnerabilities found"
exit 1
fiPhase 3: CI/CD Integration
GitHub Actions Complete Workflow
name: Container Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: myapp:${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Scan for vulnerabilities
uses: anchore/scan-action@v4
id: scan
with:
image: myapp:${{ github.sha }}
fail-build: true
severity-cutoff: high
output-format: sarif
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: ${{ steps.scan.outputs.sarif }}
- name: Upload SBOM artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.jsonPhase 4: Reporting and Remediation
Generate Human-Readable Report
# Table output with full details
grype ${IMAGE_REF} -o table > scan-report.txt
# Generate custom HTML report using template
grype ${IMAGE_REF} -o template -t report.tmpl > report.htmlRemediation Workflow
- Review critical/high findings from scan output
- Check if fix versions are available (
fix.versionsin JSON output) - Update base image to latest patched version
- Update application dependencies
- Rebuild and rescan to verify remediation
- Add accepted risks to
.grype.yamlignore list with documented justification
Phase 5: Continuous Monitoring
Scheduled Rescans
# GitHub Actions scheduled scan
name: Scheduled Vulnerability Scan
on:
schedule:
- cron: '0 6 * * 1' # Every Monday at 6 AM
jobs:
rescan:
runs-on: ubuntu-latest
steps:
- name: Scan production images
run: |
for image in $(cat image-inventory.txt); do
grype ${image} --fail-on critical -o json > "report-$(echo $image | tr '/:' '-').json"
doneScripts 2
agent.py4.9 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for scanning container images with Anchore Grype.
Runs Grype CLI against container images, parses JSON results,
applies severity thresholds, and generates vulnerability reports
with remediation guidance.
"""
import json
import subprocess
import sys
from pathlib import Path
from datetime import datetime
class GrypeScanAgent:
"""Scans container images for vulnerabilities using Grype."""
def __init__(self, output_dir="./grype_scan"):
self.output_dir = Path(output_dir)
self.output_dir.mkdir(parents=True, exist_ok=True)
self.results = []
def _run_grype(self, target, extra_args=None):
"""Execute grype CLI and return parsed JSON output."""
cmd = ["grype", target, "-o", "json", "--quiet"]
if extra_args:
cmd.extend(extra_args)
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode not in (0, 1):
return {"error": result.stderr.strip()}
return json.loads(result.stdout) if result.stdout.strip() else {}
except FileNotFoundError:
return {"error": "grype not found. Install: curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh"}
except subprocess.TimeoutExpired:
return {"error": "Scan timed out after 300 seconds"}
except json.JSONDecodeError:
return {"error": "Failed to parse grype JSON output"}
def scan_image(self, image_ref, fail_on=None, only_fixed=False):
"""Scan a container image for vulnerabilities."""
args = []
if only_fixed:
args.append("--only-fixed")
raw = self._run_grype(image_ref, args)
if "error" in raw:
return raw
matches = raw.get("matches", [])
vulns = []
for m in matches:
vuln = m.get("vulnerability", {})
artifact = m.get("artifact", {})
vulns.append({
"id": vuln.get("id", ""),
"severity": vuln.get("severity", "Unknown"),
"package": artifact.get("name", ""),
"version": artifact.get("version", ""),
"fixed_in": vuln.get("fix", {}).get("versions", []),
"type": artifact.get("type", ""),
})
summary = self._summarize(vulns)
scan_result = {
"image": image_ref,
"scan_date": datetime.utcnow().isoformat(),
"total_vulnerabilities": len(vulns),
"summary": summary,
"vulnerabilities": vulns,
}
if fail_on:
sev_order = ["Critical", "High", "Medium", "Low", "Negligible"]
threshold_idx = sev_order.index(fail_on) if fail_on in sev_order else -1
for sev in sev_order[:threshold_idx + 1]:
if summary.get(sev, 0) > 0:
scan_result["gate_status"] = "FAILED"
break
else:
scan_result["gate_status"] = "PASSED"
self.results.append(scan_result)
return scan_result
def scan_sbom(self, sbom_path):
"""Scan a pre-generated SBOM file."""
return self._run_grype(f"sbom:{sbom_path}")
def scan_directory(self, dir_path):
"""Scan a local directory for vulnerabilities."""
return self._run_grype(f"dir:{dir_path}")
def check_db_status(self):
"""Check Grype vulnerability database status."""
try:
result = subprocess.run(
["grype", "db", "status"], capture_output=True, text=True, timeout=30
)
return {"status": result.stdout.strip()}
except (FileNotFoundError, subprocess.TimeoutExpired) as e:
return {"error": str(e)}
def _summarize(self, vulns):
summary = {}
for v in vulns:
sev = v["severity"]
summary[sev] = summary.get(sev, 0) + 1
return summary
def generate_report(self):
report = {
"report_date": datetime.utcnow().isoformat(),
"scans": self.results,
"total_images": len(self.results),
}
out = self.output_dir / "grype_report.json"
with open(out, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps(report, indent=2))
return report
def main():
if len(sys.argv) < 2:
print("Usage: agent.py <image_ref> [--fail-on critical|high|medium]")
sys.exit(1)
image = sys.argv[1]
fail_on = None
if "--fail-on" in sys.argv:
idx = sys.argv.index("--fail-on")
if idx + 1 < len(sys.argv):
fail_on = sys.argv[idx + 1].capitalize()
agent = GrypeScanAgent()
result = agent.scan_image(image, fail_on=fail_on)
agent.generate_report()
if result.get("gate_status") == "FAILED":
sys.exit(1)
if __name__ == "__main__":
main()
process.py6.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Grype Container Image Scanner - Automated scanning and reporting utility.
Scans container images using Grype, parses results, and generates
summary reports with severity breakdowns and remediation guidance.
"""
import json
import subprocess
import sys
import argparse
from datetime import datetime
from pathlib import Path
def run_grype_scan(image: str, output_format: str = "json", fail_on: str = None) -> dict:
"""Run grype scan on a container image and return parsed results."""
cmd = ["grype", image, "-o", output_format, "--quiet"]
if fail_on:
cmd.extend(["--fail-on", fail_on])
result = subprocess.run(cmd, capture_output=True, text=True)
if output_format == "json":
try:
return json.loads(result.stdout)
except json.JSONDecodeError:
print(f"Error parsing Grype output: {result.stderr}", file=sys.stderr)
sys.exit(1)
return {"raw": result.stdout, "returncode": result.returncode}
def parse_vulnerabilities(scan_data: dict) -> list:
"""Extract and structure vulnerability matches from scan results."""
matches = scan_data.get("matches", [])
vulns = []
for match in matches:
vuln = match.get("vulnerability", {})
artifact = match.get("artifact", {})
fix_info = vuln.get("fix", {})
vulns.append({
"id": vuln.get("id", "UNKNOWN"),
"severity": vuln.get("severity", "Unknown"),
"package": artifact.get("name", "unknown"),
"version": artifact.get("version", "unknown"),
"type": artifact.get("type", "unknown"),
"fix_versions": fix_info.get("versions", []),
"fix_state": fix_info.get("state", "unknown"),
"data_source": vuln.get("dataSource", ""),
"description": vuln.get("description", ""),
})
return vulns
def severity_summary(vulns: list) -> dict:
"""Generate severity count summary."""
summary = {"Critical": 0, "High": 0, "Medium": 0, "Low": 0, "Negligible": 0, "Unknown": 0}
for v in vulns:
sev = v["severity"]
if sev in summary:
summary[sev] += 1
else:
summary["Unknown"] += 1
return summary
def fixable_summary(vulns: list) -> dict:
"""Count fixable vs non-fixable vulnerabilities."""
fixable = sum(1 for v in vulns if v["fix_state"] == "fixed")
not_fixable = sum(1 for v in vulns if v["fix_state"] != "fixed")
return {"fixable": fixable, "not_fixable": not_fixable, "total": len(vulns)}
def generate_report(image: str, vulns: list, output_path: str = None) -> str:
"""Generate a markdown vulnerability report."""
summary = severity_summary(vulns)
fix_info = fixable_summary(vulns)
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
report = f"""# Vulnerability Scan Report
**Image:** `{image}`
**Scan Date:** {timestamp}
**Scanner:** Grype (Anchore)
**Total Vulnerabilities:** {len(vulns)}
## Severity Summary
| Severity | Count |
|----------|-------|
| Critical | {summary['Critical']} |
| High | {summary['High']} |
| Medium | {summary['Medium']} |
| Low | {summary['Low']} |
| Negligible | {summary['Negligible']} |
## Fix Availability
- **Fixable:** {fix_info['fixable']}
- **Not Fixable:** {fix_info['not_fixable']}
## Critical and High Findings
| CVE | Severity | Package | Version | Fix Available |
|-----|----------|---------|---------|---------------|
"""
critical_high = [v for v in vulns if v["severity"] in ("Critical", "High")]
critical_high.sort(key=lambda x: (0 if x["severity"] == "Critical" else 1, x["id"]))
for v in critical_high:
fix = ", ".join(v["fix_versions"]) if v["fix_versions"] else "No fix"
report += f"| {v['id']} | {v['severity']} | {v['package']} | {v['version']} | {fix} |\n"
report += f"\n## All Vulnerabilities ({len(vulns)} total)\n\n"
report += "| CVE | Severity | Package | Version | Type | Fix State |\n"
report += "|-----|----------|---------|---------|------|----------|\n"
for v in sorted(vulns, key=lambda x: (
{"Critical": 0, "High": 1, "Medium": 2, "Low": 3, "Negligible": 4}.get(x["severity"], 5),
x["id"]
)):
report += f"| {v['id']} | {v['severity']} | {v['package']} | {v['version']} | {v['type']} | {v['fix_state']} |\n"
if output_path:
Path(output_path).write_text(report)
print(f"Report written to {output_path}")
return report
def gate_check(vulns: list, max_critical: int = 0, max_high: int = 0) -> bool:
"""Evaluate vulnerabilities against gate thresholds. Returns True if pass."""
summary = severity_summary(vulns)
passed = True
if summary["Critical"] > max_critical:
print(f"GATE FAIL: {summary['Critical']} critical vulnerabilities (max: {max_critical})")
passed = False
if summary["High"] > max_high:
print(f"GATE FAIL: {summary['High']} high vulnerabilities (max: {max_high})")
passed = False
if passed:
print("GATE PASS: Vulnerability thresholds met")
return passed
def main():
parser = argparse.ArgumentParser(description="Grype Container Image Scanner Utility")
parser.add_argument("image", help="Container image reference to scan")
parser.add_argument("--report", "-r", help="Output report file path (markdown)")
parser.add_argument("--json-output", "-j", help="Output raw JSON results to file")
parser.add_argument("--max-critical", type=int, default=0, help="Max allowed critical vulns (default: 0)")
parser.add_argument("--max-high", type=int, default=0, help="Max allowed high vulns (default: 0)")
parser.add_argument("--gate", action="store_true", help="Enable gate check mode")
args = parser.parse_args()
print(f"Scanning {args.image}...")
scan_data = run_grype_scan(args.image)
vulns = parse_vulnerabilities(scan_data)
if args.json_output:
Path(args.json_output).write_text(json.dumps(scan_data, indent=2))
print(f"JSON results written to {args.json_output}")
report = generate_report(args.image, vulns, args.report)
if not args.report:
print(report)
summary = severity_summary(vulns)
print(f"\nSummary: C={summary['Critical']} H={summary['High']} M={summary['Medium']} L={summary['Low']}")
if args.gate:
passed = gate_check(vulns, args.max_critical, args.max_high)
sys.exit(0 if passed else 1)
if __name__ == "__main__":
main()
Assets 1
template.mdtext/markdown · 2.1 KBKeep exploring