Security Scanning
Every skill package is automatically scanned through a 6-stage security pipeline before being published to the Tank registry. This pipeline detects malicious code, credential leaks, prompt injection, and supply chain risks.Overview
Tank’s security scanner is implemented in Python (python-api/) and runs automatically during tank publish. It produces a verdict and audit score based on findings from all stages.
The scanner is inspired by the ClawHavoc incident — 341 malicious skills (12% of a major marketplace) that exfiltrated credentials and executed arbitrary code. Tank prevents this through mandatory scanning and permission enforcement.
6-Stage Pipeline
Stage 0: Ingest
File:python-api/lib/scan/stage0_ingest.py
Purpose: Download, extract, and validate tarball structure
Checks:
- Download from authorized domains only (Supabase storage)
- Validate tarball size (max 50MB compressed, 50MB extracted)
- Detect zip bombs (compression ratio >100x)
- Reject symlinks, hardlinks, path traversal
- Reject binary executables (
.exe,.so,.dll,.pyc,.class) - Compute SHA-256 hash for each file
download_failed— Cannot retrieve tarballzip_bomb— Compression ratio exceeds 100xpath_traversal— Archive contains dangerous paths like../../../etc/passwdblocked_file_type— Binary or executable file detectedsize_exceeded— Extracted size exceeds 50MB
Stage 1: Structure
File:python-api/lib/scan/stage1_structure.py
Purpose: Validate file structure and detect anomalies
Checks:
- Required files present (
skills.json,SKILL.md) - File count within limits (max 1000 files)
- No hidden files in unexpected locations
- Valid file extensions (whitelist)
- Manifest schema validation
missing_manifest— Noskills.jsonfoundmissing_skill_md— NoSKILL.mdfoundfile_count_exceeded— More than 1000 files
suspicious_hidden_file— Hidden file outside expected locations (e.g.,.git/)unexpected_extension— File extension not in allowlist
Stage 2: Static Analysis
File:python-api/lib/scan/stage2_static.py (~550 lines, largest stage)
Purpose: Static code analysis via AST and pattern matching
Checks:
- Python AST analysis (detects
eval,exec,compile,__import__) - JavaScript pattern matching (obfuscated code, dynamic requires)
- Dangerous function calls (shell execution, file operations)
- Network operations (HTTP requests, socket connections)
- Subprocess spawning
- Dynamic code generation
- Obfuscation detection (base64, hex encoding)
- Suspicious imports (e.g.,
ctypes,subprocess,socket)
arbitrary_code_exec— Use ofeval(),exec(),compile()shell_injection_risk— Unsafe shell command constructionunsafe_deserialization— Use ofpickle.loads()or similar
obfuscated_code— Base64-encoded payloads or hex stringsdynamic_import— Use of__import__()orimportlibsuspicious_network— Network calls to non-declared domains
subprocess_spawn— Subprocess usage without declarationfilesystem_write— File writes to sensitive paths
Stage 3: Injection Detection
File:python-api/lib/scan/stage3_injection.py
Purpose: Detect prompt injection and manipulation attempts
Checks:
- Prompt injection patterns (“Ignore previous instructions”)
- System prompt extraction attempts
- Role confusion attacks (impersonating user/assistant)
- Instruction override patterns
- Multi-turn attack sequences
- Unicode homoglyphs (lookalike characters)
- Hidden instructions (whitespace tricks, zero-width chars)
prompt_injection— Direct injection pattern detectedsystem_prompt_extraction— Attempts to extract system prompts
role_confusion— Instructions to change agent roleinstruction_override— Attempts to override core instructions
unicode_homoglyph— Suspicious lookalike charactershidden_instruction— Zero-width or invisible characters
Stage 4: Secrets Scanning
File:python-api/lib/scan/stage4_secrets.py
Purpose: Detect hardcoded secrets and credentials
Checks:
- API keys (OpenAI, Anthropic, AWS, GitHub, Stripe, etc.)
- AWS credentials (access keys, secret keys)
- Private keys (RSA, SSH, PGP)
- Database connection strings
- JWT tokens
- Generic high-entropy strings (potential secrets)
hardcoded_api_key— API key found in codehardcoded_aws_credentials— AWS credentials embeddedprivate_key_embedded— RSA/SSH private key in code
database_credentials— DB connection string with passwordjwt_token— Hardcoded JWT
high_entropy_string— Potential secret (base64, hex)
Stage 5: Supply Chain
File:python-api/lib/scan/stage5_supply.py
Purpose: Analyze supply chain risks
Checks:
- Dependency analysis (NPM, PyPI, etc.)
- Known vulnerable packages (CVE database)
- Typosquatting detection (e.g.,
reqestsvsrequests) - Dependency confusion attacks
- Unpinned dependencies
- Deprecated packages
known_vulnerability— Dependency has published CVEtyposquatting— Dependency name closely matches popular package
unpinned_dependency— Version range too broad (e.g.,*)deprecated_package— Dependency is marked deprecated
outdated_dependency— Newer version available
Verdict Rules
After all stages complete, findings are aggregated and a verdict is computed: Frompython-api/lib/scan/verdict.py:
Verdict Meanings
| Verdict | Severity | Can Publish? | Description |
|---|---|---|---|
PASS | Clean | Yes | No findings — perfect security score |
PASS_WITH_NOTES | Minor | Yes | Only medium/low findings — publishes with warnings |
FLAGGED | Moderate | Requires review | 1-3 high findings — manual review required |
FAIL | Severe | No | 1+ critical OR 4+ high findings — cannot publish |
Finding Structure
Each finding follows this schema (frompython-api/lib/scan/models.py):
Example Finding
Audit Score
The audit score (0-10) is computed from findings and stored inskills.lock:
Score Calculation:
- 10: Perfect — no findings
- 9-10: Excellent — minor notes only
- 7-8: Good — some medium findings
- 5-6: Fair — multiple medium or 1-2 high
- <5: Poor — critical or many high findings
You can require a minimum audit score for dependencies in your Dependencies with scores below 8.0 will be rejected during
skills.json:tank install.Scan Response
The full scan response includes all findings, stage results, and metadata:Viewing Scan Results
You can view scan results for any published skill:CLI
Web UI
Visit the skill page on the registry:- Verdict badge
- Audit score
- All findings with severity, location, and evidence
- Stage-by-stage breakdown
Best Practices
Before Publishing
-
Run local scan (if available):
- Review findings before submitting
- Fix critical and high findings — These will block publishing
- Document medium/low findings — Explain why they’re safe in your README or SKILL.md
Handling Flagged Verdicts
If your skill getsFLAGGED, you’ll need manual review:
-
Submit for review:
- Provide context — Explain why high findings are false positives
- Wait for approval — Registry admins will review and approve/reject
False Positives
Some legitimate code may trigger findings: Example: Dynamic imports for optional dependenciesNext Steps
- Permissions — Declare permissions correctly to avoid findings
- Manifest — Configure
audit.min_scorefor dependencies - Lockfile — Audit scores are stored in skills.lock