Conversation
…ssions - Token no longer embedded in shell shim scripts. Written to separate 0600 file, read at runtime via cat. Not visible in /proc or ps output. - Audit directories use 0700 (was 0755) in daemon and wrap commands. - Base64 command decoding capped at 1MB to prevent memory exhaustion. - MCP proxy pendingCalls: TTL eviction (5min) + hard cap (1000 entries) prevents unbounded memory growth from orphaned JSON-RPC requests. - Webhook timeout capped at 30s maximum regardless of policy config.
…ging - WebSocket: 90s read deadline + 30s ping/pong heartbeat detects dead connections. Goroutines no longer hang indefinitely on broken pipes. - Approval store: watchExpiry goroutines now exit on Store.Close(), preventing goroutine leak during daemon shutdown. - Shell wrapper temp directory restricted to 0700 (was default). - Token startup log reduced from 8-char prefix to 4-char prefix.
Agent frameworks (OpenClaw, etc.) prepend descriptive comments to shell commands (e.g. '# Check status\nkubectl get pods'). These comments caused command_matches patterns to miss because matching started at the '#' character instead of the actual command. Now strips leading comment lines and blank lines in enrichParams before commands are evaluated against policy rules. Inline comments are preserved. Includes 10 test cases covering edge cases (empty, all-comments, multiline).
peg
added a commit
that referenced
this pull request
Feb 18, 2026
- Dedup identical pending approvals within 60s window using sha256(tool+command+agent) to handle agent retries on timeout/reconnect (#13) - Write hash-chained audit events on approval resolution (approve/deny/always-allow) with full metadata: tool, command, resolution, resolved_by, approval_id, persist (#22) - Add integration test for hook fallback to native prompt on unreachable serve (#15)
peg
added a commit
that referenced
this pull request
Feb 18, 2026
* feat: persist-to-policy (Always Allow) for approval resolve - Add 'persist' field to resolve request; when true + approved, auto-generates an allow rule and appends to auto-allowed.yaml - New internal/engine/persist.go: GeneralizeCommand, GenerateAllowRule, AppendAllowRule functions - Command generalization: keeps first 2 tokens, wildcards rest - Paths kept exact (security-sensitive) - MCP tools matched by tool name with default condition - Auto-allowed rules written to ~/.rampart/policies/auto-allowed.yaml - Full test coverage in persist_test.go - All existing tests pass * feat(watch): interactive approval keybindings Add --serve-url and --serve-token flags to 'rampart watch'. When set, watch polls the serve API for pending approvals and displays them in a highlighted section at the top of the TUI. Keybindings: 1-9 select pending approval by number a approve selected d deny selected A approve + always allow (persist) q quit Without --serve-url, watch works exactly as before (read-only audit tail). Includes ApprovalClient for API communication, countdown timers, ANSI-colored pending section, and comprehensive tests. * feat: dashboard 'Always Allow' button + persist support Add 'Always Allow' button between Approve and Deny in the dashboard. Sends resolve with persist:true to auto-generate an allow rule in auto-allowed.yaml. Same functionality as 'A' keybinding in watch. * docs: update rampart.sh messaging — guardrails not firewall - Hero: 'Guardrails for AI agents. Works when sandboxes can't.' - Replace 'policy engine/firewall' with 'guardrails' throughout - Soften sandbox comparison language - Fix tamper-proof → tamper-evident - Update meta descriptions and structured data * fix: critical issues for unified approval system 1. Empty when: clause now matches all tool calls (catch-all behavior) - matchCondition returns true for empty conditions - Lint message changed to INFO level 2. Watch TUI 'A' keybinding sends persist:true in resolve request - ApprovalClient.Resolve now accepts persist parameter - 'a' = approve (persist:false), 'A' = always allow (persist:true) 3. --serve-token flag deprecated in favor of RAMPART_TOKEN env var - Warning printed when flag is used directly (visible in ps aux) - Flag marked deprecated but kept for backward compatibility 4. GeneralizeCommand: dangerous commands never generalized - Blocklist: rm, chmod, chown, kill, dd, mkfs, reboot, shutdown, etc. - Single-token commands kept exact (ls -> ls, not ls *) 5. Atomic persist writes using temp file + rename pattern - Prevents corruption from concurrent Always Allow actions 6. Polling loop accepts context.Context for clean cancellation - requestApprovalCtx and pollApprovalCtx with context support - HTTP requests use NewRequestWithContext * feat: approval deduplication, audit trail for resolutions, fallback test - Dedup identical pending approvals within 60s window using sha256(tool+command+agent) to handle agent retries on timeout/reconnect (#13) - Write hash-chained audit events on approval resolution (approve/deny/always-allow) with full metadata: tool, command, resolution, resolved_by, approval_id, persist (#22) - Add integration test for hook fallback to native prompt on unreachable serve (#15) * feat: directory-based policy loading and periodic reload Add support for loading policies from multiple YAML files in a directory, enabling the persist-to-policy feature to work end-to-end. New components: - PolicyStore interface (Load/Path) implemented by FileStore, DirStore, MultiStore - DirStore: loads all *.yaml files from a directory in sorted order - MultiStore: combines a primary config file with a policy directory - Engine.StartPeriodicReload(interval): re-reads policies on a timer - Engine.Stop(): terminates the reload goroutine CLI changes: - 'rampart serve' and 'rampart hook' now accept --config-dir flag - Default: ~/.rampart/policies/ is included if it exists - 'rampart serve' accepts --reload-interval (default 30s) Merge behavior: - Files loaded in sorted order (deterministic) - default_action taken from first file that specifies one - Duplicate policy names: first wins, later skipped with warning - Invalid YAML files: logged and skipped (don't break other files) This closes the gap where AppendAllowRule writes to ~/.rampart/policies/auto-allowed.yaml but the engine never loaded it. * feat: configurable approval timeout (--approval-timeout flag) * feat: dashboard redesign with brand colors + dangerous command detection - Rampart brand colors (#FF6392 accent, zinc palette) - Inter + JetBrains Mono fonts matching rampart.sh - Stats bar: pending/approved/denied counters - Dangerous command detection with orange warning badges - Improved card layout with better visual hierarchy - Responsive mobile design - CSP updated for Google Fonts - Configurable approval timeout (--approval-timeout flag) * fix: dashboard timer flicker + more specific timeout message - Update countdown timers in-place via data-id targeting instead of full DOM rebuild every second (eliminates card jumping) - Timeout message now says 'no response received within' for clarity * dashboard: fix layout shifts, add virtual scrolling, optimize for scale - Virtual scrolling: only renders visible cards + buffer (5 above/below). Supports thousands of pending approvals with ~20 DOM nodes max. - Efficient diffing: Map keyed by approval ID, only add/remove/update cards that actually changed between polls. Never rebuilds entire list. - Timer updates: pure textContent swaps on visible cards only, with classList.toggle guard to avoid unnecessary DOM touches. - Cards built with createElement (no innerHTML), escape results cached. - Event delegation on container (no per-card listeners). - Adaptive polling: 2s with pending, 5s idle, exponential backoff on error. - Visibility API: pauses polling when tab is hidden, resumes on focus. - requestAnimationFrame batching for DOM mutations after poll. - CSS: added contain:layout style on cards, min-width on timer to prevent layout shift from text width changes. Replaced pulse animation with one-shot cardPulse that only fires on genuinely new cards. - History only re-renders when dirty flag is set. - Stats use textContent with change guards. * feat(api): add audit history endpoints Add four new authenticated API endpoints for querying historical audit data: - GET /v1/audit/events — query events with pagination, filtering by tool, action, and agent. Returns most-recent-first. - GET /v1/audit/dates — list available audit log dates - GET /v1/audit/export — download a day's JSONL as attachment (streamed) - GET /v1/audit/stats — aggregate stats across a date range Includes WithAuditDir server option and comprehensive test coverage. * fix: wire audit dir to proxy server for audit API endpoints * feat: dashboard v2 — table layout, audit log, zero layout shift * fix: command column showing ID instead of actual command extractCmd() checked a.request.command (audit event format) before a.command (approval API format). Approvals have command at top level. * fix: Always Allow button requires confirmation + clearer label - Clicking 'Always Allow' now shows a confirm dialog explaining it creates a permanent auto-approve rule, showing the command that will be allowed - Button label changed from bare '★' to '★ Always' with descriptive tooltip - Cleaned up duplicate test rules from auto-allowed.yaml * fix: approval ordering (newest first) + action buttons column width - Sort approvals by created_at before prepending so newest appears on top - Widen actions column to 140px to fit all three buttons - Actions container uses nowrap to prevent line breaking * fix: persist dedup + clean YAML output - Skip appending rule if identical pattern already exists (same tool + conditions) - Use clean YAML structs with omitempty to eliminate verbose empty fields - auto-allowed.yaml now outputs minimal, readable YAML * fix: sort pending approvals by creation time for deterministic ordering Store.List() iterated a map, producing random order on each call. Now sorts by CreatedAt (oldest first) so the dashboard shows consistent ordering across refreshes. * feat: resizable table columns via drag handles Drag the right edge of any column header to resize. Uses mousedown/ mousemove tracking with table-layout:fixed for predictable sizing. Accent-colored handle appears on hover. * feat: dashboard polish — history persistence, theme toggle, bulk actions * fix: audit findings — double Stop() panic, audit dir leak, CHANGELOG v0.3.0 - Engine.Stop() uses sync.Once to prevent panic on double close - /v1/audit/dates no longer exposes server filesystem path - Comprehensive CHANGELOG for v0.3.0 with breaking change callout for empty when: clause behavior * chore: version to v0.2.29 * chore: version to v0.2.3
peg
added a commit
that referenced
this pull request
Feb 18, 2026
* feat: persist-to-policy (Always Allow) for approval resolve - Add 'persist' field to resolve request; when true + approved, auto-generates an allow rule and appends to auto-allowed.yaml - New internal/engine/persist.go: GeneralizeCommand, GenerateAllowRule, AppendAllowRule functions - Command generalization: keeps first 2 tokens, wildcards rest - Paths kept exact (security-sensitive) - MCP tools matched by tool name with default condition - Auto-allowed rules written to ~/.rampart/policies/auto-allowed.yaml - Full test coverage in persist_test.go - All existing tests pass * feat(watch): interactive approval keybindings Add --serve-url and --serve-token flags to 'rampart watch'. When set, watch polls the serve API for pending approvals and displays them in a highlighted section at the top of the TUI. Keybindings: 1-9 select pending approval by number a approve selected d deny selected A approve + always allow (persist) q quit Without --serve-url, watch works exactly as before (read-only audit tail). Includes ApprovalClient for API communication, countdown timers, ANSI-colored pending section, and comprehensive tests. * feat: dashboard 'Always Allow' button + persist support Add 'Always Allow' button between Approve and Deny in the dashboard. Sends resolve with persist:true to auto-generate an allow rule in auto-allowed.yaml. Same functionality as 'A' keybinding in watch. * docs: update rampart.sh messaging — guardrails not firewall - Hero: 'Guardrails for AI agents. Works when sandboxes can't.' - Replace 'policy engine/firewall' with 'guardrails' throughout - Soften sandbox comparison language - Fix tamper-proof → tamper-evident - Update meta descriptions and structured data * fix: critical issues for unified approval system 1. Empty when: clause now matches all tool calls (catch-all behavior) - matchCondition returns true for empty conditions - Lint message changed to INFO level 2. Watch TUI 'A' keybinding sends persist:true in resolve request - ApprovalClient.Resolve now accepts persist parameter - 'a' = approve (persist:false), 'A' = always allow (persist:true) 3. --serve-token flag deprecated in favor of RAMPART_TOKEN env var - Warning printed when flag is used directly (visible in ps aux) - Flag marked deprecated but kept for backward compatibility 4. GeneralizeCommand: dangerous commands never generalized - Blocklist: rm, chmod, chown, kill, dd, mkfs, reboot, shutdown, etc. - Single-token commands kept exact (ls -> ls, not ls *) 5. Atomic persist writes using temp file + rename pattern - Prevents corruption from concurrent Always Allow actions 6. Polling loop accepts context.Context for clean cancellation - requestApprovalCtx and pollApprovalCtx with context support - HTTP requests use NewRequestWithContext * feat: approval deduplication, audit trail for resolutions, fallback test - Dedup identical pending approvals within 60s window using sha256(tool+command+agent) to handle agent retries on timeout/reconnect (#13) - Write hash-chained audit events on approval resolution (approve/deny/always-allow) with full metadata: tool, command, resolution, resolved_by, approval_id, persist (#22) - Add integration test for hook fallback to native prompt on unreachable serve (#15) * feat: directory-based policy loading and periodic reload Add support for loading policies from multiple YAML files in a directory, enabling the persist-to-policy feature to work end-to-end. New components: - PolicyStore interface (Load/Path) implemented by FileStore, DirStore, MultiStore - DirStore: loads all *.yaml files from a directory in sorted order - MultiStore: combines a primary config file with a policy directory - Engine.StartPeriodicReload(interval): re-reads policies on a timer - Engine.Stop(): terminates the reload goroutine CLI changes: - 'rampart serve' and 'rampart hook' now accept --config-dir flag - Default: ~/.rampart/policies/ is included if it exists - 'rampart serve' accepts --reload-interval (default 30s) Merge behavior: - Files loaded in sorted order (deterministic) - default_action taken from first file that specifies one - Duplicate policy names: first wins, later skipped with warning - Invalid YAML files: logged and skipped (don't break other files) This closes the gap where AppendAllowRule writes to ~/.rampart/policies/auto-allowed.yaml but the engine never loaded it. * feat: configurable approval timeout (--approval-timeout flag) * feat: dashboard redesign with brand colors + dangerous command detection - Rampart brand colors (#FF6392 accent, zinc palette) - Inter + JetBrains Mono fonts matching rampart.sh - Stats bar: pending/approved/denied counters - Dangerous command detection with orange warning badges - Improved card layout with better visual hierarchy - Responsive mobile design - CSP updated for Google Fonts - Configurable approval timeout (--approval-timeout flag) * fix: dashboard timer flicker + more specific timeout message - Update countdown timers in-place via data-id targeting instead of full DOM rebuild every second (eliminates card jumping) - Timeout message now says 'no response received within' for clarity * dashboard: fix layout shifts, add virtual scrolling, optimize for scale - Virtual scrolling: only renders visible cards + buffer (5 above/below). Supports thousands of pending approvals with ~20 DOM nodes max. - Efficient diffing: Map keyed by approval ID, only add/remove/update cards that actually changed between polls. Never rebuilds entire list. - Timer updates: pure textContent swaps on visible cards only, with classList.toggle guard to avoid unnecessary DOM touches. - Cards built with createElement (no innerHTML), escape results cached. - Event delegation on container (no per-card listeners). - Adaptive polling: 2s with pending, 5s idle, exponential backoff on error. - Visibility API: pauses polling when tab is hidden, resumes on focus. - requestAnimationFrame batching for DOM mutations after poll. - CSS: added contain:layout style on cards, min-width on timer to prevent layout shift from text width changes. Replaced pulse animation with one-shot cardPulse that only fires on genuinely new cards. - History only re-renders when dirty flag is set. - Stats use textContent with change guards. * feat(api): add audit history endpoints Add four new authenticated API endpoints for querying historical audit data: - GET /v1/audit/events — query events with pagination, filtering by tool, action, and agent. Returns most-recent-first. - GET /v1/audit/dates — list available audit log dates - GET /v1/audit/export — download a day's JSONL as attachment (streamed) - GET /v1/audit/stats — aggregate stats across a date range Includes WithAuditDir server option and comprehensive test coverage. * fix: wire audit dir to proxy server for audit API endpoints * feat: dashboard v2 — table layout, audit log, zero layout shift * fix: command column showing ID instead of actual command extractCmd() checked a.request.command (audit event format) before a.command (approval API format). Approvals have command at top level. * fix: Always Allow button requires confirmation + clearer label - Clicking 'Always Allow' now shows a confirm dialog explaining it creates a permanent auto-approve rule, showing the command that will be allowed - Button label changed from bare '★' to '★ Always' with descriptive tooltip - Cleaned up duplicate test rules from auto-allowed.yaml * fix: approval ordering (newest first) + action buttons column width - Sort approvals by created_at before prepending so newest appears on top - Widen actions column to 140px to fit all three buttons - Actions container uses nowrap to prevent line breaking * fix: persist dedup + clean YAML output - Skip appending rule if identical pattern already exists (same tool + conditions) - Use clean YAML structs with omitempty to eliminate verbose empty fields - auto-allowed.yaml now outputs minimal, readable YAML * fix: sort pending approvals by creation time for deterministic ordering Store.List() iterated a map, producing random order on each call. Now sorts by CreatedAt (oldest first) so the dashboard shows consistent ordering across refreshes. * feat: resizable table columns via drag handles Drag the right edge of any column header to resize. Uses mousedown/ mousemove tracking with table-layout:fixed for predictable sizing. Accent-colored handle appears on hover. * feat: dashboard polish — history persistence, theme toggle, bulk actions * fix: audit findings — double Stop() panic, audit dir leak, CHANGELOG v0.3.0 - Engine.Stop() uses sync.Once to prevent panic on double close - /v1/audit/dates no longer exposes server filesystem path - Comprehensive CHANGELOG for v0.3.0 with breaking change callout for empty when: clause behavior * chore: version to v0.2.29 * chore: version to v0.2.3 * fix: goreleaser writes formula to Formula/ directory
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.
Security Fixes
Two commits addressing 9 findings from automated security review:
Commit 1: Token exposure, resource limits, permissions
Commit 2: WebSocket deadlines, approval cleanup, dir perms
104 lines changed across 9 files. No functional changes — hardening only.