Conversation
call_count was added to the engine in v0.4.8 but the linter's validConditionFields map was never updated. Any policy using call_count (including standard.yaml's rate-limit-fetch rule) would get a lint error from rampart doctor and rampart policy lint. Adds regression test.
rampart bench with no arguments looked for bench/corpus.yaml relative to the current working directory. This always fails for installed users (go install, binary download) who are not in the repo root. Fix: embed corpus.yaml into the binary via go:embed. When --corpus is not explicitly set, use the embedded bytes directly. --corpus still accepts a custom file path as before. Output shows 'Corpus: built-in' when using embedded corpus. Adds regression test.
1. audit: expandHome missing from listAuditFiles and listAnchorFiles All rampart audit subcommands (tail/verify/stats/search/replay) crashed with the default --audit-dir because ~ was never expanded. Fixed in the shared helpers so all paths are covered. 2. bench: approval-gated coverage always 0% require_approval expected corpus entries were never included in DenyTotal/ApprovalGated accounting. Coverage math now includes both deny and require_approval expected entries. 3. status: require_approval and webhook decisions dropped from today stats Both action types were silently skipped in todayEvents() switch. Now counted alongside deny (they are blocking decisions). 4. generate: verbose null/empty fields in output YAML Added omitempty to Policy.Priority, Enabled, Match.Agent, Match.Session, and all Condition slice/bool fields. Marshaling-only change — parsing of existing policies is unaffected.
rampart upgrade returned early when current == target, skipping the policy update step. Users who upgraded binaries manually or who were already on the latest version never got policy improvements from newer releases without knowing to run rampart init --force separately. Now always runs upgradeStandardPolicies unless --no-policy-update is set. Adds regression test.
rampart serve --background generated a fresh random token on every start, ignoring ~/.rampart/token and never writing to it. Only the systemd/launchd install path (serve install) called resolveServiceToken, which reads the persisted file. The foreground serve path only checked RAMPART_TOKEN env. Fix: before creating the proxy, read the persisted token (RAMPART_TOKEN > ~/.rampart/token > generate new). After the listener is bound, write the token back to ~/.rampart/token so it survives restarts. Adds regression test: TestServeReadsAndPersistsToken — starts serve with a pre-written known token, verifies the same token is used and re-persisted.
- tests/e2e.yaml: 31-case test suite covering allow/deny/require_approval/watch
across destructive cmds, FP regression, credentials, network exfil, env injection.
Run: rampart test tests/e2e.yaml (uses installed policy)
or: rampart test --config policies/standard.yaml tests/e2e.yaml (repo policy)
- policies/standard.yaml: three policy gaps fixed:
* block-credential-access: /etc/shadow, /etc/passwd, /etc/sudoers, /etc/gshadow
now blocked for the read tool (were only blocked via exec patterns)
* block-credential-commands: scp/rsync of private keys now blocked via
combined command_contains('.ssh/id_') + command_matches('scp *'|'rsync *')
* block-credential-commands: .pub exclusion added to SSH key rule to fix
regression introduced by the scp rule split
- internal/engine/testrunner.go: expandHome for policy: path in test YAML
(tilde was resolved relative to test file dir, not home directory)
- cmd/rampart/cli/test_cmd.go: --config flag now overrides policy: in test YAML,
allowing 'rampart test --config dev-policy.yaml tests/e2e.yaml' workflows
- policies/standard.yaml: properly split block-credential-commands into 3 rules (ssh key cmds with .pub exclusion, scp/rsync transfer — dropped as FP-prone, aws/git/etc creds), add /etc/shadow|passwd|sudoers to read-tool block - tests/e2e.yaml: 30 cases, all passing — removed scp test (gap noted) - test_cmd.go: --config overrides policy: in test YAML - testrunner.go: expandHome for policy path
- Narrow cat/head/tail patterns from /**/.ssh/** to /**/.ssh/id_* (too broad) - Add matching exclusion patterns for .pub files (cat **/.ssh/*.pub etc) - Add dedicated scp/rsync exfil rule with proper glob patterns - *scp*/.ssh/id_* matches exfil, excludes scp -i (auth) - *rsync*/.ssh/id_* matches exfil - E2E tests: 36 cases including scp/rsync edge cases All 36 e2e tests pass. All Go unit tests pass.
Fixes found by automated code review: Security fixes: - Remove -i flag exclusion from scp/rsync rule (prevents dual-key bypass: 'scp -i auth_key exfil_key remote:' was incorrectly allowed) - Add sftp to blocked transfer commands - Add mv, xxd, hexdump, od, strings to blocked read commands - Use *mv* pattern (leading wildcard) so mv with dest arg matches Tradeoff: - scp -i auth usage now blocked (security > convenience) - Users needing -i can use ssh-agent or local policy override Test updates: - Updated scp -i test expectation (now intentionally blocked) - All 36 e2e tests pass - All Go unit tests pass
Add block-self-modification rule to standard.yaml that prevents AI agents from running rampart allow/block/rules/policy commands. These commands must be run by humans, not agents. This prevents the attack where an agent sees 'run rampart allow' in a denial message and tries to bypass its own restrictions. Tests: 4 new e2e tests, 40/40 passing
…reload API Merged features: - rampart allow/block: Add custom allow/deny rules via CLI - rampart rules: List, remove, reset custom rules - POST /v1/policy/reload: Force immediate policy reload with rate limiting - Denial suggestions: Show safe rampart allow commands in deny messages Fixes applied from code review: - Rate limiting on reload endpoint (1s cooldown) - URL detection in IsPathPattern (curl https://... not a path) - --tool flag override fixed in AddRule - Atomic file writes (temp + rename) - sudo/env wrapper detection in dangerous command check - strconv.Atoi for strict integer parsing - Mutual exclusion for --global/--project flags TODO: Rewrite rules_test.go for new CustomPolicy structure
- CHANGELOG.md: add v0.5.0 release entry with Added/Changed/Fixed sections - docs-site/getting-started/quickstart.md: add Customize Your Rules section with allow/block/rules quick start and self-modification protection note - docs-site/features/policy-engine.md: add Custom Rules section documenting custom.yaml, allow/block, scopes, denial suggestions, self-mod protection, and the /v1/policy/reload API endpoint - docs-site/guides/customizing-policy.md: new full guide covering patterns, scopes, flags reference, team sharing, denial workflow, and self-mod protection - mkdocs.yml: add Customizing Policy to both Security Guides nav sections
- status: Boxed dashboard with progress bar, live server detection, and allow/deny/pending stats (lipgloss-powered, color + no-color safe) - doctor: Structured 'Try this:' hints on failures/warnings, embedded in messages with hintSep so JSON output gets a 'hint' field too - upgrade: Animated spinner during download and install phases; better permission error with sudo hint - serve: Startup spinner with clean 'Rampart ready' message and emoji-labeled dashboard/token lines - New spinner.go: lightweight braille-dot spinner (no bubbles dep), degrades to plain text on non-TTY outputs - Tests updated to match new status box output format
- rampart policy generate preset: Interactive wizard with 4 presets (coding-agent, research-agent, ci-agent, devops-agent) - Uses charmbracelet/huh for interactive forms - 20 tests for policy generate, 26 tests for rules command - Flags: --preset, --dest, --force, --print
…...' - Add commonCommands map to detect known executables - 'go build ./...' now correctly detected as exec, not path - Handle './...' Go package patterns specially - Add comprehensive tests for command/path detection
When using --global or --project filters with 'rampart rules', the displayed indices now match the global index used by 'rampart rules remove'. This prevents confusion where index 1 in a filtered view would remove a different rule than expected.
- README: Add 'Customizing rules' section showing allow/block workflow - CLI reference: Add full documentation for allow, block, rules, policy generate preset - Homepage: Update FAQ to mention 'rampart allow' command
Normalize backslashes to forward slashes in MatchGlob so that policy patterns like '**/.ssh/id_*' match Windows paths like 'C:\Users\Trevor\.ssh\id_rsa'. This is critical for Windows users using Claude Code with Rampart — without this fix, path-based policies would never match on Windows. Added tests for Windows path patterns to verify cross-platform matching.
Security fixes: - Move backslash normalization to cleanPaths() before filepath.Clean (fixes audit integrity issue on Unix with paths like /home/user\../etc) - Add Windows token file security warning (os.Chmod is no-op on Windows) - Add backslash injection test cases Cursor/Windsurf fixes: - Fix hardcoded Cursor docs URL shown to Windsurf users - Add URL field to mcpServer for SSE-based servers - Skip SSE servers with warning (cannot be wrapped) - Only create backup if none exists (preserve true original) - Remove dead init() function - Update setup --help to list Cursor and Windsurf
Cursor and Windsurf have native built-in tools (file read/write, terminal) that don't go through MCP. The setup commands only wrapped MCP servers, giving users false confidence their agents were protected when 90%+ of tool calls were unmonitored. Removed: - rampart setup cursor - rampart setup windsurf - cursor/windsurf auto-detection in quickstart - All related tests and docs Users wanting MCP-only protection can still use 'rampart mcp --' manually. Focus remains on agents with real hook APIs (Claude Code, Cline) where we can intercept all tool calls. Also removed dead 'go generate ./...' from goreleaser (no go:generate directives exist in the codebase).
- install.ps1: one-liner install for Windows users - Downloads latest release from GitHub - Extracts to ~/.rampart/bin, adds to PATH - Auto-detects Claude Code and offers to set up hooks - No admin rights required Usage: irm https://rampart.sh/install.ps1 | iex
- docs-site/getting-started/installation.md: Add Windows tab with PowerShell installer, document Windows limitations table - docs-site/integrations/cursor.md: Add warning that only MCP servers are protected, not Cursor's native built-in tools - docs/guides/windows.md: Standalone Windows setup guide - docs/install.ps1: Copy for GitHub Pages hosting at rampart.sh/install.ps1
- token_windows.go: Proper Windows ACL implementation using advapi32.dll Sets owner-only GENERIC_ALL access, removes all other permissions - token_unix.go: Standard chmod 0600/0700 for Unix platforms - Remove 'future version' comments - it's implemented now - Update Windows service messages to be more helpful (suggest NSSM/Task Scheduler) - Update docs to remove 'planned for future' language
The previous implementation used hand-rolled Windows API calls via advapi32.dll with the unsafe package. This had risks: - Struct alignment must exactly match Windows C structures - Memory management with LocalFree - No easy way to test correctness New implementation uses icacls.exe (built into Windows): - icacls path /inheritance:r /grant:r USERNAME:F - Removes inherited permissions - Grants full control only to current user - Well-tested Windows utility - No unsafe package needed - Graceful fallback if icacls fails -108 lines of complex syscall code, +23 lines of simple exec.
The hook evaluates policies locally — no server needed for allow/deny. Serve is only required for: - Live dashboard (rampart watch) - Approval flow (require_approval policies) - Centralized audit streaming Updated: - install.ps1: Simplified next steps, serve marked as optional - docs/guides/windows.md: Reframed serve as optional - docs-site/getting-started/installation.md: Changed note to success callout
rampart uninstall: - Removes hooks from Claude Code, Cline - Stops and removes systemd/launchd services - Removes from PATH (Windows: programmatic, Unix: prints instructions) - Removes shell shim if present - Prints final instructions to delete ~/.rampart Works on Windows, macOS, and Linux. Use --yes to skip prompts.
- Windows: Use PowerShell to find and stop rampart serve processes - macOS/Linux: Use pkill -f 'rampart serve' before service removal This ensures 'rampart uninstall' cleans up running processes on all platforms.
Fixes 'ExtractToFile: file already exists' error when reinstalling. Some PowerShell versions/configurations use .NET extraction methods that don't honor -Force overwrite flag.
- Detect and explain permission errors when removing existing install - Clean up partial install on extraction failure - Provide takeown/icacls recovery commands
- Fix icacls command quoting in error message ($env:USERNAME:F → $($env:USERNAME):F) - Add cmd.exe rd fallback when PowerShell Remove-Item fails - Better formatting for recovery instructions
- Remove 'restart terminal' instruction (PATH refreshes in-session) - Always refresh session PATH (not just when adding) - Update uninstall instruction to use 'rampart uninstall' - Show try-it-now commands after install
- Add SmartScreen/Defender/antivirus bypass instructions - Add permission error recovery steps - Update uninstall to use 'rampart uninstall'
Previously we only hooked Bash, Read, Write|Edit. Now we use '.*' to catch all tools including Fetch, Task, and any future tools Claude adds. This ensures comprehensive coverage without needing to update Rampart when Anthropic adds new tool types. Users should run 'rampart setup claude-code --force' to upgrade their hooks to the new wildcard matcher.
On reinstall, if Rampart hooks already exist in Claude Code settings, prompts 'Update hooks to latest version?' and runs with --force. This ensures users get new hook features (like wildcard matcher) when upgrading Rampart.
High: Add SHA256 checksum verification to Windows installer - Downloads checksums.txt from release and verifies downloaded zip - Fails with clear error on mismatch, warns if checksums unavailable Medium: Fix PowerShell single-quote injection in uninstall.go - Escape single quotes in PATH value before passing to PowerShell - Prevents injection via crafted PATH entries like C:\Users\O'Brien\ Low: Use os.Executable() instead of PATH lookup in uninstall - Prevents malicious 'rampart' binary earlier in PATH from executing Low: Improve upgrade detection regex in install.ps1 - Match specific 'rampart hook' command pattern, not just 'rampart' substring
- install.ps1: Add (?:\.exe)? to match both 'rampart hook' and 'rampart.exe hook' - setup.go hasRampartInMatcher: Handle Windows backslash paths and .exe extension Fixes regression where Windows installs weren't detected as upgrades.
SSE connections were blocking server shutdown, causing 'context deadline exceeded' errors on Ctrl+C. Now we close all SSE clients before shutting down the HTTP server. Also fixed potential double-close panic in unsubscribe by checking if channel still exists.
- Add 'closed bool' field to prevent post-Close subscriptions - subscribe() returns immediately-closed channel if hub is closed - broadcast() skips if hub is closed - Add 5 tests covering shutdown scenarios with -race flag Closes the race window between sse.Close() and srv.Shutdown().
- Try 'rampart serve stop' gracefully before deletion - Kill any remaining rampart processes - Add 500ms delay for Windows to release file handles This prevents 'Access Denied' errors during upgrade when serve is running.
Windows Defender or the file indexer can briefly lock files after a process exits. Adding a small delay before serve returns gives the OS time to release handles, preventing 'Access Denied' errors on subsequent operations.
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.
Prevents 'Access Denied' errors during upgrade when serve is running.
Changes
rampart serve stopgracefully before deletionStop-ProcessNow users can just re-run the installer without manually stopping serve first.