Skip to content

Releases: nnemirovsky/sluice

v0.11.0

14 Apr 13:04
7e043b8

Choose a tag to compare

New Features

  • add ExecInspector for trampoline and dangerous pattern detection in MCP tool arguments
  • add MITM response DLP scanning for HTTPS response bodies and headers
  • add sluice policy add redact CLI subcommand and /policy redact Telegram command

Details

  • ExecInspector (internal/mcp/exec_inspect.go) detects trampoline patterns (bash -c, python -c), dangerous commands (rm -rf /, chmod 0?[0-7]?777, curl | sh, fork bombs), env overrides (GIT_SSH_COMMAND, LD_PRELOAD, DYLD_INSERT_LIBRARIES), and shell metacharacters. Field-scoped scanning with recursion into nested maps (wrapped schemas), case-insensitive slot matching, and split-argv reconstruction across command + args. Default tool-name patterns anchored to the MCP __ separator to avoid false positives on tools like shellcheck.
  • Response DLP (internal/proxy/response_dlp.go) runs per-response regex scan of buffered response bodies and headers using InspectRedactRule rows from the policy store. Supports gzip, br, deflate (zlib-wrapped per RFC 9110), and zstd. Handles up to 2 stacked Content-Encoding layers. Bounded decompression via io.LimitReader capped at maxProxyBody (16 MiB). Distinct from phantom-token stripping, which protects outbound requests. This protects the agent from seeing real credentials leaked by upstreams in responses.
  • Rule management across all channels. New CLI subcommand sluice policy add redact <pattern> --replacement "[REDACTED_X]" and Telegram /policy redact <pattern> [replacement]. HTTP API already supported this via POST /api/rules with verdict: "redact". TOML import/export continues to work via [[redact]] blocks. SIGHUP reloads rebuild the engine and atomically swap via atomic.Pointer.
  • Audit redaction. exec_block audit events include only the attack category (trampoline, dangerous_cmd, env_override, metachar), never the raw matched content, so audit logs cannot leak credentials embedded in blocked payloads.

Known limitation

Responses with Content-Type: text/event-stream or bodies exceeding go-mitmproxy's StreamLargeBodies (5 MiB) enter streaming mode, which skips the buffered DLP scan. A one-per-connection WARNING log fires when DLP rules are configured but the response streams. Stream-aware DLP is listed as Future work.

PR: #33

v0.10.2

13 Apr 06:24
6f673c4

Choose a tag to compare

Fixes

  • Stop Telegram from auto-linking destinations in /policy show, /policy allow, /policy deny, and approval prompts (#32). Destinations, host:port, and request URLs now render as inline monospace.

v0.10.1

13 Apr 05:40
b519e2c

Choose a tag to compare

Fixes

  • Telegram /policy show now surfaces protocols, replacement, name, and source (#31)
  • CLI sluice policy list now shows replacement for redact rules (#31)

v0.10.0

13 Apr 02:44
421579e

Choose a tag to compare

Highlights

QUIC/HTTP3 now works end-to-end through the full tun2proxy -> sluice -> upstream pipeline. UDP and QUIC policy evaluation is now unified with TCP semantics (unscoped rules match all transports, engine default verdict applies). Broader e2e test coverage across WebSocket, gRPC, QUIC/HTTP3, DNS, and IMAP/SMTP.

New Features

  • QUIC SNI extraction from Initial packets via RFC 9001 decryption (ExtractQUICSNI, supports QUIC v1 and v2)
  • CRYPTO data accumulation across fragmented QUIC Initial packets so SNI can be reassembled from large ClientHellos
  • Broker request deduplication with bounded per-session packet buffer (prevents duplicate Telegram prompts during approval wait)
  • sluice policy add --protocols flag for creating protocol-scoped rules
  • WebSocket handshake credential injection (requires the go-mitmproxy fork fixes)
  • Comprehensive e2e tests for WebSocket, gRPC, QUIC/HTTP3, DNS, and IMAP/SMTP

Bug Fixes

  • QUIC falls back to the engine's configured default verdict instead of hardcoded Deny
  • Unscoped policy rules now apply to UDP/QUIC (DNS keeps its own evaluation path)
  • Shared-IP session key collision: pending approvals now keyed by hostname, preventing CDN-fronted destinations from colliding
  • Race between session publish and pending entry delete closed atomically
  • httptest servers use IPv4-only listeners to avoid IPv6 bind failures in sandboxed environments
  • SSH jump host test flakiness addressed

Upstream PRs

  • lqqyt2423/go-mitmproxy#100: forward modified request headers on WebSocket upgrade (paired with existing PR firing Requestheaders for WS)

v0.9.0

12 Apr 10:15
085fd23

Choose a tag to compare

Per-request policy, go-mitmproxy migration, QUIC Ask

- Per-request HTTP policy: "Allow Once" means one HTTP request, not one TCP connection
- Replaced goproxy with go-mitmproxy for HTTP/2 per-stream interception
- gRPC-over-HTTP/2 now has per-request policy (each stream triggers approval)
- QUIC/HTTP3 per-request Ask verdicts via EvaluateQUICDetailed
- Single Telegram message per request (combined destination + method + path)
- WebSocket approval via go-mitmproxy fork (Requestheaders fires before upgrade)
- E2E tests with configurable webhook approval channel
- Protocol-aware deferred ask for non-TLS protocols (SSH, SMTP, plain TCP)

v0.8.1

11 Apr 14:29
7478657

Choose a tag to compare

fix(cli): wrap fs.Parse with reorderFlagsBeforePositional in 7 commands (#25)

Seven CLI subcommands (binding remove, cred remove, policy remove,
policy import, mcp remove, channel update, channel remove) called
fs.Parse(args) directly, so any positional argument before a flag
stopped flag parsing and fell through to flag defaults. With --db,
this meant invocations like 'sluice binding remove 2 --db /path'
operated on the default data/sluice.db instead of the requested path.

Each command now goes through reorderFlagsBeforePositional like the
other sibling subcommands already do. Regression tests per command
exercise the positional-before-flags ordering.

Also fixes the cred dispatcher's usage line to mention the 'update'
subcommand added in v0.8.0.

v0.8.0

11 Apr 10:33
32eaa23

Choose a tag to compare

feat(cli,api,store): binding CRUD, multi-destination cred add, cred update (#24)

- New sluice binding add/list/update/remove CLI subcommands and PATCH /api/bindings/{id}
- New sluice cred update CLI subcommand and PATCH /api/credentials/{name}
- sluice cred add --destination is now repeatable
- New migration 000005: case-insensitive UNIQUE index on bindings(credential, LOWER(destination))
  with operator-actionable conflict detection on upgrade
- Bindings of the same credential can share env_var
- Sentinel errors for proper API HTTP status mapping (409/404/400/500)
- CAS-protected credential rollback against concurrent writers
- Authoritative cred type detection via credential_meta

v0.7.3

10 Apr 13:58
5bdf5a5

Choose a tag to compare

fix(telegram): preserve code block when editing approval messages (#21)

* fix(telegram): preserve code block when editing approval messages

Edits on approval prompts (resolved by button, timed out, or
cancelled) were appending a status suffix to a stored rendered HTML
string. Under some conditions the appended suffix caused Telegram to
drop the <pre><code class="language-json"> formatting on the args
block, so approved/denied prompts lost the pretty-printed JSON code
block.

Switch msgMap to store the full ApprovalRequest instead of a
pre-rendered text string. Every edit path (CancelApproval, inflight
timeout, callback-resolved, callback-timed-out) now re-renders the
body by calling FormatApprovalMessage(am.req) fresh and appending the
status suffix to the result. This guarantees the <pre><code> block is
always intact in the final body we send to editMessageText.

* fix(telegram): pre-load msgMap to avoid edit race with cancelOnChannels

broker.Resolve synchronously calls cancelOnChannels -> tc.CancelApproval
on the same Telegram channel that just resolved the request. That cleanup
path did LoadAndDelete on msgMap and issued its own "(resolved via another
channel)" edit. Control then returned to handleCallback, which could no
longer find the entry in msgMap and fell back to editing with
cq.Message.Text - Telegram's plain-text extraction, where <pre>/<code>
tags are already stripped. The second edit clobbered the first, leaving
the user with plain-text JSON after tapping Allow/Deny.

Fix: handleCallback now LoadAndDelete's the msgMap entry into a local
'am' variable before calling broker.Resolve. This takes ownership of the
entry so CancelApproval becomes a no-op, and handleCallback's edit
becomes the single last-write-wins render with the <pre><code> block
intact and the "Allowed (once)/(request timed out)" label appended.

Add regression test TestHandleCallbackAllowOncePreservesCodeBlockOnEdit
that simulates an MCP approval with ToolArgs, taps Allow, and asserts
the final editMessageText payload still contains
<pre><code class="language-json"> and the status label.

v0.7.2

10 Apr 13:24
aff2467

Choose a tag to compare

fix(mcp): set transport=streamable-http in wire-mcp; revert GET SSE

v0.7.1

10 Apr 13:07
ad14509

Choose a tag to compare

feat(telegram): pretty-print tool call args in HTML code block