| Version | Supported |
|---|---|
| 3.12.x | ✅ Active |
| 3.0.x–3.11.x | ❌ End of life; recognized Specky 3.x config migration remains supported |
| 2.x | ❌ End of life |
| 1.0.x | ❌ End of life |
If you discover a security vulnerability in Specky, please report it responsibly:
- Do NOT open a public issue.
- Email paulasilvatech@github.com with:
- Description of the vulnerability
- Steps to reproduce
- Impact assessment
- Suggested fix (if any)
- You will receive an acknowledgment within 48 hours.
- A fix will be released within 7 days for critical issues.
All 58 MCP tool inputs are validated using Zod schemas with .strict() mode. No unknown fields are accepted. This prevents injection of unexpected parameters through the MCP JSON-RPC interface.
AI Client → JSON-RPC → Zod .strict() validation → Service layer
FileManager (the sole file I/O service) sanitizes all paths before any filesystem operation:
- Resolves paths relative to the workspace root
- Rejects paths containing
..sequences - Blocks access outside the designated workspace directory
- All file operations are scoped to
SDD_WORKSPACEor the current working directory
Specky does not use eval(), Function(), vm.runInNewContext(), or any dynamic code execution. Template rendering uses string replacement only — no template engines that execute code.
The Specky MCP server does not execute arbitrary shell commands or persist GitHub credentials. Generated agents may declare narrowly scoped Git, test, or release-gate capabilities, but the selected host runtime executes and approves those native tools. The optional GitHub MCP integration is registered only with specky install --integration=github; GitHub authentication and repository authorization remain with the signed-in host identity. See Target Capabilities.
The MCP server makes zero outbound network calls. All data stays on the user's machine. It communicates only via stdio (JSON-RPC over stdin/stdout) or optional HTTP transport on localhost, and this holds unconditionally — specky serve never performs the update check described below.
The CLI performs exactly one optional outbound call: a once-daily GET https://registry.npmjs.org/specky-sdd/latest to detect new releases, run after install/doctor/status/upgrade/--version and shown as an Update available: vX → vY banner. This is not telemetry — nothing is sent beyond the HTTP request itself: no identifiers, no usage data, no payload. The check fails silently when offline and is disabled automatically in CI (CI=true).
Opt out with either:
SPECKY_NO_UPDATE_CHECK=1in the environment, orupdate_check: falsein.specky/config.yml
The local version-drift warning printed by specky doctor/specky status and at MCP server startup compares installed assets against the running version on disk — it involves no network at all.
Specky declares only 3 direct runtime dependencies. Their transitives are included in runtime audits and the published SBOM:
| Dependency | Purpose | Security Profile |
|---|---|---|
@modelcontextprotocol/sdk |
MCP protocol implementation | Official SDK from Anthropic |
zod |
Input schema validation | Zero dependencies, widely audited |
yaml |
Project configuration parsing | Focused parser used for .specky/config.yml |
Runtime dependencies are kept intentionally small and are audited in CI.
- All log output goes to stderr — stdout is reserved for JSON-RPC protocol messages
- No sensitive data (credentials, tokens, file contents) is included in log messages
- Audit-relevant tool invocations are recorded only in the local
.specs/directory
| OWASP Category | Mitigation |
|---|---|
| A01 Broken Access Control | Path sanitization in FileManager; workspace-scoped operations |
| A02 Cryptographic Failures | No secrets handling. HMAC-SHA256 and a SHA-256 hash chain are used only for local integrity checks (state signature, audit trail), not for confidentiality — see the audit-trail notes below for the trust model |
| A03 Injection | Zod .strict() validation on all inputs; no SQL/eval/shell execution |
| A04 Insecure Design | State machine enforces phase ordering; thin tools / fat services separation |
| A05 Security Misconfiguration | Minimal config surface; no default credentials; no admin endpoints |
| A06 Vulnerable Components | 3 direct runtime deps; transitives included in runtime audit and SBOM; Dependabot enabled |
| A07 Authentication Failures | stdio mode is process-isolated (no network). HTTP mode binds to 127.0.0.1 by default and supports optional bearer-token auth — a shared token (SDD_HTTP_TOKEN) or a named token table (SDD_HTTP_TOKENS_FILE, principal + RBAC role per token, sha256 storage supported) — all constant-time compared, plus DNS-rebinding protection |
| A08 Data Integrity Failures | Atomic file writes via FileManager; Zod schema enforcement |
| A09 Logging Failures | Structured stderr logging; no stdout pollution |
| A10 SSRF | Zero outbound requests from the MCP server; the CLI's once-daily update check fetches a fixed registry URL only — no user-controlled URLs |
# Block high/critical findings in the package shipped to users
npm run security:audit:runtime
# Report runtime and development findings; block critical findings
npm run security:audit:allCI and publishing block high/critical findings in production dependencies and critical findings anywhere in the lockfile. The full audit still prints high findings from development tooling so they remain visible while an upstream-compatible fix is unavailable.
As of 2026-08-08, the direct MCP SDK range starts at ^1.30.0 and the lockfile resolves 1.30.0, avoiding the SDK data-isolation and ReDoS advisories affecting older releases. Its Hono dependency resolves to 4.12.33; npm advisories require 4.12.34, which is not yet available in the configured registry, leaving three moderate runtime findings. The development-only PostCSS path resolves NanoID 3.3.16; the zero-size generator advisory requires 3.3.17, also not yet available in that line. Do not use npm audit fix --force to cross declared major/version boundaries. These temporary residuals must be removed when compatible upstream releases become available.
Updating the npm package and upgrading a workspace are separate steps:
# Global installation
npm install -g specky-sdd@latest
cd your-project
specky upgrade
specky doctor
# Project-local installation
npm install --save-dev specky-sdd@latest
npx specky upgrade
npx specky doctorThe npm command installs fixed Specky code and dependencies. specky upgrade does not modify npm packages; it migrates supported workspace config, refreshes generated assets, and re-pins MCP registration to the package version already installed. specky doctor then verifies installed asset hashes.
| Variable | Purpose | Default |
|---|---|---|
SDD_WORKSPACE |
Restricts file operations to this directory | Current working directory |
PORT |
HTTP transport port (when using --http mode) |
3200 |
SPECKY_PROFILE |
enterprise flips audit/RBAC/rate-limit defaults to ON (explicit config wins) |
standard |
SDD_HTTP_TOKEN |
Shared bearer token for --http (no identity) |
unset (auth off) |
SDD_HTTP_TOKENS_FILE |
Named token table → principal + RBAC role (identity-based auth) | unset |
SDD_AUDIT_HMAC_KEY / SDD_AUDIT_HMAC_KEY_FILE |
Sign audit entries (tamper evidence); keep the key outside the workspace | unset (chain only) |
SDD_ROLE |
Local RBAC role for stdio use — ignored on authenticated HTTP requests | rbac.default_role |
SPECKY_NO_UPDATE_CHECK |
Set to 1 to disable the CLI's once-daily update check — the package's only outbound call (see Network Calls) |
unset (check enabled) |
HTTP transport (--http) binds to 127.0.0.1 by default. Binding to a non-loopback address requires an explicit --host and prints a warning. Set SDD_HTTP_TOKEN (shared token) or SDD_HTTP_TOKENS_FILE (named tokens mapping to principal + role) to require an Authorization: Bearer <token> header on every /mcp request (the /health probe stays open). Even so, do not expose Specky to public networks without a TLS-terminating reverse proxy. Hosted, air-gapped, and container deployment patterns: docs/ENTERPRISE-DEPLOYMENT.md.
The audit trail's plain hash chain detects corruption but not deliberate rewriting by a workspace writer; configure an HMAC key (held outside the workspace) to make entries tamper-evident, and anchor the current_hash from sdd_verify_audit externally to detect tail truncation.
- TypeScript
strictmode enabled — no implicitany, no unchecked index access - Zero
anytypes in source code — enforced by CI - All schemas use
.strict()— rejects unknown fields FileManageris the sole I/O boundary — no directfscalls in tools or other services- The Specky MCP server does not execute shell commands; target runtimes may execute only the explicitly declared, host-approved agent capabilities
| Practice | Details |
|---|---|
| Use stdio mode by default | specky-sdd (global install) — no network exposure, process-level isolation |
| Never expose HTTP mode publicly without TLS | --http supports bearer-token auth (SDD_HTTP_TOKEN, or SDD_HTTP_TOKENS_FILE for per-user identity + role) and binds to 127.0.0.1 by default, but has no TLS. For remote access, set tokens AND place it behind a reverse proxy (nginx, Caddy, Traefik) terminating TLS |
Protect .specs/ directory |
Contains architecture details, API contracts, security models. Add to .gitignore for sensitive projects, or use a private repository |
Protect .checkpoints/ |
Contains full copies of all spec artifacts. Treat like source code |
| Keep security-scan hook active | .claude/hooks/security-scan.sh scans for hardcoded secrets and blocks commits (exit 2). Do not disable |
| Review auto-generated specs | sdd_turnkey_spec and sdd_auto_pipeline generate from natural language — review before committing to ensure no sensitive details leaked |
| Use environment variables | Never write actual secrets in spec artifacts. Reference them as $VAR_NAME |
Run npm audit regularly |
Catches dependency vulnerabilities in runtime and development dependencies |
If you must use HTTP mode (--http):
1. [ ] Bind to localhost only (default behavior)
2. [ ] Place behind reverse proxy with TLS (HTTPS)
3. [ ] Add authentication to the reverse proxy
4. [ ] Set firewall rules to restrict access
5. [ ] Use a unique PORT via environment variable
6. [ ] Monitor access logs on the reverse proxy
Example secure deployment with nginx:
server {
listen 443 ssl;
server_name specky.internal.company.com;
ssl_certificate /etc/ssl/certs/specky.crt;
ssl_certificate_key /etc/ssl/private/specky.key;
# Require authentication
auth_basic "Specky MCP Server";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:3200;
proxy_set_header Host $host;
}
}| Data | Classification | Storage | Protection |
|---|---|---|---|
| CONSTITUTION.md | Internal | .specs/ |
Filesystem permissions |
| SPECIFICATION.md | Business Confidential | .specs/ |
May contain business logic — review before sharing |
| DESIGN.md | Confidential | .specs/ |
Contains architecture, API contracts, security model |
| TASKS.md | Internal | .specs/ |
Implementation plan |
| .checkpoints/*.json | Confidential | .specs/.checkpoints/ |
Full artifact snapshots |
| .sdd-state.json | Internal | .specs/ |
Pipeline metadata only |
| docs/journey-*.md | Business Confidential | docs/ |
Complete audit trail |
| Routing payloads | Transient | Memory only | Never persisted by Specky |
These guarantees describe the intended product boundary. Known implementation gaps are tracked in docs/DETERMINISM.md and docs/ENTERPRISE-CONTROLS.md.
- No outbound network calls from core MCP tools — optional external MCP integrations are returned as routing payloads for the AI client to execute. The CLI's once-daily update check (see Network Calls) is the package's only outbound call; it is opt-out and never runs in
specky serve. - No dynamic code execution — no
eval(),Function(), orvm.runInNewContext()in the Specky core. - No credential storage by design — no API keys, tokens, or passwords are intentionally stored by Specky.
- Workspace-scoped I/O is the target boundary —
FileManager.sanitizePath()enforces this for core file operations; document import hardening is tracked as an enterprise remediation item. - No sensitive operational logging by design — logs go to stderr and should contain only operational messages.
- Explicit hook execution is administrative —
specky hooksintentionally runs installed hook scripts for diagnostics and compatibility testing.
See docs/SYSTEM-DESIGN.md for the complete security architecture with threat model.
Running npx -y specky-sdd@latest without a pinned version downloads the latest package from npm on every invocation. This creates a supply-chain exposure: a compromised npm registry entry or a typosquat could execute malicious code in your environment before Specky even starts.
Recommended mitigations (ordered by risk reduction):
| Approach | Risk reduction | Notes |
|---|---|---|
npm install -g specky-sdd@<pinned-version> + specky install |
High — fetched once, upgrades only when you explicitly run npm install -g again |
Recommended for individual developers |
npm install --save-dev specky-sdd@<pinned-version> + npx specky install |
Higher — version-pinned in package.json; package-lock.json pins transitive deps |
Best for teams (reproducible across clones) |
Offline bundle: npm pack specky-sdd@<version> + npm install ./specky-sdd-*.tgz |
Higher — no network access at install time after the initial download | Air-gapped environments |
specky doctor after install |
Defense-in-depth — verifies SHA256 of every installed file against install.lock |
Run after every install/upgrade |
Docker (ghcr.io/paulasilvatech/specky:<version>) |
Highest — immutable image by digest | Best for CI/CD and air-gapped |
Workspace isolation pattern (CI/CD):
# Install into a local vendor directory — no global write permissions needed
npm install specky-sdd@3.6.0 --prefix ./vendor --ignore-scripts
./vendor/node_modules/.bin/specky install
./vendor/node_modules/.bin/specky doctor # integrity checkCLI binary entry points (both ship in the same package):
specky— unified CLI (install,doctor,status,upgrade,hooks,serve)specky-sdd— legacy alias; with no subcommand it routes tospecky serve(MCP stdio server), preserving any existing.mcp.jsonconfigs that referencespecky-sdddirectly
Install integrity check:
Every specky install produces .specky/install.lock with SHA256 of every deployed file. specky doctor validates these hashes — a tampered hook script or agent file is detected before any agent runs.
The --ignore-scripts flag prevents npm lifecycle scripts from running during install, which is a common supply-chain attack vector.
Specky addresses the 12 threat categories from the CoSAI MCP Security White Paper:
| ID | Threat Category | Specky Mitigation |
|---|---|---|
| T-01 | Tool Poisoning | Zod .strict() on all 58 tool inputs — no unknown fields accepted |
| T-02 | Prompt Injection via Tool Results | No user-controlled data interpolated into tool responses |
| T-03 | Excessive Tool Permissions | Thin Tools pattern — each tool does exactly one operation |
| T-04 | Insecure Data Storage | FileManager enforces workspace boundary; no secrets in files |
| T-05 | Insufficient Input Validation | All inputs validated with Zod schemas before reaching service layer |
| T-06 | Uncontrolled Resource Consumption | Rate limiter (opt-in) for HTTP mode; stdio is single-session |
| T-07 | Broken Access Control | RBAC engine (opt-in) — viewer/contributor/admin roles; path sanitization |
| T-08 | Supply Chain Compromise | 3 direct runtime deps; runtime audit + SBOM; Dependabot enabled; pinned install recommended |
| T-09 | Credential Leakage | No secrets in logs (stderr only); no credentials in spec artifacts |
| T-10 | Insecure Communication | stdio mode has zero network exposure; HTTP mode binds to localhost |
| T-11 | State Manipulation | HMAC-SHA256 signature on .sdd-state.json; tamper detection on load |
| T-12 | Audit Trail Integrity | Hash-chained JSONL audit log; rotation; syslog/OTLP export (opt-in) |
| # | OWASP MCP Risk | Specky Mitigation |
|---|---|---|
| M1 | Prompt Injection | No dynamic content in tool descriptions; outputs are structured JSON |
| M2 | Insecure Tool Design | Thin Tools / Fat Services — tools are pure input/output wrappers |
| M3 | Excessive Agency | No shell execution, no outbound network from the MCP server, no code eval |
| M4 | Insufficient Authentication | HTTP mode delegates to reverse proxy; stdio is process-isolated |
| M5 | Broken Object-Level Authorization | RBAC engine enforces per-tool access by role (opt-in) |
| M6 | Sensitive Data Exposure | FileManager path boundary; no credential logging; workspace-scoped I/O |
| M7 | Insecure Plugin Composition | Fixed tool set at startup — no dynamic loading |
| M8 | Improper Error Handling | All service errors caught; tools return structured error responses |
| M9 | Insufficient Logging | Hash-chained audit trail; syslog export available |
| M10 | Vulnerable Dependencies | 3 direct runtime deps; runtime and full-graph audit gates in CI; Dependabot on GitHub |