Skip to content

Security

swsd-mcp’s security model is built around four principles:

  1. Zero credentials at rest — the server never persists or logs API tokens
  2. Defense in depth — multiple independent controls so any single bypass isn’t compromise
  3. Verifiable supply chain — published artifacts tied to specific commits via SLSA provenance attestations
  4. Transparent governance — open source, explicit disclosure process, code review, change-management tooling

For deep compliance-grade details (every claim mapped to source-code verification), see SECURITY-POSTURE.md. This page is the user-facing summary.

Open a private security advisory

Do not open a public issue for vulnerabilities. The advisory channel is private and only visible to the maintainer.

Acknowledgment SLA72 hours
Initial assessment SLA7 days
Patch SLA (high-severity)30 days
Disclosure modelCoordinated (fix-then-publicize)
Reporter creditYes, in release notes (unless anonymity requested)

Full process in SECURITY.md.

The server has no concept of “its own” SWSD identity. Every API call uses the calling user’s token, which arrives via:

  • SWSD_TOKEN env var (stdio transport, local agent use)
  • Authorization: Bearer <token> header (Streamable HTTP)
  • X-SWSD-Token: <token> header (alternate, for Copilot Studio)

Tokens exist only in process memory for the lifetime of a single request. They are never written to disk, never logged, and never sent anywhere except the configured SWSD API host (validated against *.samanage.com).

SWSD_BASE_URL is validated at startup against the samanage.com domain. Other URLs are rejected. This prevents an attacker who manages to influence the env var (e.g., via a misconfigured deployment) from redirecting forwarded tokens to a server they control.

/mcp requests are rate-limited per sha256(token + IP) using express-rate-limit. Conservative defaults shipped; tunable per deployment via SWSD_RATE_LIMIT_MAX and SWSD_RATE_LIMIT_WINDOW_MS. Standards-compliant RateLimit-Policy and RateLimit headers in responses (draft-7 spec) so well-behaved clients can self-regulate.

The token is hashed (never stored as a key) for memory safety. /healthz is deliberately exempted — health probes from orchestrators hit it constantly.

The /mcp endpoint validates the Origin header against an allowlist (SWSD_ALLOWED_ORIGINS). This is the documented mitigation for DNS rebinding attacks where a malicious website tricks a browser into sending requests to a localhost MCP server.

Empty allowlist = no Origin restriction (acceptable behind a trusted reverse proxy that filters).

All outbound SWSD calls have a configurable timeout (default 30 seconds) via AbortSignal.timeout. Prevents hung connections from exhausting worker resources.

  • HTTP request bodies are never logged (they may contain tokens)
  • Error responses include the SWSD response body for actionable debugging, but errors are mapped through a sanitization layer in src/swsd/errors.ts
  • No telemetry, no analytics, no phone-home calls
  • Server startup log line is the only stdout output by default

/healthz returns {"ok":true} only — deliberately omits version information to avoid leaking stack details to anonymous callers. Server metadata (name, version, profile, enabled tools) is available via the swsd_get_server_info MCP tool, which is behind the authenticated MCP transport.

Every published artifact is cryptographically tied to a specific source commit and CI workflow.

LayerMitigation
npm packageOIDC trusted publishing (no long-lived NPM_TOKEN exists); SLSA provenance attestations on every release; verifiable with npm audit signatures
Direct dependenciesAll pinned to exact versions in package-lock.json; Dependabot weekly updates; npm audit in CI; small surface (4 direct production deps)
GitHub ActionsAll pinned to commit SHAs (not version tags); Dependabot auto-PRs new SHAs
Docker base imagenode:24-alpine pinned by SHA256 digest in Dockerfile; Dependabot tracks new digests

Verify provenance for any installed version:

Terminal window
npm view swsd-mcp --json | jq .dist.attestations
npm audit signatures

Every push and PR runs three security workflows in parallel:

ToolWhat it scans
gitleaksSecret patterns across the diff (PR) or full history (push)
CodeQLJavaScript/TypeScript static analysis with the security-extended query pack
OSV-ScannerDependency vulnerabilities against Google’s OSV database

A weekly scheduled run catches newly disclosed CVEs against unchanged dependencies — the killer feature, since your code didn’t change but the upstream world did.

Security

Reference frameworks we’ve drawn from. We don’t claim formal certification against any of these — they’re the languages we use to describe what we do, not audits we’ve passed.

FrameworkWhere we align
SLSA Build Level 3npm publish via OIDC + signed provenance attestations + reproducible builds
NIST SSDFPW.4 (review code), PW.7 (review and analyze software design), PW.8 (reuse vetted software), PS.2 (provenance), RV.1 (vulnerability disclosure)
OWASP ASVSV13 (API and Web Service): authentication, rate limiting, input validation, output encoding
npm package signingAll releases signed; users verify with npm audit signatures
ThreatSeverityMitigation
Token logged or persisted by the serverHigh → MitigatedNo filesystem writes for tokens; HTTP body never logged
Token forwarded to attacker-controlled URL (SSRF)High → MitigatedSWSD_BASE_URL validated against *.samanage.com allowlist at startup
DoS via request floodingMedium → MitigatedPer-token+IP rate limiting on /mcp; standards-compliant headers
DoS via hung outbound callsMedium → Mitigated30-second per-request timeout via AbortSignal.timeout
DNS rebindingMedium → MitigatedOrigin header validation hook on /mcp
Compromised maintainer account → malicious releaseHigh → Mitigatednpm OIDC; SLSA provenance
Compromised dependency (transitive)Medium → MitigatedPinned package-lock.json; Dependabot; npm audit in CI; small surface
Compromised GitHub ActionMedium → MitigatedAll actions pinned to commit SHAs; Dependabot auto-PRs
Compromised Docker base imageMedium → MitigatedPinned by SHA256 digest; Dependabot tracks
Tenant data leakage in error responsesMedium → MitigatedError mapper sanitizes upstream bodies
ConcernWhy out of scope
SolarWinds Service Desk API vulnerabilitiesReport directly to SolarWinds, not this project
User’s deployment environment hardeningEach operator hosts their own instance
User’s MCP host security (Claude, Copilot Studio, etc.)Upstream implementations out of our control
User’s token rotation disciplinePer-user responsibility; we never persist tokens
Corporate TLS-intercepting proxiesIT decision; we use TLS 1.2+ which is standard

Every claim above includes a path to verification. The general approach:

  1. Source code claims — clone the repo, navigate to the cited file, read the implementation. The codebase is small (~3000 lines not counting tests).
  2. Dependency claimscat package.json package-lock.json shows exact pinned versions; cat .github/workflows/*.yml shows pinned action SHAs.
  3. Provenance claimsnpm view swsd-mcp --json | jq .dist.attestations shows SLSA attestations; npm audit signatures verifies.
  4. CI claims — workflow runs are public at Actions tab; logs are inspectable.

The full posture doc (longer, more thorough, with code-line citations for every control) lives at docs/SECURITY-POSTURE.md.