PromptGraph implements defense-in-depth across multiple layers: input validation, path traversal protection, download limits, rate limiting, content scanning, and a trust system for the marketplace.
Every file path is resolved through path.resolve() before use. .. sequences are blocked at multiple points:
- config.js
sanitizePath()— throws if input path contains.. - api.js
index()— checkssourceDir.includes('..') - indexer.js
sanitizePath()— resolves viapath.resolve() - validator.js — checks if any path segment is exactly
.. - marketplace.js — validates destination starts with expected directory after
path.resolve()
All marketplace installations (installSkillFromUrl, installSkill, installBundle) verify path.resolve(dest).startsWith(path.resolve(SKILLS_DIR)).
| Limit | Value | Enforcement |
|---|---|---|
| Per-file download | 50 MB | streamDownload() checks Content-Length and streaming byte count |
| Per-file storage | 5 MB | indexFile() and indexAll() stat before reading |
| Files per repo | 100,000 | indexAll() counter + truncation |
| Total repo size | 500 MB | github-import.js accumulates byte count |
| Embedding calls | 10,000 per session | embedder.js throws when exceeded |
Two independent rate limiters (src/utils/rate-limiter.js) protect GitHub API calls:
| Limiter | Requests | Window | Used By |
|---|---|---|---|
| GitHub API | 30 | 60 seconds | httpsGet() for API calls |
| Download | 60 | 60 seconds | streamDownload() for raw file downloads |
The RateLimiter uses a sliding-window algorithm with automatic back-pressure: acquire() blocks until a slot is available.
Every skill file is validated:
- Required frontmatter:
name(lowercase, digits, hyphens, 2-64 chars),description(min 15 chars) - Content length: 200 – 5,242,880 chars
- Binary content detection: rejects files with null bytes
- Extension whitelist: only
.md,.json,.yaml,.yml,.txt,.js,.ts,.py,.rs,.toml - Path traversal check: rejects files with
..in path - Security scan: 17 dangerous patterns checked
curl | sh // pipes remote content to shell
wget | sh // pipes remote content to shell
rm -rf ~/ // destructive file removal
eval(atob(...)) // obfuscated code execution
AWS_SECRET_KEY= // hardcoded credentials
process.env.* + fetch/http // env var exfiltration
"ignore previous instructions" // prompt injection
"reveal your system prompt" // prompt extraction
.ssh/id_rsa access // sensitive file accessid: lowercase, digits, hyphens (2-64 chars)name: min 3 charsdescription: min 15 charsrepo_url: must be valid GitHub URL orowner/repoformatskillsarray: each entry validated as ID, max 20 skills per bundle (warning)
Only the following file extensions are accepted: .md, .json, .yaml, .yml, .txt, .js, .ts, .py, .rs, .toml.
Rejects by:
- Filename: README, LICENSE, CHANGELOG, CONTRIBUTING, SECURITY, AUTHORS, CREDITS, FAQ, INDEX, OVERVIEW, etc. (27 known names)
- Filename pattern: leading
_or., version-like (v1.0), date-like (2024-01) - Directory:
.github,docs/,tests/,assets/,node_modules/,references/, etc. (22 directory names) - Content: starts with
# Readme, contains badge images (shields.io, travis-ci)
14-dimension feature vector analysis:
- Content length, header count, instruction headers, imperative verbs
- Code blocks, numbered lists, bullet points
- Paragraph structure, vocabulary diversity
- Centroid similarity (cosine distance to good/bad training means)
- Rule A override: catches skill-like content misclassified as reject
Registry entries have a trust_level:
| Level | Description |
|---|---|
verified |
Manually audited, safe |
official |
Published by trusted source |
trusted |
Community but reputation-proven |
community |
Public contributions (default) |
unknown |
Not assessed |
Trust level is set server-side on the registry. Clients query but do not assign trust levels for community skills.
Marketplace installs write to a .tmp file first, validate, then rename() to the final path. This prevents partial/corrupt skill files.
When importing from GitHub:
- Repo existence is verified (HTTP HEAD)
- Skills directory auto-detected (API)
- Sparse checkout limits fetched content
- Every
.mdfile validated throughisSkillFile()andvalidateSkill() - Non-skill files (docs, tests, images) deleted after clone
- Empty directories cleaned up
For public issues (non-exploitable bugs, questions): open an issue on the GitHub repository.
For security vulnerabilities (CVE-class, actively exploitable): do NOT open a public issue. Send details to the maintainer:
- Email:
neipan@security.email(PGP key available on request) - GitHub Security Advisories: https://github.com/NeiP4n/promptgraph/security/advisories/new
We aim to acknowledge receipt within 24 hours and provide a fix timeline within 72 hours.
PromptGraph downloads and executes skill files from third-party sources. While validation and scanning are applied, no guarantee of safety is made for community-published skills. Review skill content before running untrusted commands.