Documentation
Pinata is an AI-powered security scanner for AI-generated code. 47 detection categories. Six layers of defense. One command.
Installation
Run Pinata instantly with npx — no global install required:
terminal
$ npx --yes pinata-security-cli@latest analyze .
Or install globally for repeated use:
$ npm install -g pinata-security-cli
Requirements
- Node.js 20+ (LTS recommended)
- Pinata API key — required for AI verification, test generation, and all paid features. Set up your key →
Quick Start
Scan any directory with AI verification enabled:
$ pinata analyze ./src --verify
Analyzing: /app/my-saas
Project: Web server (high confidence)
Files: 136 | Languages: TypeScript
Scanning... ████████████████████ 100%
AI Verification: 351 patterns → 3 confirmed vulnerabilities
┌─────────────────────────────────────────────────┐
│ CRITICAL SQL Injection api/users.ts:47 │
│ HIGH XSS views/comments.tsx:23 │
│ HIGH Command Inject utils/exec.ts:12 │
└─────────────────────────────────────────────────┘
Pinata Score: 72/100 (C)
Run 'pinata generate --gaps --write' to create security tests
The AI verification layer (--verify) confirmed only 3 of 351 pattern matches were actually exploitable. Without it, you'd be chasing 348 ghosts.
API Key Setup
All Pinata plans require an API key. After subscribing, configure it:
$ pinata config set api-key your-api-key-here
# Or use an environment variable
$ export PINATA_API_KEY=your-api-key-here
Your API key enables:
- AI semantic verification — eliminates false positives
- Security test generation — writes runnable tests for confirmed vulns
- AI-powered explanations — context-aware remediation advice
- Mutation testing — verifies your tests actually catch what they claim
Pick a plan at pinata.sh/pricing or email christian@pinata.sh to get started.
Configuration
Configure Pinata via CLI flags or a .pinatarc file in your project root:
.pinatarc
// .pinatarc (JSON)
{
"apiKey": "pk_...",
"excludeDirs": ["node_modules", "dist", "vendor"],
"minConfidence": "high",
"output": "terminal",
"verify": true,
"failOn": "critical"
}
CLI flags always override .pinatarc values.
pinata analyze
Scan a directory for security vulnerabilities:
$ pinata analyze [path] [options]
| Option | Description | Default |
|---|---|---|
--verify |
Enable AI semantic verification | false |
-c, --confidence |
Minimum confidence: high, medium, low | high |
-o, --output |
Output format: terminal, json, sarif, junit, markdown | terminal |
-d, --domain |
Filter by domain (security, data, auth, etc.) | all |
-v, --verbose |
Show detailed output with code context | false |
--exclude |
Directories to exclude (comma-separated) | node_modules,dist |
--fail-on |
Exit with code 1 if severity found: critical, high, medium | — |
pinata generate
Generate security tests for confirmed vulnerabilities:
$ pinata generate --gaps --write
Analyzing test coverage gaps...
Found 3 untested vulnerability paths
✓ Generated: tests/security/sql-injection.test.ts
✓ Generated: tests/security/xss-comments.test.ts
✓ Generated: tests/security/cmd-inject.test.ts
Running mutation verification...
✓ sql-injection.test.ts — kill rate 100%
✓ xss-comments.test.ts — kill rate 100%
✓ cmd-inject.test.ts — kill rate 100%
3 tests written. All mutations killed.
| Option | Description |
|---|---|
--gaps |
Generate tests only for uncovered vulnerability paths |
--write |
Write generated tests to disk (without this, tests are printed to stdout) |
--framework |
Test framework: vitest, jest, mocha, pytest |
--mutate |
Run mutation testing on generated tests to verify kill rate |
pinata explain
Get AI-powered explanations for specific findings with remediation advice:
$ pinata explain sql-injection src/db/queries.ts:45
SQL Injection at src/db/queries.ts:45
The query concatenates user input directly into SQL:
const query = `SELECT * FROM users WHERE id = ${userId}`;
Risk: Attackers can inject malicious SQL to read, modify,
or delete data. Could lead to full database compromise.
Fix: Use parameterized queries:
const query = 'SELECT * FROM users WHERE id = $1';
db.query(query, [userId]);
pinata dashboard
Launch the interactive TUI dashboard for a visual overview of findings:
$ pinata dashboard
Navigate with arrow keys, press Enter to drill into findings, q to quit.
pinata config
Manage persistent configuration:
$ pinata config set api-key pk_xxx
$ pinata config get api-key
$ pinata config list
$ pinata config unset api-key
AI Verification
Pattern-based scanners flag everything that looks dangerous. Pinata's AI verification layer reads the code in context and determines whether a flagged pattern is actually exploitable.
This is what separates Pinata from tools like Semgrep or CodeQL — it understands that a SQL query using an ORM's parameterized builder isn't an injection risk, even if the syntax pattern matches.
How it works
- Pattern scan flags potential issues (fast, local, zero API calls)
- AI verification reads surrounding code context and confirms exploitability
- Only confirmed findings appear in your results
Enable with the --verify flag or set "verify": true in your .pinatarc.
Test Generation
Pinata generates runnable security tests for every confirmed vulnerability. Tests target the specific code path that's exploitable — not generic boilerplate.
Run pinata generate --gaps --write to:
- Analyze which confirmed vulns have no existing test coverage
- Generate framework-specific tests (Vitest, Jest, Mocha, pytest)
- Write them directly to your test directory
- Optionally verify with mutation testing
Mutation Testing
Tests that always pass are worse than no tests — they give false confidence. Pinata's mutation testing mutates your security-critical code and verifies that your tests catch every mutation.
What it checks
- Kill rate — percentage of mutations your tests detect
- Survivor analysis — which mutations escaped and why
- Confidence score — how much you can trust your test suite
Target: ≥95% kill rate for security-critical paths.
Dynamic Execution
For high-severity findings, Pinata can spin up a Docker sandbox and attempt to exploit the vulnerability in a live runtime. This produces real proof-of-concept output — not just static inference.
Dynamic execution needs Docker installed and running. The sandbox is fully isolated — zero risk to your host environment.
CI/CD Integration
GitHub Actions
.github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
pinata:
runs-on: ubuntu-latest
env:
PINATA_API_KEY: ${{ secrets.PINATA_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Run Pinata Security Scan
run: |
npx --yes pinata-security-cli@latest \
analyze . \
--verify \
--format sarif \
--output results.sarif \
--fail-on critical
- uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: results.sarif
GitLab CI
.gitlab-ci.yml
security-scan:
image: node:20
variables:
PINATA_API_KEY: $PINATA_API_KEY
script:
- npx --yes pinata-security-cli@latest analyze . --verify --output json > pinata.json
artifacts:
reports:
sast: pinata.json
Use --fail-on critical to exit with code 1 if critical findings are detected. This fails the CI check and blocks the merge.
Output Formats
| Format | Flag | Use Case |
|---|---|---|
| terminal | --output terminal |
Human-readable output with colors (default) |
| json | --output json |
Machine-readable JSON for scripting and automation |
| sarif | --output sarif |
SARIF 2.1.0 — native GitHub Security tab integration |
| junit | --output junit |
JUnit XML for CI systems (Jenkins, CircleCI, etc.) |
| markdown | --output markdown |
Markdown report — paste into PR comments |
Ignore Files
Create a .pinataignore file to exclude paths from scanning:
.pinataignore
# Test files (scan production code only)
tests/
*.test.ts
*.spec.js
# Build output
dist/
build/
.next/
# Dependencies
node_modules/
vendor/
# Generated code
*.generated.ts
Patterns follow .gitignore syntax.
Detection Categories
Pinata scans across 47 vulnerability categories organized into security domains:
| Domain | Categories | Examples |
|---|---|---|
| Injection | 8 | SQL injection, XSS, command injection, LDAP injection, template injection |
| Authentication | 6 | Hardcoded credentials, weak hashing, missing rate limiting, session fixation |
| Data Exposure | 7 | Sensitive data in logs, missing encryption, PII leakage, debug endpoints |
| Access Control | 5 | Missing auth checks, IDOR, privilege escalation, CORS misconfiguration |
| Cryptography | 4 | Weak algorithms, predictable random, missing integrity checks |
| Infrastructure | 6 | SSRF, path traversal, open redirects, missing timeouts, DNS rebinding |
| AI-Specific | 5 | Prompt injection, unsafe eval, unvalidated AI output, training data leakage |
| Supply Chain | 6 | Dependency confusion, typosquatting, outdated packages, lockfile tampering |
See the full list with descriptions: Detection Categories →
Scoring
Pinata calculates a security score from 0–100 based on:
- Finding count — confirmed vulnerabilities weighted by severity
- Domain coverage — how many risk domains were scanned
- Confidence levels — higher-confidence findings weigh more
| Grade | Score | Meaning |
|---|---|---|
| A | 90–100 | Excellent — minimal or no findings |
| B | 80–89 | Good — some low-severity findings |
| C | 70–79 | Moderate — action recommended |
| D | 60–69 | Below average — significant gaps |
| F | 0–59 | Poor — immediate action needed |
Troubleshooting
API key not working
Verify your key is set correctly:
$ pinata config get api-key
# Should show pk_**** (masked)
# Or check environment
$ echo $PINATA_API_KEY
Scan takes too long
- Exclude large directories with
--excludeor.pinataignore - Use
--confidence highto skip low-confidence patterns - AI verification adds ~2-5s per finding — this is normal
False positives
If you're seeing false positives, make sure --verify is enabled. Pattern-only scanning (without AI verification) will always have more noise. You can also suppress specific findings with inline comments:
// pinata-ignore: sql-injection — using parameterized ORM builder
const users = await db.users.findMany({ where: { id: userId } });
Need help?
Email christian@pinata.sh — we respond within 24 hours.