infract
v1.0.3
Published
Detect permission and linking gaps in SST/Pulumi projects before deployment
Readme
infract
Static analysis tool for SST projects that detects permission and resource linking gaps before deployment.
Catches issues like:
- A Lambda handler references
Resource.MyTable.namebutMyTableisn't in the function'slinkarray - A function makes AWS SDK calls (e.g.,
dynamodb:Query) but lacks the required IAM permissions - A queue subscriber calls
ses:SendEmailbut no SES permission is granted
No AWS credentials or deployment required. Runs entirely against your source code.
Install
npm install infract
# or
bun add infractUsage
Run from your SST project root:
npx infractOptions
| Flag | Description |
|------|-------------|
| --explain | Verbose step-by-step narration of the analysis |
| --no-warnings | Suppress warnings, only show errors |
| --strict | Treat warnings as errors (exit code 1) |
| --filter <pattern> | Filter functions by name (supports * wildcard) |
| --format <format> | Output format: console (default) or json |
| --dir <path> | Path to SST project root (default: current directory) |
Examples
# Quick check — errors only
npx infract --no-warnings
# Detailed analysis of everything
npx infract --explain
# Only check billing routes
npx infract --filter "BillingApi:*"
# JSON output for CI
npx infract --format json --strict
# Point to a different project
npx infract --dir /path/to/my-sst-projectWhat it detects
Unlinked Resources (ERROR)
When your handler code references Resource.X.name but X isn't linked to that function:
ERROR: Unlinked Resource Usage
Function: Api:GET /v1/devices
references Resource.ExternalDevice.name but "ExternalDevice" is not linked to this function
Location: packages/core/services/device/getDevices.ts:15
Fix: Add the ExternalDevice resource to this function's link arrayMissing Permissions (ERROR/WARNING)
When your handler makes AWS SDK calls that require permissions not granted to the function:
ERROR: Missing IAM Permission
Function: BillingErrorQueue:subscriber
calls SendEmailCommand but lacks ses:SendEmail permission
Location: packages/handlers/billing/emailHandler.ts:32
Fix: Add { actions: ["ses:SendEmail"], resources: ["*"] } to this function's permissionsIf the function has sts:AssumeRole (cross-account access), missing permissions are downgraded to warnings since the assumed role may provide them.
How it works
See howItWorks.md for a deeper walkthrough. Short version:
- Parses
sst.config.ts— follows all imports and re-exports via the TypeScript compiler API to discover your full infrastructure definition - Extracts resources — identifies Functions, API routes (
api.route()), queue subscribers (.subscribe()), DynamoDB tables, S3 buckets, queues, secrets, and linkables - Resolves links — maps variable names to SST resource names (e.g.,
billingAccountTable→ExternalBillingAccount), expands array variables (allLinkables), and resolves spread configs (...crossAccountTransform) - Resolves permissions — collects explicit permissions from
permissions: [...],transform.route.handler.permissions, and spread objects, plus auto-grants from linked resources (linking aDynamoauto-grantsdynamodb:*,Bucketgrantss3:*,Queuegrantssqs:*) - Scans handler source code — walks the AST, following local imports recursively into service/lib modules. Detects:
- AWS SDK v3 command usage (
new GetItemCommand(...),new SendEmailCommand(...), etc.) @aws-sdk/lib-dynamodbsimplified commands (GetCommand,PutCommand, etc.)Resource.X.name/Resource.X.urlreferences
- AWS SDK v3 command usage (
- Compares what the code needs vs what the infrastructure provides, and reports the gaps
Supported SST patterns
new sst.aws.Function()new sst.aws.ApiGatewayV2()withapi.route()callsnew sst.aws.Queue()with.subscribe()callsnew sst.aws.Dynamo(),new sst.aws.Bucket()new sst.Secret(),new sst.Linkable()addAuthRoute()helper functionslink: [table, bucket, ...allLinkables]— variable refs, spreads, and array variablespermissions: [{ actions: [...], resources: [...] }]transform: { route: { handler: { permissions: [...] } } }- Spread config objects (
...crossAccountTransform,...envAndPermissions) - Factory functions (
const myTable = createTableLinkable('ExternalTable', ...))
Supported AWS SDK commands
50+ commands across these services:
| Service | Commands | |---------|----------| | DynamoDB | GetItem, PutItem, UpdateItem, DeleteItem, Query, Scan, BatchGetItem, BatchWriteItem, TransactGetItems, TransactWriteItems + lib-dynamodb equivalents | | S3 | GetObject, PutObject, DeleteObject, ListObjects, HeadObject, CopyObject, DeleteObjects, CreateMultipartUpload | | SES | SendEmail, SendRawEmail, SendBulkEmail, SendTemplatedEmail | | SQS | SendMessage, ReceiveMessage, DeleteMessage, SendMessageBatch | | SNS | Publish, Subscribe | | EventBridge | PutEvents | | Secrets Manager | GetSecretValue, PutSecretValue | | SSM | GetParameter, PutParameter, GetParametersByPath |
JSON output
Use --format json for programmatic consumption:
{
"summary": {
"totalResources": 23,
"totalFunctions": 16,
"totalErrors": 2,
"totalWarnings": 0
},
"functions": [...],
"violations": [
{
"severity": "error",
"type": "unlinked-resource",
"resource": "Api:GET /v1/devices",
"message": "references Resource.ExternalDevice.name but \"ExternalDevice\" is not linked",
"suggestion": "Add the ExternalDevice resource to this function's link array",
"filePath": "packages/core/services/device/getDevices.ts",
"lineNumber": 15
}
]
}CI/CD integration
# Fail the build if any errors are found
npx infract --no-warnings
# Fail on warnings too
npx infract --strict
# JSON for parsing in scripts
npx infract --format json --no-warnings | jq '.summary.totalErrors'Development
bun install
bun run dev # run from source
bun test # run tests
bun run build # compile to dist/License
MIT
