-
Notifications
You must be signed in to change notification settings - Fork 0
Security and Hardening
A security tool you cannot trust is worse than none, because it tells you you are safe when you are not. security-mcp is built so its own verdicts are hard to forge, hard to weaken silently, and hard to subvert through the very repositories it inspects. This page is the enterprise trust story.
The three files that shape a verdict (policy, exceptions, and baseline) can all be HMAC-signed. With a key of at least 32 bytes in SECURITY_POLICY_HMAC_KEY, security-mcp sign-policy writes a .hmac sidecar, and the gate verifies it on load. A signed control plane means an attacker who edits the policy to lower the bar cannot do so undetected.
The defense is not just detection; it is failing in the safe direction.
- Unsigned policy floor. If a policy is unsigned or its signature does not verify, the gate does not trust the file's thresholds. It applies a HIGH/CRITICAL severity floor, so the most serious findings still block even if someone tried to neuter the policy.
-
Unsigned-exceptions suppression floor. When signed exceptions are required (
SECURITY_REQUIRE_SIGNED_EXCEPTIONS), an unsigned exceptions file cannot suppress findings at HIGH or above. A waiver has to be signed to silence a serious finding.
Both have an explicit, named break-glass path so an emergency is possible but never accidental:
| Break-glass | Effect |
|---|---|
SECURITY_ALLOW_UNSIGNED_HIGH_SUPPRESSION |
Allow unsigned exceptions to suppress HIGH-and-above |
SECURITY_ATTEST_ALLOW_INCOMPLETE |
Allow attestation over an incomplete result |
These are deliberate overrides, set knowingly, not defaults.
Attestation is the signed claim that a review passed. It will not sign over a failing or incomplete result: a green attested report genuinely means the gate passed. The attestation chain itself is tamper-evident, opening with a genesis block and linking each block with HMAC or SHA-256, verified in constant time. You cannot rewrite history in the chain without detection.
Everything written under .mcp/ (reviews, reports, agent runs, baselines) is created with 0o600 permissions, owner read/write only. Reports may contain sensitive findings, so they are locked down on disk by default.
security-mcp reads untrusted repositories and runs scanners over untrusted code, and that output flows back toward an LLM-driven agent. A malicious repo could try to plant instructions in a filename, a comment, or scanner output to hijack the agent. Repo-derived tool output is sanitized before it reaches the model, and the agentic quarantine behavior is configurable via SECURITY_AGENTIC_QUARANTINE (strip, sanitize, quarantine, or off). The agentic-instructions check module also flags injection-prone patterns in the code itself.
There is no curl | sh install path. Installation goes through npm, and the installer verifies artifacts with SHA-256 before use. The supply chain into your machine is checkable, not a blind execution of a remote script.
SECURITY_OFFLINE disables all egress. The server then runs entirely from local analysis plus any cached threat intel, with no outbound calls at all. Even online, private scoped package names are never sent to public endpoints, so an internal package name cannot leak through a registry lookup. This makes the tool viable in regulated and air-gapped environments. See Environment Variables.
When SECURITY_MCP_SHARED_SECRET is set, the MCP channel is authenticated with a constant-time HMAC handshake. Secrets are at least 32 bytes, three failed attempts lock the caller out, and sessions expire after a default 8 hours capped at 24. This stops an unauthorized local process from driving the security tooling.
Two questions come up often: how is execution contained, and who gets to waive a finding.
How execution is contained:
- The server runs locally as a Node process and never sends your code to a third party. Repository access goes through guarded tools.
repo.read_fileandrepo.searchare path-traversal guarded, capped (10 MB per file), ReDoS-guarded, and secret-redacted, so a hostile repository cannot coax the tool into reading arbitrary files or hanging the scanner. - It does not execute untrusted repository code in order to analyze it. Detection is static analysis plus the bundled scanners. The optional live probes run only against targets you set explicitly (
SECURITY_STAGING_URL,SECURITY_AI_ENDPOINT). - Repo-derived output is sanitized before it reaches the model (see Prompt-injection sanitization above), and
SECURITY_OFFLINEremoves all egress. In CI the gate runs with least-privilege workflow permissions and SHA-pinned actions.
How approvals work:
- Waivers are policy-driven, not ad hoc. An exception must carry a ticket, an owner, an approver, and an approval role drawn from
approval_roles(default SecurityLead, GRC, CTO), and the approver cannot be the change author (no self-approval). Exceptions expire within 365 days, and an expired waiver becomes a blocking finding. - A waiver for a serious finding has to be signed: an unsigned exceptions file cannot suppress findings at HIGH or above by default.
- Actions that change posture require explicit human consent. Adaptive policy updates (
self_heal_loop) and generated OPA/Rego both require an opt-in flag, and attestation refuses to sign a result that is not PASS.
See Configuration and Policy for the exception schema and Environment Variables for the controls named here.
Found a weakness in security-mcp itself? Report it through GitHub private vulnerability reporting; see SECURITY.md in the repository. The project takes its own threat model seriously, which is the entire point of the controls above.
Start here
Engines
Agents
Reference
Operate