Skip to main content

Findings API

Findings are the core output of Infracast's compliance engine. Each finding represents a rule violation — a specific resource failing a compliance check against one or more frameworks. Findings include the affected resource, the control it violates, severity, and a remediation recommendation.


Endpoints

MethodPathDescriptionPermission
GET/api/v1/tenants/{tenantID}/findingsList findings with filtersfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}Get a findingfindings:read
POST/api/v1/tenants/{tenantID}/findings/runRun the audit engineaudit:run
GET/api/v1/tenants/{tenantID}/findings/scoredFindings with risk scoresfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}/remediationAI remediation stepsfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}/terraform-patchTerraform fixfindings:read
POST/api/v1/tenants/{tenantID}/findings/{findingID}/acceptAccept riskfindings:write
POST/api/v1/tenants/{tenantID}/findings/{findingID}/resolveMark resolvedfindings:write

List Findings

GET /api/v1/tenants/{tenantID}/findings

Returns a paginated, filterable list of compliance findings.

Query Parameters

ParameterTypeDescription
frameworkstringFilter by framework ID (e.g., nist-800-53, cis-aws, pci-dss)
severitystringComma-separated severity levels: CRITICAL,HIGH,MEDIUM,LOW,INFO
statusstringopen, resolved, accepted, all (default: open)
resource_typestringFilter by node type (e.g., aws.s3.bucket)
control_idstringFilter by control ID (e.g., AC-2, 5.2)
searchstringSearch in title, description, or resource ID
pageintPage number (default: 1)
per_pageintResults per page (default: 50, max: 500)
sortstringSort field: severity, created_at, resource_id. Prefix with - for descending

Example Requests

# All critical and high findings across all frameworks
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?severity=CRITICAL,HIGH"

# NIST 800-53 findings, sorted by severity
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?framework=nist-800-53&sort=severity"

# Open PCI DSS findings for S3 buckets
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?framework=pci-dss&resource_type=aws.s3.bucket&status=open"

Example Response

{
"items": [
{
"id": "NIST-AC-2-STALE-ACCESS-KEY-aws:us-east-1:aws.iam.user:svc-deploy",
"rule_id": "NIST-AC-2-STALE-ACCESS-KEY",
"framework": "nist-800-53",
"control_id": "AC-2",
"severity": "HIGH",
"status": "open",
"title": "IAM access key unused for 90+ days",
"description": "IAM user 'svc-deploy' has an access key unused for 127 days (≥90).",
"resource_id": "aws:us-east-1:aws.iam.user:svc-deploy",
"resource_type": "aws.iam.user",
"resource_name": "svc-deploy",
"region": "us-east-1",
"account_id": "123456789012",
"remediation": "Deactivate or delete access keys unused for 90+ days per NIST AC-2.",
"first_seen": "2024-02-15T10:00:00Z",
"last_seen": "2024-03-16T08:30:00Z",
"created_at": "2024-02-15T10:00:00Z"
},
{
"id": "CIS-AWS-5.2-aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"rule_id": "CIS-AWS-5.2",
"framework": "cis-aws",
"control_id": "5.2",
"severity": "HIGH",
"status": "open",
"title": "Security group allows unrestricted SSH access",
"description": "Security group 'web-servers-sg' (sg-0abc123) has an inbound rule permitting SSH (port 22) from 0.0.0.0/0.",
"resource_id": "aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"resource_type": "aws.ec2.security_group",
"resource_name": "web-servers-sg",
"region": "us-east-1",
"account_id": "123456789012",
"remediation": "Remove the 0.0.0.0/0 inbound rule for port 22. Restrict SSH to a bastion host or known admin CIDR ranges.",
"first_seen": "2024-03-10T14:00:00Z",
"last_seen": "2024-03-16T08:30:00Z",
"created_at": "2024-03-10T14:00:00Z"
}
],
"total": 287,
"page": 1,
"per_page": 50,
"total_pages": 6
}

Finding Severity

Findings are assigned one of five severity levels:

SeverityDescriptionTypical Response Time
CRITICALDirect path to data breach or account compromiseImmediate (hours)
HIGHSignificant control failure, exploitable with low effort24–48 hours
MEDIUMControl gap that increases risk but requires additional factors1–2 weeks
LOWDefense-in-depth gap, minimal immediate risk30–90 days
INFOInformational, no direct riskBacklog / best effort

Framework Mapping

A single finding can map to multiple framework controls. When a resource fails, Infracast surfaces the cross-framework impact:

GET /api/v1/tenants/acme-corp/findings/CIS-AWS-5.2-sg-0abc123

{
"id": "CIS-AWS-5.2-aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"rule_id": "CIS-AWS-5.2",
"framework_mappings": [
{ "framework": "cis-aws", "control_id": "5.2" },
{ "framework": "nist-800-53", "control_id": "SC-7" },
{ "framework": "pci-dss", "control_id": "Req 1.3.2" },
{ "framework": "soc2", "control_id": "CC6.3" }
],
...
}

Run the Audit Engine

Trigger an immediate audit run against the current infrastructure graph:

POST /api/v1/tenants/{tenantID}/findings/run
{
"framework": "nist-800-53" # Optional: run only one framework
}

# Or run all frameworks
POST /api/v1/tenants/{tenantID}/findings/run
{}

Response:

{
"job_id": "audit-job-abc123",
"status": "running",
"frameworks": ["nist-800-53", "cis-aws", "pci-dss", "soc2"],
"started_at": "2024-03-16T10:00:00Z"
}

Get AI Remediation

Infracast provides detailed, AI-enhanced remediation steps for each finding:

GET /api/v1/tenants/{tenantID}/findings/{findingID}/remediation
{
"finding_id": "CIS-AWS-5.2-sg-0abc123",
"summary": "Remove the unrestricted SSH inbound rule and restrict to specific CIDR ranges.",
"steps": [
{
"order": 1,
"action": "Identify all EC2 instances using this security group",
"command": "aws ec2 describe-instances --filters 'Name=instance.group-id,Values=sg-0abc123'"
},
{
"order": 2,
"action": "Revoke the unrestricted SSH rule",
"command": "aws ec2 revoke-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 0.0.0.0/0"
},
{
"order": 3,
"action": "Add a restricted rule for your admin CIDR range",
"command": "aws ec2 authorize-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 10.0.0.0/8"
}
],
"terraform_patch_available": true
}

Get Terraform Patch

GET /api/v1/tenants/{tenantID}/findings/{findingID}/terraform-patch
{
"finding_id": "CIS-AWS-5.2-sg-0abc123",
"patch": "--- a/security_groups.tf\n+++ b/security_groups.tf\n@@ -15,8 +15,8 @@\n ingress {\n- from_port = 22\n- to_port = 22\n- protocol = \"tcp\"\n- cidr_blocks = [\"0.0.0.0/0\"]\n+ from_port = 22\n+ to_port = 22\n+ protocol = \"tcp\"\n+ cidr_blocks = [\"10.0.0.0/8\"] # Restricted to internal only\n }"
}

Accept Risk

When a finding cannot be remediated (legacy system, compensating control, risk accepted):

POST /api/v1/tenants/{tenantID}/findings/{findingID}/accept
{
"justification": "Legacy jump server scheduled for decommission Q3 2024. Compensating control: Security group only applied to isolated legacy VLAN.",
"review_date": "2024-09-30",
"approved_by": "ciso@company.com"
}

Accepted findings are excluded from compliance scoring but remain visible to auditors with full justification history.


Real-Time Findings Stream

Subscribe to findings as they are created (Server-Sent Events):

GET /api/v1/tenants/{tenantID}/findings/stream

# Events are streamed as:
data: {"event":"finding.created","finding":{"id":"...","severity":"HIGH",...}}
data: {"event":"finding.resolved","finding_id":"...","resolved_at":"2024-03-16T10:05:00Z"}

Findings Summary by Framework

GET /api/v1/tenants/{tenantID}/compliance/summary

{
"overall_score": 83,
"total_findings": 287,
"by_severity": {
"CRITICAL": 3,
"HIGH": 47,
"MEDIUM": 164,
"LOW": 73
},
"by_framework": {
"nist-800-53": { "score": 82, "findings": 94 },
"cis-aws": { "score": 84, "findings": 62 },
"pci-dss": { "score": 88, "findings": 78 },
"soc2": { "score": 91, "findings": 52 }
}
}

Python Example

from infracast import InfracastClient

client = InfracastClient(api_url="https://api.infracast.io", api_token="your-token")

# Get all critical findings
findings = client.findings.list(
tenant="acme-corp",
severity=["CRITICAL", "HIGH"],
status="open"
)

# Group by framework
by_framework = {}
for finding in findings:
fw = finding.framework
by_framework.setdefault(fw, []).append(finding)

for framework, fw_findings in by_framework.items():
print(f"{framework}: {len(fw_findings)} critical/high findings")

# Accept a risk
client.findings.accept_risk(
tenant="acme-corp",
finding_id="CIS-AWS-5.2-sg-0abc123",
justification="Legacy system, decommission scheduled Q3 2024",
review_date="2024-09-30"
)

Next Steps