Deterministic pre-deploy security scanner for AI-generated web apps.
TrustBoundary scans committed repository evidence for a small set of high-confidence security mistakes common in fast AI-built apps. It is designed to run before deployment, especially in GitHub Actions.
It does not execute scanned project code. It does not import scanned project files. It does not use LLM judgment for findings. It does not claim your app is secure.
Clean result wording is:
No Confirmed Critical issues found.
Latest V1 release:
v1.1.0
Public GitHub Action usage:
uses: Arjunisking/trustboundary@v1For immutable pinning:
uses: Arjunisking/trustboundary@v1.1.0v1 is the floating major tag. v1.1.0 is the immutable release tag.
TrustBoundary V1 currently enforces exactly three blocking rules:
| Rule ID | Rule | What it detects | Severity | Confidence |
|---|---|---|---|---|
TB001 |
Client-Side Secret Exposure | Hardcoded or publicly exposed secrets in browser-delivered code | critical |
confirmed |
TB002 |
Destructive Public RLS / DB Rules | Supabase/Postgres/Firebase policy text that allows destructive public writes or deletes | critical |
confirmed |
TB003 |
Unsigned Known Provider Webhook | Known provider webhook routes that read payloads and reach dangerous sinks without deterministic signature verification evidence | critical |
confirmed |
V1 active automated enforcement is intentionally narrow.
TrustBoundary does not currently enforce:
TB004- advisory rules
- broad authentication scanning
- unsafe mutation scanning
- broken authorization scanning
- broad webhook or AI-agent abuse scanning
Those categories may exist in older planning docs, manual-review docs, or historical prototypes, but they are not active V1 automated blockers.
TrustBoundary now includes defensive education docs that explain common ways AI-generated apps become vulnerable.
These docs are for prevention, review, and product education. They do not expand automated scanner enforcement.
| Doc | Purpose |
|---|---|
docs/security-learning-model.md |
Defines how TrustBoundary explains security risks safely without turning education into enforcement claims. |
docs/attack-patterns.md |
Catalogs common defensive attack patterns such as URL/object ID tampering, unsafe mutation, prompt injection, unsigned webhooks, XSS, SSRF, CORS issues, and insecure uploads. |
docs/rule-to-attack-map.md |
Maps current automated blockers, future advisory candidates, docs-only topics, fixture examples, and intentional non-coverage. |
Important boundary:
If TrustBoundary explains a pattern, that does not mean TrustBoundary detects or blocks it.
Current enforcement still means only:
TB001, TB002, TB003
The learning docs intentionally separate patterns into:
| Status | Meaning |
|---|---|
Blocker |
Active automated detection exists today and can block when Confirmed Critical evidence is proven. |
Advisory |
Important risk that may support future non-blocking educational surfacing, but is not active enforcement today. |
Docs-only |
Defensive education only because deterministic repo evidence would be too broad, runtime-dependent, or noisy. |
This keeps TrustBoundary useful without becoming a fake “complete security scanner,” which would be both wrong and deeply on-brand for the modern software industry.
TB001 blocks when committed evidence shows secrets exposed to client-side code.
Examples of risky evidence:
- hardcoded Stripe live secret keys in browser-exposed files
- public env exposure such as
NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY - hardcoded Supabase service role JWT-like values with Supabase/service role proof
- hardcoded GitHub, Shopify, or Clerk secret-shaped values in browser-exposed code when safely detectable
TB001 does not block when client exposure cannot be proven.
TB002 blocks narrow, deterministic database policy failures.
Examples of risky evidence:
FOR UPDATE TO public USING (true)FOR DELETE TO anon USING trueFOR ALL TO publicwith missing guard clauses- Firebase rules like
allow write: if true - Firebase rules like
allow update, delete: if true
TB002 does not block complex policy logic, ownership checks, custom functions, auth.uid(), request.auth, JWT claims, or public insert-only rules.
TB003 blocks only supported known provider webhook routes when all required evidence is present.
Supported V1 providers:
- Stripe
- Clerk
- Shopify
- GitHub
To block, TB003 must prove all of these in the same committed route evidence:
- known provider webhook route
- payload read
- dangerous sink
- no same-file provider verification marker
- no clearly named local relative verification helper import
Examples of verification evidence that can suppress TB003:
stripe.webhooks.constructEvent(...)stripe-signaturewith crypto verification- Clerk/Svix
Webhook(...).verify(...) - Shopify
x-shopify-hmac-sha256with HMAC verification - GitHub
x-hub-signature-256with HMAC and timing-safe comparison - local helper imports such as
verifyWebhook,verifySignature,validateHmac, orconstructEvent
Unsupported providers and ambiguous webhook routes pass by design.
The defensive learning docs explain broader risks so builders understand what to review manually.
These include:
- URL/object ID tampering
- broken authorization
- admin route abuse
- unsafe mutation
- mass assignment
- public storage bucket risk
- webhook replay risk
- prompt injection
- excessive AI-agent permissions
- sensitive data in AI context
- SQL/raw query injection risk
- XSS
- CSRF
- open redirect
- path traversal
- SSRF
- CORS misconfiguration
- missing rate limits
- insecure file upload
- secrets in logs/errors
These are not active V1 automated blockers unless they directly map to TB001, TB002, or TB003.
Create .github/workflows/trustboundary.yml:
name: TrustBoundary
on:
pull_request:
push:
branches: [main]
workflow_dispatch:
jobs:
trustboundary:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run TrustBoundary
id: trustboundary
uses: Arjunisking/trustboundary@v1
with:
target_path: .
enforce: "true"
report_path: trustboundary-report.html
- name: Print outputs
run: |
echo "total_findings=${{ steps.trustboundary.outputs.total_findings }}"
echo "confirmed_critical_count=${{ steps.trustboundary.outputs.confirmed_critical_count }}"
echo "blocked=${{ steps.trustboundary.outputs.blocked }}"
echo "report_path=${{ steps.trustboundary.outputs.report_path }}"Action behavior:
- scans the checked-out repository text
- treats scanned files as untrusted input
- does not run target repository install/build scripts
- passes when no Confirmed Critical findings exist
- fails only when Confirmed Critical findings exist and
enforceis enabled - emits declared outputs for CI usage
- can write a static HTML report
Action inputs:
| Input | Default | Description |
|---|---|---|
target_path |
. |
Repository path to scan |
enforce |
"true" |
Whether Confirmed Critical findings fail the action |
report_path |
empty | Optional HTML report output path |
Action outputs:
| Output | Description |
|---|---|
total_findings |
Total findings returned by active rules |
confirmed_critical_count |
Number of blocking Confirmed Critical findings |
blocked |
true when enforcement should fail |
report_path |
HTML report path when configured |
TrustBoundary is a TypeScript monorepo.
Package layout:
packages/core scanner orchestration and file walking
packages/rules deterministic embedded rules
packages/cli local CLI wrapper
packages/action GitHub Action wrapper and bundled runtime
packages/report static HTML report generation
Install dependencies:
pnpm installBuild packages:
pnpm buildRun tests:
pnpm testRun typecheck:
pnpm typecheckThe local CLI is intended for repository development and manual scans.
Human-readable scan:
pnpm trustboundary scan examples/insecure-next-supabaseJSON output:
pnpm trustboundary scan examples/insecure-next-supabase --jsonHTML report:
pnpm trustboundary scan examples/insecure-next-supabase --report trustboundary-report.htmlEnforced exit code:
pnpm trustboundary scan examples/insecure-next-supabase --enforceCLI shape:
trustboundary scan <target-directory> [--json] [--report <file>] [--enforce]
Top-level JSON fields:
targetPathsummary.totalFindingssummary.confirmedCriticalCountsummary.blockingsummary.statusMessagehasBlockingFindingsenforcementEnabledexitCodefindings
Finding fields include:
ruleIdseverityconfidencefilelinemessageexploitPathpatch
Example shape:
{
"targetPath": "/repo",
"summary": {
"totalFindings": 1,
"confirmedCriticalCount": 1,
"blocking": true,
"statusMessage": "Confirmed Critical findings: 1"
},
"hasBlockingFindings": true,
"enforcementEnabled": true,
"exitCode": 1,
"findings": [
{
"ruleId": "TB003",
"severity": "critical",
"confidence": "confirmed",
"file": "app/api/webhooks/stripe/route.ts",
"line": 2,
"message": "Stripe webhook route reads payload and reaches a dangerous sink without deterministic signature verification evidence."
}
]
}This separation avoids confusion when blocking findings exist but enforcement is off.
TrustBoundary can generate a static HTML report.
Report behavior:
- generates one self-contained HTML file
- escapes untrusted file paths, messages, exploit paths, patches, and target paths
- reports deterministic repository evidence only
- does not claim the repository is secure
- uses safe clean-scan wording
Clean report status:
No Confirmed Critical issues found.
Future report education may add defensive learning context under findings, but that must not change blocker logic, rule IDs, CLI behavior, JSON output, GitHub Action behavior, or clean-scan wording.
examples/insecure-next-supabase is intentionally unsafe.
It exists to exercise TrustBoundary's active V1 blockers:
- TB001 client-side secret exposure
- TB002 destructive public RLS / DB rules
- TB003 unsigned known provider webhook
It also contains contrast examples that help explain manual-review and future advisory topics in the defensive learning docs.
Do not copy fixture code into production apps.
The v1.1.0 release was verified with:
- local rule/core/CLI/action tests
- full workspace tests
- typecheck
- build
- external red test using unsigned Stripe webhook
- external green test using signed Stripe webhook
- immutable
v1.1.0GitHub Action smoke test - floating
v1GitHub Action smoke test
For future releases:
- Run local verification.
pnpm --filter @trustboundary/rules test
pnpm --filter @trustboundary/core test
pnpm --filter @trustboundary/cli test
pnpm --filter @trustboundary/action test
pnpm test
pnpm typecheck
pnpm build- Create immutable version tag.
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z- Test from an external repository using the immutable tag.
uses: Arjunisking/trustboundary@vX.Y.Z- Move floating major tag only after the immutable tag passes external smoke tests.
git tag -fa v1 -m "Move v1 to vX.Y.Z"
git push origin refs/tags/v1 --force- Test again from an external repository using:
uses: Arjunisking/trustboundary@v1- Deterministic evidence over assumptions
- Confirmed Critical only for blocking
- False negatives over false positives
- No scanned-code execution
- No imported scanned files
- No LLM judgment for findings
- No full-security claims
- Clear exploit path and patch guidance
- Defensive education without offensive instructions
- Small V1 scope that developers can trust
TrustBoundary V1 is intentionally narrow.
It may miss:
- unsupported webhook providers
- custom verification patterns
- unusual route layouts
- cross-file dataflow
- runtime-only configuration issues
- broader authorization flaws
- unsafe mutation issues
- mass assignment issues
- prompt injection issues
- AI-agent permission issues
- general input validation bugs
- SQL/raw query injection risks
- XSS risks
- CSRF risks
- SSRF risks
- CORS misconfiguration
- rate-limit and abuse-control gaps
- insecure upload flows
- non-committed secrets or deployment misconfiguration
A clean TrustBoundary scan means only:
No Confirmed Critical issues found.
It does not mean the application is secure.
MIT
0 comments
log in to comment.