Skip to content

Security hardening: command gate, secret redaction, macOS sandbox#1

Open
laplaque wants to merge 11 commits intourjitbhatia:masterfrom
laplaque:feature/secret-redaction
Open

Security hardening: command gate, secret redaction, macOS sandbox#1
laplaque wants to merge 11 commits intourjitbhatia:masterfrom
laplaque:feature/secret-redaction

Conversation

@laplaque
Copy link
Copy Markdown

Summary

Adds four security layers to it2mcp, turning it from a convenience tool into a hardened MCP server suitable for production use with AI assistants.

What's included

1. Command confirmation gate (gate.py)

  • Allow/deny/ask command classification with configurable lists in config.yaml
  • macOS osascript dialog for unknown commands: Allow / Allow for Session / Deny
  • Configurable timeout with auto-deny (default 30s)
  • Commands piped through secfilter before display in dialog
  • "Allow for Session" uses in-memory list (resets on server restart)
  • Integrated into session_run, session_send, tab_new, window_new, and all batch equivalents

2. Secret redaction engine (redact/)

  • Pluggable architecture: built-in + custom plugins auto-discovered from ~/.config/it2mcp/redact-plugins/
  • Applied to all session_read output before returning to the MCP client
  • Built-in plugins: GitHub, GitLab, AWS, GCP, Azure, OpenAI, Anthropic, JWTs, bearer tokens, Slack, Stripe, SendGrid, Twilio, PEM/OpenSSH private key blocks, env var output
  • Configurable: disable specific plugins, custom replacement string

3. macOS sandbox (sandbox/, install.sh)

  • Kernel-level file access restrictions via sandbox-exec
  • Blocks: SSH keys, credentials, shell profiles, .env files, cloud provider configs, shell history, password manager DBs, and more
  • env -i stripping of inherited environment variables
  • Anti-bypass: sandbox profile unreadable from within, sandbox-exec can't re-launch, debuggers blocked
  • install.sh sets up sandbox profile, iTerm2 dynamic profile, and default config

4. Session hardening

  • Removed session_set_variable tool (chicken-and-egg problem allowed bypassing session tagging)
  • tab_new and window_new always use "MCP Sandboxed" profile (no profile parameter)
  • Sessions auto-tag via sandboxed shell — no manual tagging needed

Other additions

  • session_status tool: busy/idle detection via foreground job inspection
  • session_interrupt tool: reliable SIGINT delivery to foreground process
  • Structured audit log covers all gate decisions (allow/deny/timeout)
  • 192 tests passing

Breaking changes

  • session_set_variable tool removed
  • tab_new and window_new no longer accept a profile parameter
  • Commands sent via session_run/session_send are now subject to the command gate (requires commands section in config, or all commands will trigger the confirmation dialog)

Installation

git clone https://github.com/laplaque/it2mcp.git
cd it2mcp
uv sync
./install.sh

See README for full setup instructions.

laplaque added 11 commits April 6, 2026 10:43
Adds a RedactEngine with a plugin pipeline that scrubs secrets from
terminal output before it reaches the MCP client. Secrets can be used
by tools (e.g. curl -H "Bearer $TOKEN") but never displayed.

Architecture:
- RedactPlugin ABC with patterns() and redact() methods
- RedactEngine loads built-in + custom plugins, applies as pipeline
- Custom plugin auto-discovery from ~/.config/it2mcp/redact-plugins/

Built-in plugins:
- GitHub: ghp_, gho_, ghu_, ghs_, ghr_, github_pat_ tokens
- GitLab: glpat-, gldt-, glrt-, glcbt-, glft-, glsoat- tokens
- AWS: AKIA access keys, secret keys, session tokens
- Generic: OpenAI sk-, Anthropic sk-ant-, JWTs, Bearer tokens,
  Slack, Stripe, SendGrid, Twilio, PEM blocks
- Env vars: context-aware KEY=VALUE redaction for sensitive names
- Add RedactEngine to Config, loaded from config.yaml redact section
- Add redact_output() convenience function for use by tools
- Redact audit log result text before writing (audit log itself is clean)
- Default: redaction enabled with [REDACTED] replacement
Wrap session_read and _b_session_read output through redact_output()
so terminal content is scrubbed before reaching the MCP client.
Only two lines changed in server.py — minimal invasive patch.
92 tests at 95% coverage:
- RedactEngine: enabled/disabled, plugin loading, config, pipeline,
  custom plugin discovery, error resilience
- GitHubPlugin: all 6 token prefixes, boundary cases
- GitLabPlugin: all 6 token prefixes, boundary cases
- AWSPlugin: AKIA/ASIA keys, secret keys, session tokens
- GenericSecretsPlugin: OpenAI, Anthropic, JWT, Bearer, Slack,
  Stripe, SendGrid, Twilio, PEM blocks
- EnvVarsPlugin: KEY=VALUE, export/declare, sensitive vs non-sensitive

Run: uv run --extra test pytest tests/ -v
Shows all redaction options: enabled, replacement string,
disabled_plugins list, and custom_plugin_dir path.
… Ctrl+C

Fixes two bugs documented in the bug report:
1. No way to detect if a session is busy - session_status now returns
   job_name, job_pid, shell_pid, and is_at_shell_prompt boolean
2. Ctrl+C via session_send doesn't work - session_interrupt sends SIGINT
   directly via os.kill() with ETX fallback

Implementation:
- Extract pure functions (parse_pid, is_at_shell_prompt, build_session_status,
  determine_interrupt_action) for testability
- Add InterruptAction class to encapsulate interrupt decision logic
- Register new tools in security tiers (READ and INTERACT)
- Add batch handlers for both tools
- Add pytest-asyncio for async test support

Tests:
- 65 new unit tests covering all pure functions
- 100% coverage on new code
- Tests for shell detection, PID parsing, edge cases, security tiers
…w/window_new

- Removed session_set_variable tool, batch handler, and TOOL_TIERS entry
- tab_new and window_new always use 'MCP Sandboxed' profile (no profile param)
- Updated session_list docstring to reflect auto-tagging via sandboxed profile
- Updated README: session tagging section, removed session_set_variable from tables
- New cloud_cli plugin: catches GCP ya29 tokens, Azure accessToken JSON and bare TSV output
- Generic plugin: full PEM/OpenSSH key block redaction (header+body+footer), standalone header fallback
- Tests for all new patterns
- New gate.py: allow/deny/ask command classification with osascript confirmation dialog
- macOS dialog with Allow, Allow for Session, Deny buttons + configurable timeout
- Commands piped through secfilter before display in dialog
- Session allow list (in-memory, resets on server restart)
- Integrated into session_run, session_send, tab_new, window_new and all batch equivalents
- New cloud_cli redaction plugin: GCP ya29 tokens, Azure accessToken JSON/TSV output
- Enhanced generic plugin: full PEM/OpenSSH key block redaction (header+body+footer)
- Config: commands section with allow/deny lists, timeout, enabled flag
- Tests for gate pattern matching, command classification, all redaction plugins
- Fixed _session_allowed type (set, not list)
- Updated example config with command gate and cloud_cli plugin
- install.sh: sets up sandbox profile, iTerm2 dynamic profile, config
- sandbox/iterm-sandbox.sb: macOS kernel-level file access restrictions
- sandbox/sandboxed-shell.sh: auto-tagging wrapper with env -i stripping
- config/env-passthrough.conf: environment variable allowlist
- README rewritten: four security layers, command gate, all tools listed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant