Your security team triaged 200 findings last week. 12 of them were unique.
Collapse security findings by root cause. Takes Security Hub exports, SARIF files, or live API data and groups duplicate findings into actionable fix targets.
200 findings → 12 root causes. Fix the source, not the symptoms.
pip install findingfold
# From exported JSON
findingfold findings.json
# From Security Hub API
pip install findingfold[aws]
findingfold --from-hub --region us-east-1
# From SARIF (Semgrep, Snyk, Trivy, GitHub Code Scanning)
findingfold results.sarif.json --sariffindingfold runs 6 fold rules in priority order. First match wins.
| Priority | Rule | Groups by | Example |
|---|---|---|---|
| 1 | AMI | Source AMI ID | 47 instances from ami-0abc123 → 1 fix |
| 2 | CloudFormation | Stack name | 12 resources in payment-service stack → 1 fix |
| 3 | IaC Tags | Terraform/CDK/Pulumi module | 6 SGs from vpc-baseline module → 1 fix |
| 4 | Security Group | Identical rule patterns | 8 SGs with same open port 22 → 1 fix |
| 5 | IAM Policy | Shared policy ARN | 10 roles with LegacyAdminAccess → 1 fix |
| 6 | Title Fingerprint | Normalized finding title | 20 "SSHBruteForce" on different instances → 1 fix |
Each group tells you what to fix (the AMI, the template, the module), not just what's wrong.
$ findingfold tests/fixtures/sample_findings.json --explain
findingfold report
════════════════════════════════════════════════════════════
📊 10 findings → 4 root causes (2.5x compression)
#1 [CRITICAL] 🔴 Score: 70.8 ──────────────────────────────
AMI ami-0abc123def456789a — CVE-2026-1234 - vulnerable package openssl
│ 3 findings across 3 resources
│ Accounts: 111122223333, 444455556666
│ Regions: us-east-1, us-west-2
│ First seen: 2026-02-15
│ Fix: Rebuild AMI ami-0abc123def456789a with patched packages, then rotate instances
│ Grouping rationale:
│ • finding-ami-1 → ami: ImageId=ami-0abc123def456789a
│ • finding-ami-2 → ami: ImageId=ami-0abc123def456789a
│ • finding-ami-3 → ami: ImageId=ami-0abc123def456789a
#2 [HIGH] 🟠 Score: 55.0 ──────────────────────────────
Terraform module 'vpc-baseline' has findings
│ 2 findings across 2 resources
│ Accounts: 111122223333, 222233334444
│ Regions: eu-west-1, us-east-1
│ First seen: 2026-03-10
│ Fix: Update terraform module 'vpc-baseline' and redeploy
│ Grouping rationale:
│ • finding-tf-1 → iac: tf_module=vpc-baseline
│ • finding-tf-2 → iac: tf_module=vpc-baseline
#3 [HIGH] 🟠 Score: 50.8 ──────────────────────────────
CloudFormation stack 'payment-service' has misconfigurations
│ 3 findings across 3 resources
│ Accounts: 111122223333
│ Regions: us-east-1
│ First seen: 2026-03-01
│ Fix: Update template for stack 'payment-service' and redeploy
│ Grouping rationale:
│ • finding-cfn-1 → cloudformation: aws:cloudformation:stack-name=payment-service
│ • finding-cfn-2 → cloudformation: aws:cloudformation:stack-name=payment-service
│ • finding-cfn-3 → cloudformation: aws:cloudformation:stack-name=payment-service
#4 [MEDIUM] 🟡 Score: 25.0 ──────────────────────────────
UnauthorizedAccess:EC2/SSHBruteForce on *
│ 2 findings across 2 resources
│ Accounts: 111122223333
│ Regions: us-east-1
│ First seen: 2026-04-01
│ Fix: Remediate: UnauthorizedAccess:EC2/SSHBruteForce on *
│ Grouping rationale:
│ • finding-gd-1 → title: title fingerprint (normalized from: UnauthorizedAccess:EC2/SSHBruteForce on i-0ddd4...)
│ • finding-gd-2 → title: title fingerprint (normalized from: UnauthorizedAccess:EC2/SSHBruteForce on i-0eee5...)
════════════════════════════════════════════════════════════| Source | Command | Requires |
|---|---|---|
| JSON export | findingfold findings.json |
Nothing |
| Security Hub API | findingfold --from-hub |
boto3, AWS credentials |
| SARIF | findingfold results.sarif --sarif |
Nothing |
SARIF support means findingfold works with GitHub Code Scanning, Semgrep, Snyk, Checkov, Trivy, and any tool that outputs SARIF.
findingfold <source> [options]
Sources:
findings.json Exported Security Hub JSON
--from-hub Security Hub API (boto3)
--sarif SARIF format input
Filtering:
--min-severity LEVEL CRITICAL, HIGH, MEDIUM, LOW (default: LOW)
--min-group-size N Only show groups with N+ findings (default: 2)
--include-suppressed Include SUPPRESSED findings
--max-findings N Limit ingested findings (default: 10000)
--filter-fp LLM-powered false positive filter (before fold)
--fp-backend BACKEND anthropic or openai (auto-detects from env)
Grouping:
--rules RULES ami, cloudformation, iac, security_group, iam, title
--enrich Backfill AMI IDs via AWS API (before fold)
--explain Show why each finding was grouped
Output:
--format FORMAT terminal, json, markdown, csv
-o, --output FILE Write to file
-v, --verbose Show individual findings in groups
API source:
--region REGION AWS region
--accounts 111,222 Comma-separated account IDs
See exactly why each finding was grouped where it was:
findingfold findings.json --explain #1 [CRITICAL] 🔴 Score: 85
AMI ami-0abc123def — CVE-2026-1234
│ Grouping rationale:
│ • finding-001 → ami: ImageId=ami-0abc123def
│ • finding-002 → ami: ImageId=ami-0abc123def
│ • finding-003 → ami: ImageId=ami-0abc123def
Security Hub findings from Config rules often lack AMI IDs. Use --enrich to backfill via describe-instances before folding:
findingfold --from-hub --enrich --region us-east-1This gives significantly better AMI grouping than raw JSON exports. Requires ec2:DescribeInstances permission.
Use --filter-fp to run an LLM-powered false positive filter before folding. The LLM classifies each finding based on resource context, tags, severity, and common FP patterns (dev/test resources, informational findings, terminated instances).
# Filter FPs, then fold
findingfold findings.json --filter-fp
# With explicit backend
findingfold findings.json --filter-fp --fp-backend anthropic
# Verbose — see which findings were removed and why
findingfold findings.json --filter-fp -vThe filter is conservative — when in doubt, findings are kept. Requires ANTHROPIC_API_KEY or OPENAI_API_KEY. Findings are sent in batches of 20 to minimize LLM calls.
Pipeline order: ingest → filter → enrich → FP filter → fold → report
# Terminal (default) — human-readable
findingfold findings.json
# JSON — for CI pipelines and automation
findingfold findings.json --format json -o report.json
# Markdown — for PRs, Confluence, Jira
findingfold findings.json --format markdown -o report.md
# CSV — for spreadsheets and ticketing imports
findingfold findings.json --format csv -o report.csvfindingfold ships as an MCP server so you can install it on any personal AI agent (Kiro, Claude Code, Cline, etc.) and fold findings conversationally.
pip install findingfold[mcp]Kiro / Claude Code / any MCP client:
{
"mcpServers": {
"findingfold": {
"command": "findingfold-mcp"
}
}
}| Tool | Description |
|---|---|
fold_findings |
Fold a Security Hub JSON export by root cause |
fold_sarif |
Fold a SARIF file (Semgrep, Snyk, Trivy, etc.) |
fold_from_security_hub |
Pull live from Security Hub API and fold (requires boto3 + credentials) |
You: Fold my security findings at ~/exports/findings.json, only show HIGH and above
Agent calls
fold_findings(file_path="~/exports/findings.json", min_severity="HIGH")Agent: Found 47 findings collapsed into 6 root causes (7.8x compression). The top issue is AMI ami-0abc123 with 12 findings across 3 accounts...
mcpfw enforces policies on MCP tool calls. Wrap findingfold-mcp to control which findings data your agent can access:
mcpfw --policy policy.yaml -- findingfold-mcpHow is this different from Security Hub's built-in grouping? Security Hub groups by finding type. findingfold groups by root cause. "50 instances have CVE-X" is a finding type. "AMI ami-abc123 needs patching" is a root cause.
Does it modify my findings? No. Read-only. It downloads/loads findings, groups them in memory, and outputs a report.
What about multi-account?
Works out of the box. If you export from a delegated admin Security Hub, findings already include AwsAccountId. The report shows which accounts are affected per group.
Can I write custom rules? Yes. See docs/custom-rules.md.
What if a finding matches multiple rules? First match wins. Rules run in priority order (AMI → CFN → IaC → SG → IAM → Title). More specific rules run first.
- Create
findingfold/rules/your_rule.pyimplementingBaseRule - Add it to the rule list in
fold.py - Return a match dict with
key,root_cause,fix_target,recommendation
- Create
findingfold/sources/your_source.pywith aload()function - Convert findings to Security Hub format (or close enough for the rules to work)
- Wire it into
cli.py
Non-Commercial Source License — see LICENSE