Pipeline Overview
Every skill published to Tank passes through a 6-stage security pipeline. Stages run sequentially, with each stage building on the output of the previous one.Stage 0: Ingest & Quarantine
Purpose: Download tarball, safely extract to temp directory, validate archive structure Source:python-api/lib/scan/stage0_ingest.py (398 lines)
Checks Performed
URL Validation
URL Validation
Tarball Size Limit
Tarball Size Limit
Content-Length header before streaming downloadZip Bomb Detection
Zip Bomb Detection
Symlink & Hardlink Rejection
Symlink & Hardlink Rejection
TarInfo.issym() and TarInfo.islnk() for every memberPath Traversal Prevention
Path Traversal Prevention
.. in path or absolute pathsWhy: Prevents extraction to arbitrary filesystem locationsExamples:../../etc/passwd→ BLOCKED/tmp/malicious.sh→ BLOCKEDsrc/utils.py→ ALLOWED
Blocked File Types
Blocked File Types
- Executables:
.exe,.dll,.so,.dylib,.wasm - Compiled:
.class,.pyc,.pyo,.jar,.war - Binary:
.bin,.dat
File Hash Computation
File Hash Computation
{"src/main.py": "a3b2c1...", "README.md": "d4e5f6..."} included in scan responseOutput
- temp_dir: Path to extracted skill files (cleaned up after pipeline)
- file_hashes: Dict of filename → SHA-256 hash
- file_list: Array of extracted file paths
- total_size: Total bytes extracted
Stage 1: File & Structure Validation
Purpose: Validate skill structure, detect Unicode tricks, encoding issues Source:python-api/lib/scan/stage1_structure.py (323 lines)
Checks Performed
SKILL.md Presence
SKILL.md Presence
SKILL.md exists in skill rootWhy: SKILL.md is the manifest file required for all skillsSeverity: High if missingBidirectional Override Detection
Bidirectional Override Detection
U+202A- LEFT-TO-RIGHT EMBEDDINGU+202B- RIGHT-TO-LEFT EMBEDDINGU+202E- RIGHT-TO-LEFT OVERRIDE (most dangerous)U+2066throughU+2069- Isolates
Zero-Width Character Detection
Zero-Width Character Detection
U+200B- ZERO WIDTH SPACEU+200C- ZERO WIDTH NON-JOINERU+200D- ZERO WIDTH JOINERU+FEFF- ZERO WIDTH NO-BREAK SPACE (BOM)
Cyrillic Homoglyph Detection
Cyrillic Homoglyph Detection
import requеsts where ‘е’ is Cyrillic)Examples:- Cyrillic
а(U+0430) looks like Latina(U+0061) - Cyrillic
е(U+0435) looks like Latine(U+0065) - Cyrillic
о(U+043E) looks like Latino(U+006F)
NFKC Normalization Check
NFKC Normalization Check
fi (U+FB01 ligature) normalizes to fi (two separate chars)Severity: MediumNon-UTF-8 Encoding Detection
Non-UTF-8 Encoding Detection
charset-normalizer library for detectionSeverity: MediumHidden File Detection
Hidden File Detection
Stage 2: Static Code Analysis
Purpose: AST analysis and pattern matching to detect dangerous code Source:python-api/lib/scan/stage2_static.py (551 lines — largest stage)
Checks Performed
Python AST Analysis
Python AST Analysis
PythonASTAnalyzer + Bandit security linterDetected Patterns:Shell Injection (Critical):os.system()os.popen()subprocess.call(),.run(),.Popen()
eval()exec()compile()
pickle.loads(),pickle.load()marshal.loads()shelve.open()
requests.get(),.post(), etc.httpxcallsurllib.request.urlopen()socket.connect()
os.environos.getenv()
Bandit Integration
Bandit Integration
- B102 (exec usage)
- B307 (eval usage)
- Hardcoded passwords
- SQL injection
- Assert usage
- Bandit HIGH → critical (for exec/eval)
- Bandit MEDIUM → high
- Bandit LOW → medium
JavaScript/TypeScript Pattern Matching
JavaScript/TypeScript Pattern Matching
eval()usageFunction()constructornew Function()child_process.exec()spawn()withshell: true
fetch()callsXMLHttpRequestrequire('http')orrequire('https')- Sensitive file reads (
.ssh,.aws,.env,.config)
process.envaccessrequire('dotenv')
Shell Script Pattern Matching
Shell Script Pattern Matching
curl <url> | bashwget <url> | bashevalusage in shell
chmod 777
chmod +x(making files executable)exportwith variable substitution
Obfuscation Detection
Obfuscation Detection
base64.b64decode() + exec()(Critical)codecs.decode(..., 'rot13')(High)
Permission Cross-Check
Permission Cross-Check
- Stage 2 finds
requests.get("https://api.example.com") - Checks if
permissions.network.outboundincludes*.example.comor* - If NOT declared → High severity finding
- Subprocess usage vs
permissions.subprocess - Filesystem access vs
permissions.filesystem.read/write
Stage 3: Prompt Injection Detection
Purpose: Detect malicious instructions embedded in markdown files Source:python-api/lib/scan/stage3_injection.py (401 lines)
Checks Performed
Direct Override Patterns
Direct Override Patterns
- “ignore all previous instructions”
- “disregard above rules”
- “forget everything you were told”
- “your new role is…”
- “this is the real system prompt”
Role Hijacking Patterns
Role Hijacking Patterns
- “you are now a…”
- “act as if you are…”
- “pretend to be…”
- “switch to developer mode”
- “from now on, you are…”
Context Manipulation
Context Manipulation
- “the above was just a test”
- “never mind what I said”
- “new instructions:”
- “updated instructions:”
Exfiltration Directives
Exfiltration Directives
- “send to [URL]”
- “post this to…”
- “include in your response the contents of…”
- “output the system prompt”
- “reveal your instructions”
Privilege Escalation Keywords
Privilege Escalation Keywords
- “run as root”
- “sudo”
- “disable safety checks”
- “bypass security filters”
- “enable admin mode”
Claude-Specific Format Injection
Claude-Specific Format Injection
<tool_use><function_calls><system>,<human>,<assistant><invoke>[SYSTEM],[HUMAN],[ASSISTANT]
Hidden Content Detection
Hidden Content Detection
Suspicion Score Heuristic
Suspicion Score Heuristic
Cisco Skill-Scanner Integration
Cisco Skill-Scanner Integration
Stage 4: Secrets & Credential Scanning
Purpose: Detect hardcoded API keys, credentials, private keys Source:python-api/lib/scan/stage4_secrets.py (305 lines)
Checks Performed
detect-secrets Integration
detect-secrets Integration
detect-secrets library with 12 pluginsPlugins:- AWSKeyDetector
- AzureStorageKeyDetector
- BasicAuthDetector
- GitHubTokenDetector
- Base64HighEntropyString (limit 4.5)
- HexHighEntropyString (limit 3.0)
- PrivateKeyDetector
- SlackDetector
- StripeDetector
- TwilioKeyDetector
- KeywordDetector
- JwtTokenDetector
Custom Regex Patterns
Custom Regex Patterns
- Google Cloud API keys:
AIza[0-9A-Za-z_-]{35} - Generic API keys:
api_key = "[a-zA-Z0-9]{16,}" - Database URLs:
postgres://user:pass@host - SSH private keys:
-----BEGIN RSA PRIVATE KEY----- - JWT tokens:
eyJ... - Slack webhooks:
https://hooks.slack.com/services/... - Discord webhooks:
https://discord.com/api/webhooks/... - High-entropy strings (40+ chars)
.env File Detection
.env File Detection
.env files with actual values (not .env.example)Logic:- Allow
.env.example,.env.template, etc. - Flag
.env,.env.local,.env.productionif they containKEY=valuepairs
Stage 5: Supply Chain Audit
Purpose: Check dependencies for vulnerabilities and typosquatting Source:python-api/lib/scan/stage5_supply.py (545 lines)
Checks Performed
OSV Vulnerability Scanning
OSV Vulnerability Scanning
https://api.osv.dev/v1/querybatch (free, no auth, no rate limits)Ecosystems: PyPI (Python), npm (JavaScript)Batch Processing: Up to 100 packages per requestSeverity Mapping:- CVSS >= 9.0 → Critical
- CVSS >= 7.0 → High
- CVSS >= 4.0 → Medium
- CVSS < 4.0 → Low
Typosquatting Detection
Typosquatting Detection
- Compare against top 1000 popular PyPI/npm packages
- Flag if Levenshtein distance is 1-2
- Flag if single character differs at same position
reqeustsvsrequests(distance 1) → FLAGGEDnumppyvsnumpy(distance 1) → FLAGGED
Unpinned Dependency Detection
Unpinned Dependency Detection
requests(no version)requests>=2.0(range)requests==*(wildcard)
requests==2.28.0(exact pin)
Dynamic Installation Detection
Dynamic Installation Detection
pip install or npm install at runtimePatterns:subprocess.run(["pip", "install", ...])os.system("pip install ...")exec("npm install ...")
Dependency Manifest Parsing
Dependency Manifest Parsing
requirements.txt(Python)package.json(npm)pyproject.toml(Python Poetry/PDM)
Performance
| Stage | Typical Duration | Max Duration (timeout) |
|---|---|---|
| Stage 0 | 500ms - 2s | 30s (download timeout) |
| Stage 1 | 100ms - 500ms | N/A |
| Stage 2 | 1s - 3s | N/A |
| Stage 3 | 500ms - 2s | N/A |
| Stage 4 | 500ms - 1s | N/A |
| Stage 5 | 1s - 3s | 20s (OSV API timeout) |
| Total | 3s - 8s | ~50s |