Harden secure mode: deny-by-default classification plus env, git, and config fixes - #28
Merged
Merged
Conversation
…g fixes Invert the executable model to deny-by-default classification and close a batch of secure-mode weaknesses found while auditing that change. Classification (GHSA-gg85-6grh-63fp): an allowlisted executable now runs only if it is a known data-only utility or governed by an argument policy. Command wrappers (env, timeout, nice, xargs, ...) and any unclassified binary are rejected even when allowlisted. uniq gains a policy that rejects its positional output file; tar is not classifiable (its useful modes all write a caller-chosen path) and is dropped from the governed set. Executor: - Run children with a minimal environment. The server environment and any .env are no longer inherited, so an allowlisted reader (cat /proc/self/environ, env) cannot exfiltrate secrets. - Neutralise git repo-local config that executes programs: core.fsmonitor is disabled and diff/show/blame/log get --no-ext-diff/--no-textconv injected, so a hostile repository's diff.external or textconv driver cannot run. - Enforce max_output_size at write time and stop the child on overflow instead of after it has already filled memory. Validator and config: - Reject a relative argv[0] containing a path separator (./ls): it was existence-checked against one directory and executed relative to another. - Reject NUL in the command string (the parser silently dropped it, so the audited command differed from what ran). - Match blocked_patterns/blocked_commands against the resolved argv, closing a split-quote evasion (cat /etc/pass"wd"). - Fail closed on config load: a file that omits keys keeps the secure defaults, and enabled:false requires MCP_SHELL_ALLOW_UNSAFE. SECURITY.md documents the threat model, secure-mode guarantees, known gaps and the scope for vulnerability reports.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Harden secure mode. Inverts the executable model to deny-by-default classification (closing GHSA-gg85-6grh-63fp) and closes a batch of weaknesses found by auditing that change with three independent security passes.
Classification (GHSA-gg85-6grh-63fp)
An allowlisted executable now runs only if it is a data-only utility or governed by an argument policy. Command wrappers (
env,timeout,nice,xargs,setsid, ...) and any unclassified binary are rejected even when allowlisted, so there is no denylist of "dangerous binaries" to keep chasing.uniqgains a policy that rejects its positional output file (uniq in out= arbitrary write).taris dropped from the governed set: its useful modes all write a caller-chosen path (tar -cf <path>), so it is not classifiable as safe. Allowlisting it is now rejected as unclassified.Executor
.envare no longer inherited.cat /proc/self/environ/envcan no longer exfiltrate secrets exported by the client or loaded at startup..git/configand.gitattributes, invisible to any argv check.core.fsmonitoris disabled anddiff/show/blame/logget--no-ext-diff/--no-textconvinjected, so a hostile repository'sdiff.externalortextconvdriver cannot run during a read-only command.max_output_sizeenforced at write time, cancelling the child on overflow instead of checking after it has already filled memory (cat /dev/zerono longer OOMs the server).Validator / config
argv[0]with a separator rejected (./ls): it was existence-checked against one directory and executed relative to another.blocked_patterns/blocked_commandsmatched against the resolved argv, closing a split-quote evasion (cat /etc/pass"wd").enabled: falserequiresMCP_SHELL_ALLOW_UNSAFE.Known, documented gap
branch/tag/symbolic-ref/reflogcan still mutate refs within the repository git runs in. Bounded to the repo (no arbitrary-path write, no execution), lower severity, noted inSECURITY.md.Tests
Every fix landed red-first. The security-critical ones (git repo-config neutralisation, output cap, blocked_patterns-on-argv) were mutation-checked: reverting the fix turns the test red.
go test -race ./...is green.Notes
v0.8.0).deploy/CI check will be red: the account is out of GitHub Actions minutes. That is billing noise, not a regression. Local gate (go test -race ./...,go vet) is green.