Skip to content

Security: paulasilvatech/specky

SECURITY.md

Security Policy

Supported Versions

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

Reporting a Vulnerability

If you discover a security vulnerability in Specky, please report it responsibly:

  1. Do NOT open a public issue.
  2. Email paulasilvatech@github.com with:
    • Description of the vulnerability
    • Steps to reproduce
    • Impact assessment
    • Suggested fix (if any)
  3. You will receive an acknowledgment within 48 hours.
  4. A fix will be released within 7 days for critical issues.

Security Architecture

Input Validation

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

Path Traversal Prevention

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_WORKSPACE or the current working directory

No Dynamic Code Execution

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.

Agent Capability Boundaries

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.

Network Calls

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=1 in the environment, or
  • update_check: false in .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.

Dependency Minimalism

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.

Logging

  • 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 Top 10 Coverage

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

Dependency Auditing

# 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:all

CI 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.

Applying Security Updates

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 doctor

The 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.

Security-Related Configuration

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.

Secure Development Practices

  • TypeScript strict mode enabled — no implicit any, no unchecked index access
  • Zero any types in source code — enforced by CI
  • All schemas use .strict() — rejects unknown fields
  • FileManager is the sole I/O boundary — no direct fs calls 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

Security Best Practices for Users

Always Use These Practices

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

HTTP Deployment Checklist

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 Sensitivity Classification

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

Architectural Security Guarantees

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(), or vm.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 boundaryFileManager.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 administrativespecky hooks intentionally runs installed hook scripts for diagnostics and compatibility testing.

See docs/SYSTEM-DESIGN.md for the complete security architecture with threat model.

NPX Supply Chain Risk

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 check

CLI 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 to specky serve (MCP stdio server), preserving any existing .mcp.json configs that reference specky-sdd directly

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.

MCP Security Framework Compliance

CoSAI MCP Security White Paper — Threat Category Coverage

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 Top 10 Coverage

# 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

References

There aren't any published security advisories