| name | review-kics |
| description | Review KICS Infrastructure as Code scan results to identify exploitable misconfigurations. Use when analyzing KICS output, triaging IaC findings, or when the user has kics-results directories to review. Automatically performs safe verification of discovered resources using read-only operations. |
Review KICS IaC Findings
Expert analysis workflow for triaging KICS Infrastructure as Code scan results. Critical distinction: IaC findings are reconnaissance, not vulnerabilities. The code describes intended infrastructure - you must verify actual deployment.
Project Structure
All paths are relative to the project root (working directory):
threat_hunting/ # Project root (working directory)
├── <org-name>/ # Cloned repositories (e.g., jitsi/, tronprotocol/)
│ └── <repo-name>/ # Individual repository source code
├── findings/<org-name>/ # All scan results for an organization
│ ├── semgrep-results/
│ ├── trufflehog-results/
│ ├── artifact-results/
│ ├── kics-results/ # KICS JSON output files
│ │ └── <repo-name>.json
│ └── reports/ # Final consolidated reports
└── scripts/ # ALL extraction and scanning scripts
Repository source code location: <org-name>/<repo-name>/ (e.g., jitsi/infra-provisioning/terraform/...)
Scan results location: findings/<org-name>/kics-results/<repo-name>.json
CRITICAL: Do NOT Write Custom Scripts
All extraction scripts already exist in ./scripts/. Never write custom jq, Python, or shell scripts to parse findings. The existing scripts handle:
- Complex JSON/NDJSON parsing
- Large file handling
- Edge cases and error handling
- Consistent output formatting
Available extraction scripts:
./scripts/extract-semgrep-findings.sh- Parse semgrep results./scripts/extract-trufflehog-findings.sh- Parse trufflehog results./scripts/extract-artifact-findings.sh- Parse artifact results./scripts/extract-kics-findings.sh- Parse KICS results
If you need functionality not provided by existing scripts, ask the user to update the scripts rather than writing one-off solutions.
The IaC Reality Check
IaC Finding (code) → Resource Identifier → Auto-Verify → Reportable Finding
↑ ↑ ↑
KICS scan Extraction Safe GET requests
CRITICAL: Always Use the Extraction Script First
MANDATORY: Before doing ANY manual analysis, run the extraction script:
./scripts/extract-kics-findings.sh <org-name>
Then extract resource identifiers:
./scripts/extract-kics-findings.sh <org-name> resources
Quick Reference
# Extract from findings/ directory (per-repo files)
./scripts/extract-kics-findings.sh <org-name> # All repos, summary
./scripts/extract-kics-findings.sh <org-name> resources # Resource identifiers
./scripts/extract-kics-findings.sh <org-name> count # Counts only
# Extract from catalog scans (merged gzipped files)
./scripts/extract-kics-findings.sh <org-name> --catalog # Latest scan
./scripts/extract-kics-findings.sh <org-name> --scan 2025-12-24 # Specific scan
Data Sources:
findings/<org>/kics-results/*.json- Per-repo results (uncompressed)catalog/tracked/<org>/scans/<timestamp>/kics.json.gz- Merged scan (gzipped)
Workflow
Step 1: Extract Findings and Verify Counts
# Step 1a: Get counts to verify total findings
./scripts/extract-kics-findings.sh <org-name> count
# Step 1b: Get summary of all findings
./scripts/extract-kics-findings.sh <org-name>
# Or from catalog scans
./scripts/extract-kics-findings.sh <org-name> --catalog
# Step 1c: Extract resource identifiers for verification
./scripts/extract-kics-findings.sh <org-name> resources
CRITICAL COUNT VERIFICATION: The summary output shows "Total: N findings" at the bottom. This MUST match the sum of all counts from step 1a (summing the 'total' column). If they don't match, the extraction may be truncating results - investigate before proceeding.
Note: Findings are sorted by severity (HIGH first, then MEDIUM, then LOW). All HIGH severity findings appear at the top of the output for immediate attention.
Step 2: Read IaC Files to Extract Resource Names
For each HIGH/MEDIUM finding, read the IaC file to extract actual resource identifiers:
Terraform - Look for:
bucket = "actual-bucket-name"name = "resource-name"- Hardcoded values (not
var.orlocal.references)
Kubernetes - Look for:
metadata.name- Service
type: LoadBalancerorNodePort - Ingress hostnames
CloudFormation - Look for:
BucketName:- Resource logical names and properties
Step 3: Automatic Safe Verification
YOU MUST AUTOMATICALLY RUN THESE VERIFICATIONS for any discovered resource identifiers. All commands below are safe, read-only operations.
S3 Buckets (Safe - Anonymous HEAD/LIST)
For each S3 bucket name found:
# Check if bucket exists and is public (HEAD request)
curl -s -I "https://BUCKET_NAME.s3.amazonaws.com/" | head -20
# Check bucket region
curl -s -I "https://BUCKET_NAME.s3.amazonaws.com/" 2>&1 | grep -i "x-amz-bucket-region"
# Try anonymous listing (read-only)
curl -s "https://BUCKET_NAME.s3.amazonaws.com/?max-keys=5"
Interpreting results:
HTTP 200+ XML listing = PUBLIC BUCKET (reportable!)HTTP 403 AccessDenied= Bucket exists but not publicHTTP 404 NoSuchBucket= Bucket doesn't existHTTP 301= Bucket exists, different region
GCS Buckets (Safe - Anonymous GET)
# Check if bucket is public
curl -s "https://storage.googleapis.com/BUCKET_NAME?maxResults=5"
Azure Blob Storage (Safe - Anonymous GET)
# Check if container is public (format: account.blob.core.windows.net/container)
curl -s "https://ACCOUNT.blob.core.windows.net/CONTAINER?restype=container&comp=list&maxresults=5"
Web Endpoints / APIs (Safe - GET only)
For discovered hostnames, endpoints, or IPs:
# Check if endpoint responds (GET request only)
curl -s -I -m 10 "https://HOSTNAME/" | head -10
# Check HTTP (if HTTPS fails)
curl -s -I -m 10 "http://HOSTNAME/" | head -10
DNS Verification (Safe - Lookup only)
# Check if hostname resolves
dig +short HOSTNAME
# Check for internal hostnames that might resolve externally
dig +short internal.company.local
Kubernetes API (Safe - Unauthenticated probe only)
If you find a potential K8s API endpoint:
# Check if API is exposed (GET only)
curl -s -k -m 10 "https://HOSTNAME:6443/version"
curl -s -k -m 10 "https://HOSTNAME:443/version"
DO NOT run kubectl commands or attempt authentication.
Step 4: Triage Findings
Automatically Verify These (HIGH Priority)
| Finding Type | Extract | Verify With |
|---|---|---|
| S3 Public ACL | Bucket name from bucket = |
curl -s https://BUCKET.s3.amazonaws.com/ |
| GCS Public | Bucket name | curl -s https://storage.googleapis.com/BUCKET |
| Azure Public Blob | Account + container | curl -s https://ACCOUNT.blob.core.windows.net/CONTAINER?comp=list |
| Exposed Service | Hostname/IP + port | curl -s -I https://HOST:PORT/ |
| K8s API Exposed | Endpoint | curl -s -k https://HOST:6443/version |
Skip These (Not Verifiable via Safe Methods)
- IAM policy issues (requires authenticated access)
- Encryption-at-rest issues (requires data access)
- Internal network configurations
- Container privilege issues (requires runtime access)
Step 5: Document Results
For each verification, record:
Resource: s3://company-backup-bucket
Source: terraform/storage.tf:23
Verification: curl -s "https://company-backup-bucket.s3.amazonaws.com/?max-keys=5"
Result: [PASTE OUTPUT]
Status: PUBLIC / PRIVATE / NOT_FOUND
Step 6: Report Confirmed Findings
Only report findings you verified with actual evidence.
## VERIFIED - Public S3 Bucket
**Repository**: org/repo-name
**IaC File**: terraform/storage.tf:45
**Bucket Name**: company-data-bucket
**IaC Code**:
```hcl
resource "aws_s3_bucket" "data" {
bucket = "company-data-bucket"
acl = "public-read"
}
Verification Command:
curl -s "https://company-data-bucket.s3.amazonaws.com/?max-keys=10"
Verification Output:
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult>
<Name>company-data-bucket</Name>
<Contents>
<Key>backup-2024-01.sql</Key>
<Size>1234567</Size>
</Contents>
...
</ListBucketResult>
Impact: Bucket is publicly listable and contains database backups
Recommendation:
- Remove public ACL immediately
- Review bucket contents for sensitive data exposure
- Enable bucket policy to restrict access
## Safe Verification Commands Reference
### ALWAYS SAFE (Run automatically)
| Resource Type | Command | What It Does |
|---------------|---------|--------------|
| S3 Bucket | `curl -s -I https://BUCKET.s3.amazonaws.com/` | HEAD request, checks existence |
| S3 Bucket | `curl -s https://BUCKET.s3.amazonaws.com/?max-keys=5` | Anonymous list (5 items max) |
| GCS Bucket | `curl -s https://storage.googleapis.com/BUCKET?maxResults=5` | Anonymous list |
| Azure Blob | `curl -s https://ACCT.blob.core.windows.net/CONTAINER?comp=list&maxresults=5` | Anonymous list |
| Web Endpoint | `curl -s -I -m 10 https://HOST/` | HEAD request with timeout |
| DNS | `dig +short HOSTNAME` | DNS lookup only |
| K8s API | `curl -s -k -m 10 https://HOST:6443/version` | Version check (no auth) |
### NEVER RUN (Potentially harmful)
- `aws s3 cp` or `aws s3 sync` - Downloads files
- `nmap` - Active port scanning (may trigger alerts)
- `kubectl` with any write operations
- `docker` commands against remote hosts
- Any POST/PUT/DELETE requests
- Brute force or enumeration scripts
- Anything requiring or attempting authentication
## False Positive Indicators
**Skip verification for:**
- Resources in `test/`, `examples/`, `samples/` directories
- Parameterized values (`var.bucket_name`, `${var.name}`)
- Obviously fake names (`example-bucket`, `my-test-bucket`)
- Template files (`.example`, `.template`)
**Always verify:**
- Hardcoded bucket/resource names
- Production directories (`prod/`, `production/`)
- CI/CD configurations
- Helm values.yaml with real-looking values
## Output Summary Format
After verification, provide a summary:
KICS Review Summary:
Verified Exposures (Reportable)
- PUBLIC S3: company-backup-bucket (terraform/storage.tf:23)
- Contains: database backups, config files
- Evidence: [curl output showing listing]
Verified Private (Not Reportable)
- s3://company-internal - Returns 403 AccessDenied
- s3://company-logs - Returns 403 AccessDenied
Not Found (IaC May Be Stale)
- s3://old-bucket-name - Returns 404 NoSuchBucket
Could Not Verify (No Resource Identifier)
- terraform/network.tf:45 - Security group rule, no target IP to test
Skipped (Test/Example Code)
- examples/demo-bucket - In examples/ directory
## Guidelines
- **Automatically verify** all discovered resource identifiers using safe commands
- **Only report** findings with verified evidence
- **Document everything** - show the command and its output
- **Be conservative** - if curl returns 403, it's working as intended
- **Check context** - same bucket name in test/ vs prod/ means different things
- IaC code ≠ deployed infrastructure - verification is mandatory