How to set up Terraform security scanning for your GitHub pull requests
audytx is a deterministic Terraform security engine for AWS, hand-written in Rust with no model in the loop. Identical input, identical findings, and every verdict cites the exact check and the relationship behind it.
Pick a surface — the engine is the same
The same reasoning engine runs at code time, review time, and plan time. It flags issues but never blocks a merge.
GitHub App
Read-only install, one comment per PR plus SARIF. Install →
MCP server
The same engine your coding agent calls before the PR exists. Connect →
Plan enrichment
Opt-in: upload a resolved terraform plan for exact values. Enable →
Install the GitHub App
Click Install on GitHub, pick the repository, and approve — no CI wiring, no runners. The App requests these scopes:
- Contents: Read-only — to read your repository's
.tf/.tfvarsfiles. audytx reads beyond the diff, because cross-resource reasoning needs the unchanged files too. - Pull requests: Read and write — to post the comment.
- Metadata: Read-only — required by GitHub for every App.
- Code scanning alerts: Read and write — for SARIF upload (when enabled).
Subscribed events: pull_request. We listen for
opened, synchronize, and reopened — other
actions (labels, edits, etc.) are ignored to keep noise down.
MCP server setup (coding agents)
The same engine is a hosted MCP server any MCP-capable agent (Claude Code, Cursor, and friends) can call before the PR exists. All you need is a free Client ID:
claude mcp add --transport http audytx https://audytx.com/mcp \
--header "X-Client-ID: YOUR_CLIENT_ID"
Clients and gateways that inject standard OAuth-style headers can send
the same Client ID as Authorization: Bearer YOUR_CLIENT_ID
instead; both carriers are accepted. Auth failures return HTTP
401 with a WWW-Authenticate header per the
MCP authorization spec, with RFC 9728 metadata served at
/.well-known/oauth-protected-resource. Setup guides for
Cursor, VS Code, JetBrains, Claude Desktop, and any
stdio-only client (via mcp-remote) live at
audytx.com/mcp#setup.
Two core tools:
-
scan_terraform— pass all.tf/.tfvarsfiles, get findings with file:line evidence, severity, fix snippets, and the findings the context layer suppressed as false positives, each with its rationale. The response also carries a per-framework compliance-coverage summary. Pass the complete file set: cross-resource reasoning (IAM trust graphs, attack paths, DLQ identity) can't see files you omit. -
autofix_terraform— audytx applies its sound fixes server-side (only precisely line-anchored replacements, held to the same bar as GitHub one-click suggestions, never a corrupting edit), re-scans, and loops until nothing auto-fixable remains. Returns the fixed file contents plus the findings left for the agent to fix itself.
Limits: 1000 files / 10 MB per call. File contents are processed in
memory and never persisted — same privacy posture as the
GitHub App path. scan_terraform also
accepts an optional plan argument (your
terraform show -json output) for the plan enrichment described
below. Handy when the agent has already run a plan.
Terraform plan scanning (optional)
Static parsing can't resolve values behind variables, count/for_each
expansion, or resources inside modules. Hand audytx the resolved plan and
it scans with those final values folded in. Strictly opt-in and
additive — the normal scan keeps working exactly as before; this
only ever adds signal.
Add this workflow. It authenticates with the run's GitHub OIDC token — nothing to paste, no secret to store; audytx verifies the token and scopes it to your repo automatically:
# .github/workflows/audytx-plan.yml
on: [pull_request]
permissions:
contents: read
id-token: write # lets the job mint an OIDC token
jobs:
audytx-plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- run: terraform init -backend=false && terraform plan -out=tf.plan && terraform show -json tf.plan > plan.json
- name: Upload plan to audytx
env:
AUD: https://audytx.com
run: |
OIDC=$(curl -sH "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=$AUD" | jq -r .value)
PR=$(jq -r .number "$GITHUB_EVENT_PATH")
SHA=$(jq -r .pull_request.head.sha "$GITHUB_EVENT_PATH")
jq -n --slurpfile p plan.json \
--arg repo "$GITHUB_REPOSITORY" --argjson pr "$PR" --arg sha "$SHA" \
'{repo:$repo, pr_number:$pr, head_sha:$sha, plan:$p[0]}' > body.json
curl -sf -X POST "$AUD/plan-upload" \
-H "Authorization: Bearer $OIDC" -H "Content-Type: application/json" \
--data-binary @body.json
Limits: 5 MB per upload, and the request must declare its size with a
Content-Length header so it can be checked before the body is
read — curl sets that for you in the command above. The
pr_number and head_sha you send are checked against
the real pull request: it must still be open, and the commit must be the tip of
its branch right now, or the upload is refused. The usual innocent cause is a
branch that moved while the job ran — rerun on the new commit. The plan JSON
is processed in memory and never persisted — same posture as
your source files. The PR comment notes a 🔬 plan-enhanced scan in its
footer when plan data was used. If the upload fails for any reason, your normal
scan still stands.
HCP Terraform run task · Beta
Review your HCP Terraform (and Terraform Enterprise) runs with
the same engine, as a post-plan run task: HCP hands audytx
the plan, audytx scans it and reports passed/failed
back into the run with severity, message, and remediation per finding. The
enforcement level you pick decides what happens next — advisory warns
and continues, mandatory errors the run before apply.
Set it up in two steps:
- Create a free Client ID — the same one MCP uses.
-
In HCP Terraform, open Settings → Run Tasks → Create a run
task and enter:
-
Endpoint URL:
https://audytx.com/run-task?client_id=YOUR_CLIENT_ID - HMAC key: your Client ID again — the same UUID. Every payload is signed with it, so only HCP can trigger your task. (No separate secret to manage.)
-
Endpoint URL:
Authenticated by your Client ID and metered against the same free-tier monthly quota as MCP. The plan JSON is fetched from HCP, processed in memory, and never persisted — same privacy posture as the GitHub App path. If you're over quota, the task returns an advisory pass and skips the scan rather than blocking your run.
Start free during the open beta
Install on GitHub, point your agent at the MCP server, and your next pull request gets its first review. Free for every team while the beta runs.
What does audytx check?
266 checks across operations, security, reliability, and cost. Opinionated and AWS-only — we'd rather go deep on one cloud than shallow on five.
Context-aware reasoning
Most IaC scanners are single-resource pattern matchers: they flag a Lambda for a missing DLQ whether or not anything async invokes it. audytx pre-computes 24 cross-resource axes before evaluating each check. The four foundational ones:
DLQ identity
Which SQS queues are themselves a DLQ. A DLQ doesn't need its own DLQ.
Lambda invocation graph
Sync vs async-push vs polled-async. A Lambda DLQ only helps async-push.
Encryption variants
KMS-CMK vs AWS-managed vs service-managed SSE — all real encryption.
Data lifetime
DynamoDB TTL, S3 lifecycle, log retention. Ephemeral tables don't need PITR.
Checks consult these predicates as a post-filter — the check stays naive, the graph delivers the verdict — which is why legitimate patterns don't turn into false-positive noise.
The reasoning is visible. Findings the context layer
reclassified as false positives don't silently disappear. Each appears
in the PR comment under a collapsed 🧠 audytx reasoned about N
findings and chose not to flag them block, with the check ID, the
resource address, and a one-sentence rationale ("queue is itself a
dead-letter queue", "Lambda is not invoked async-push", "table actively
expires its own data via TTL"). The same reasoning rides into
GitHub's Code Scanning Security tab as a SARIF suppressions[]
justification — alerts there show as "closed (dismissed)" with the
rationale visible in the alert detail. A reviewer sees everything
audytx considered, including what it dismissed and why.
Live inventory of axes + the checks that consult each is available at
/status under
context_reasoning_axes.
GitHub Code Scanning integration (SARIF)
Alongside the PR comment, audytx uploads a SARIF v2.1.0 document to
GitHub's Code Scanning API at every scan. Findings land in the repo's
Security tab with file:line annotations, severity
grouping, framework-tag filter chips (aws-fsbp-s3.1,
soc2-cc6.1), and stable per-finding fingerprints so the same
alert tracks across PR pushes.
SARIF severity mapping:
error
Medium → warning
Low + Informational → note
Requirements. SARIF upload needs either a public repo or a private repo with GitHub Advanced Security. Private repos without GHAS still get the full PR comment; the Security-tab upload just no-ops.
Reasoning visible in the Security tab. When the engine
reasons a finding away as a false positive (see
Context-aware reasoning), it still goes into the
SARIF payload with a suppressions[] array carrying the
justification. GitHub shows these as closed (dismissed)
alerts — your security team gets the full record of what fired, what
audytx dismissed, and why.
Baseline suppression
Add a .audytx-baseline.yaml file to your repo root. The
expires: field is required — suppressions
without an expiry rot into never-relitigated tech debt:
ignored_findings:
- rule_id: AWS_LAMBDA_004
resource_address: aws_lambda_function.api
reason: "API GW sync invocation — DLQ is a semantic mismatch"
expires: 2026-12-31
- rule_id: AWS_IAM_001
resource_address: aws_iam_role.legacy_admin
reason: "Legacy admin role; removal scheduled Q3"
expires: 2026-09-30
An expired entry stops suppressing and the finding reappears, forcing re-justification. Suppressed findings still appear in the comment under a "Suppressed" appendix so auditors see what was acknowledged.
From a coding agent (MCP): the scan_terraform,
autofix_terraform, and dry_run_autofix tools accept
an optional baseline argument — pass the contents of your
.audytx-baseline.yaml as a string and the same baseline
suppression applies, so findings you iterate on locally match what the
PR check reports. The response itemizes each baseline-suppressed finding
(with the matched rule/resource, its reason, and its expiry) in
baseline_suppressed — a separate array from
context_suppressed, so the two suppression kinds never merge
— and reports a baseline_expired count; autofix won't touch a
finding you've suppressed. A malformed baseline is returned as an error,
never silently ignored.
Privacy Policy
Effective: 1 June 2026 · Operator: Rexstart Labs Pvt Ltd · Contact: open a support ticket
What we collect
- GitHub App path. When a pull request is opened, audytx fetches
.tfand.tfvarsfiles from your repository via the GitHub Contents API. These files are loaded into memory, analyzed, and immediately discarded. No source file content is written to any database or log. - Scan metadata. Our database records: GitHub App installation ID, repository name, PR number, engine version, check catalog version, finding count, and scan duration. No file content, no secrets, no personally identifiable information beyond the GitHub identifiers you consented to share when installing the App.
- Installation records. When the GitHub App is installed, suspended, or uninstalled, we record the installation ID, the account login and account type (user or organization) it belongs to, and the matching timestamps — the minimum needed to count active installations. No emails, no tokens, no repository lists.
- MCP path. Requests to
POST /mcpare stateless, and audytx keeps no copy of your code: file contents sent in the request body are processed in memory and never written anywhere, and no request body is logged. Successful tool calls increment a monthly usage counter keyed to your account email and write a usage-event record (tool name, account and token identifiers, client IP) for quota metering and abuse defense — never Terraform source. - Web analytics. We do not use third-party analytics scripts. We record only request counts, error rates, and latency — no user-level tracking, no cookies, no fingerprinting.
What we do not collect
- Terraform source code, HCL file contents, or any infrastructure configuration beyond what is required to produce a scan result — and that content is never persisted.
- AWS credentials, secrets, or sensitive values found in scanned files. If a hardcoded secret is detected, the finding references the file path and line number only — the secret value is never stored.
- IP addresses for tracking or profiling. Security and usage event records (sign-in, token, and MCP tool-call events) do include the client IP for abuse defense — never for tracking, analytics, or sale. Our hosting provider may also log IPs transiently at the network layer per its own privacy policy.
Data retention
- Scan metadata records are retained for 90 days, then automatically deleted.
- Removing the GitHub App from your repository or organization immediately and permanently revokes audytx's access. No separate deletion request is needed.
- To request deletion of scan metadata records for your installation, open a support ticket with your GitHub installation ID. We will delete within 7 days and confirm by reply.
Data location
All processing runs on our global network. Scan metadata is stored in a database in the US region. No data is transferred to third-party analytics, advertising, or data-broker services.
Changes
Material changes to this policy will be announced via the GitHub App release notes at least 14 days before taking effect. The effective date above is updated on each revision.
Terms of Service
Effective: 1 June 2026 · Operator: Rexstart Labs Pvt Ltd · Contact: legal@audytx.com
Acceptance
By installing the audytx GitHub App or sending requests to audytx.com/mcp, you agree to these terms. If you do not agree, uninstall the App and discontinue use.
Permitted use
- audytx is provided for the purpose of analyzing AWS Terraform infrastructure-as-code for security and cost findings.
- You may use audytx on repositories you own or have explicit authorization to scan.
- Automated use via the MCP endpoint is permitted within the published rate limits.
Prohibited use
The following are strictly prohibited and will result in permanent account termination:
- Abuse of the scan infrastructure. Deliberate attempts to overload, circumvent rate limits, or degrade service for other users — including automated flood attacks, distributed request amplification, and rate-limit evasion.
- Unauthorized scanning. Using audytx to scan repositories, code, or infrastructure you do not own and have not been explicitly authorized to scan.
- Extraction or reverse-engineering. Attempting to extract, reconstruct, or reverse-engineer audytx's check logic, scoring weights, or engine internals through systematic probing or differential analysis.
- Resale or white-labeling. Reselling, sublicensing, or white-labeling audytx's output or API without written permission from Rexstart Labs Pvt Ltd.
- Malicious input. Sending crafted input designed to exploit vulnerabilities in the analysis engine, including prompt injection via Terraform file contents targeting the MCP surface.
Reporting violations
Report abuse, violations of these terms, or suspected misuse via the abuse-report form. Include as much detail as possible: timestamps, GitHub usernames or installation IDs, and a description of the observed behavior. All reports are investigated. Confirmed violators are permanently banned from the GitHub App and the MCP endpoint, with no reinstatement path.
Service availability
- audytx is provided free of charge on a best-effort basis. We make no uptime guarantees.
- We reserve the right to rate-limit, suspend, or terminate access to any installation or IP address that violates these terms or degrades service for others, without prior notice.
- We may modify, suspend, or discontinue the service at any time. We will provide reasonable advance notice via the GitHub App release notes where practical.
Intellectual property
audytx's check catalog, engine, and all associated software are the property of Rexstart Labs Pvt Ltd. Your Terraform source code remains entirely your property. We claim no rights over the code you submit for analysis.
Disclaimer and limitation of liability
audytx is a static analysis tool. Findings are provided for informational purposes only and do not constitute a security audit, legal advice, or a guarantee that your infrastructure is free of vulnerabilities. Use findings as one input in your security review process, not as a substitute for it. To the maximum extent permitted by applicable law, Rexstart Labs Pvt Ltd is not liable for any damages arising from your use of or reliance on audytx findings.
Governing law
These terms are governed by the laws of India. Disputes shall be subject to the exclusive jurisdiction of the courts of Bangalore, Karnataka, India.
Current limits
- AWS only. No Azure, no GCP. Drop us a line if that shapes whether you'd adopt; we're tracking demand.
- Terraform / HCL only. No CloudFormation, CDK, or Pulumi yet.
- Up to 400 Terraform files per scan. audytx reads your repository's Terraform tree for cross-resource context, changed files first. Repos past that ceiling are scanned diff-only, and the PR comment says so when it happens. If you regularly exceed it, ping us.
- Precision is the bar. Open a support ticket if a finding looks wrong; we tune the check catalog from real PR feedback.