Repository navigation
v3.0.0
Highlights
- MCP Ecosystem β
crust mcpnamespace unifies the MCP gateway, adds
crust mcp-discover(auto-patches Claude/Cursor/Windsurf configs) and
crust mcp-http(Streamable HTTP gateway) - Critical SSE Buffer Fix β Buffer overflow previously bypassed the rule
engine; now retries the full request as non-streaming for complete evaluation
(fail-closed) - Fail-Closed Shell Parsing β Unparseable shell commands now block by
default instead of passing through, closing a rule-bypass vector - OS Keyring β API keys stored in OS keyring (macOS Keychain, Windows
DPAPI, Linux Secret Service) with file fallback; no more plaintext env vars
at rest - Locked Rules β New rule category that survives
--disable-builtin,
protecting self-management endpoints and git hooks - 4 New Builtin Rules β System config, persistence, reverse shell, and
SSRF protection - 13 Security Gaps Closed β Percent-encoded self-protect bypass, nc/socat
UNIX socket variants, macOS Keychain/GNOME Keyring/1Password/KeePassXC
paths, .netrc, browser credential DBs, Anthropic DLP pattern fix, and more - NTFS ADS Defense β Strips alternate data stream suffixes
(file.txt:Zone.Identifier) before path matching on Windows - Filesystem-Aware Case Sensitivity β
internal/pathutilprobes the
actual filesystem to decide case-folding rather than using compile-time OS
constants
β οΈ Breaking Change
crust mcp-gateway has been renamed to crust mcp.
Update any scripts, systemd units, shell aliases, or CI configs that reference
the old command name.
New Features
crust mcpβ unified MCP command namespace (replacescrust mcp-gateway)crust mcp-discoverβ auto-discovers installed MCP servers and patches
Claude/Cursor/Windsurf configscrust mcp-httpβ MCP Streamable HTTP gateway- OS keyring secret storage with file fallback;
ANTHROPIC_API_KEY/
OPENAI_API_KEYenv vars still accepted as fallback - Locked rules: rules that survive
--disable-builtin(self-management, git
hooks, agent configs) - 4 new builtin locked rules:
protect-system-config,protect-persistence,
block-reverse-shell,block-ssrf-metadata - Auto-restore agent configs on daemon shutdown
- Desktop app token protection (VS Code, JetBrains, browser credential DBs)
$HOMEexpansion in YAML rule patterns- Locked rule count exposed in API and TUI rule list header
- Atomic file lock-on-open (O_EXLOCK/O_SHLOCK) eliminating TOCTOU on rule
file reads/writes - ACP E2E test suite with mock agent
- FreeBSD CI
Security Fixes
- Critical: SSE buffer overflow now retries as non-streaming β previously
FlushAll()sent uninspected buffered events andio.Copystreamed the
remainder unfiltered (security bypass) - Critical: Percent-encoded self-protect bypass (
%63%72%75%73%74for
"crust") β URL-decode before regex match - High: Fail-closed on unparseable shell commands β unanalyzable input
now blocks instead of passing through - High:
nc.traditional/nc.openbsdreverse shell bypass β word
boundary\bnc\bdidn't match dotted variants - High:
ncat --exec=bypass β regex required trailing space, missed
=form - High:
nc -U,ncat --unixsock,socat UNIX:added to socket
self-protection regex - High: Dynamic self-protection rules (rules-dir delete, rule-file write,
socket access) now locked so--disable-builtincannot remove them - High: Trailing-dot FQDN bypass of SSRF metadata rule
(metadata.google.internal.) β strip trailing dot before hostname match - High: HTML escaping corruption β
json.Marshalwas encoding&,<,
>as\u0026/\u003c/\u003ein intercepted responses and
non-streaming retry bodies; fixed withSetEscapeHTML(false)throughout - Medium: NTFS alternate data stream suffix stripping
(file.txt:Zone.Identifier) - Medium: macOS Keychain, GNOME Keyring, KDE Wallet, 1Password,
KeePassXC, LastPass paths added to protection rules - Medium: GitHub CLI
hosts.yml,.netrc, Edge/BraveLogin Data/
Cookies, Gradle/Maven credential files added - Medium: Anthropic DLP pattern fixed (
sk-ant-api03β
sk-ant-api\d{2}) to match future key versions - Medium: Fish history XDG path fix; fish
conf.dadded to persistence
protection - Medium:
git credentialsseverity upgraded to critical - Medium: 4 additional security gaps closed (action gaps, missing paths,
over-blocking) - Client disconnect now cancels non-streaming retry (was using
context.Background()) - Unknown YAML fields in rule files now warn instead of silently dropping
(catches typos like stalealert_webhook:) - SSE scanner errors checked;
sync.Oncefor session close; upstream
failures logged
Refactoring
internal/selfprotectextracted as an independent packageinternal/proxyrenamed tointernal/httpproxyEvaluate()modularized into 3 phase methods (PreparePaths,
EvaluateConditions, ApplyDLP)- Null byte stripping unified to single
stripNullBytesfunction - Hardcoded path protections unified into
pathGuardregistry internal/messagemodule for centralized block message formattinginternal/pathutilfor filesystem-based case sensitivity detection- Phantom newtype IDs (
TraceID,SessionID,SpanID) prevent parameter
swap bugs InterceptionContext,SSEBufferConfig,SSERequestContextstructs
replace positional constructor args- Sum types for
shellExecResult,bufferState,NewMatch/NoMatch
Testing
- ACP E2E tests with mock agent
FuzzInterceptAnthropicResponseuses real rule engine + HTML-escaping
invariant (\u0026must not appear in re-serialized output)FuzzBufferEventnow exercises size-limit overflow path on every seedTestBufferedSSEWriter_Timeoutverifies fail-closed: flush after timeout
writes zero bytesTestRetryAsNonStreaming_RespectsClientContextverifies client disconnect
cancels retryTestRetryAsNonStreaming_ErrorStatusCodescovers 429/500 upstream
propagation- Bypass regression tests for FQDN trailing-dot and dynamic rule lockout
- FreeBSD CI added; macOS CI gated to main branch