From 47c3cbbba47c64eab91d1121dc223b7c0b83040a Mon Sep 17 00:00:00 2001 From: rsteube Date: Fri, 16 Sep 2022 18:40:35 +0200 Subject: [PATCH] added mitmproxy --- completers/mitmproxy_completer/cmd/root.go | 106 ++++++++ completers/mitmproxy_completer/main.go | 7 + pkg/actions/tools/mitmproxy/console.go | 11 + pkg/actions/tools/mitmproxy/file.go | 21 ++ pkg/actions/tools/mitmproxy/filter.go | 39 +++ pkg/actions/tools/mitmproxy/mode.go | 32 +++ pkg/actions/tools/mitmproxy/option.go | 273 +++++++++++++++++++++ pkg/actions/tools/mitmproxy/pattern.go | 56 +++++ 8 files changed, 545 insertions(+) create mode 100644 completers/mitmproxy_completer/cmd/root.go create mode 100644 completers/mitmproxy_completer/main.go create mode 100644 pkg/actions/tools/mitmproxy/console.go create mode 100644 pkg/actions/tools/mitmproxy/file.go create mode 100644 pkg/actions/tools/mitmproxy/filter.go create mode 100644 pkg/actions/tools/mitmproxy/mode.go create mode 100644 pkg/actions/tools/mitmproxy/option.go create mode 100644 pkg/actions/tools/mitmproxy/pattern.go diff --git a/completers/mitmproxy_completer/cmd/root.go b/completers/mitmproxy_completer/cmd/root.go new file mode 100644 index 0000000000..5ef460b714 --- /dev/null +++ b/completers/mitmproxy_completer/cmd/root.go @@ -0,0 +1,106 @@ +package cmd + +import ( + "strings" + + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/pkg/actions/net" + "github.com/rsteube/carapace-bin/pkg/actions/tools/mitmproxy" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: "mitmproxy", + Short: "interactive, SSL/TLS-capable intercepting proxy", + Long: "https://mitmproxy.org/", + Run: func(cmd *cobra.Command, args []string) {}, +} + +func Execute() error { + return rootCmd.Execute() +} +func init() { + carapace.Gen(rootCmd).Standalone() + + rootCmd.Flags().StringArray("allow-hosts", []string{}, "Opposite of --ignore-hosts") + rootCmd.Flags().Bool("anticache", false, "Strip out request headers that might cause the server to return 304-not-modified") + rootCmd.Flags().Bool("anticomp", false, "Try to convince servers to send us un-compressed data") + rootCmd.Flags().String("cert-passphrase", "", "Passphrase for decrypting the private key provided in the --cert option") + rootCmd.Flags().String("certs", "", "SSL certificates of the form \"[domain=]path\"") + rootCmd.Flags().StringP("client-replay", "C", "", "Replay client requests from a saved file") + rootCmd.Flags().Bool("commands", false, "Show all commands and their signatures") + rootCmd.Flags().String("console-layout", "", "Console layout") + rootCmd.Flags().Bool("console-layout-headers", false, "Show layout component headers") + rootCmd.Flags().BoolP("help", "h", false, "show this help message and exit") + rootCmd.Flags().Bool("http2", false, "Enable HTTP/2 support") + rootCmd.Flags().StringArray("ignore-hosts", []string{}, "Ignore host and forward all traffic without processing it") + rootCmd.Flags().String("intercept", "", "Intercept filter expression") + rootCmd.Flags().String("key-size", "", "TLS key size for certificates and CA") + rootCmd.Flags().String("listen-host", "", "Address to bind proxy to") + rootCmd.Flags().StringP("listen-port", "p", "", "Proxy service port") + rootCmd.Flags().String("map-local", "", "Map remote resources to a local file") + rootCmd.Flags().StringP("map-remote", "M", "", "Map remote resources to another remote URL") + rootCmd.Flags().StringP("mode", "m", "", "Set mode") + rootCmd.Flags().StringP("modify-body", "B", "", "Replacement pattern") + rootCmd.Flags().StringP("modify-headers", "H", "", "Header modify pattern") + rootCmd.Flags().Bool("no-anticache", false, "Do not strip out request headers that might cause the server to return 304-not-modified") + rootCmd.Flags().Bool("no-anticomp", false, "Do not try to convince servers to send us un-compressed data") + rootCmd.Flags().Bool("no-console-layout-headers", false, "Do not show layout component headers") + rootCmd.Flags().Bool("no-http2", false, "Disable HTTP/2 support") + rootCmd.Flags().Bool("no-rawtcp", false, "Disable raw TCP connections") + rootCmd.Flags().BoolP("no-server", "n", false, "Do not start a proxy server") + rootCmd.Flags().Bool("no-server-replay-kill-extra", false, "Do not kill extra requests during replay") + rootCmd.Flags().Bool("no-server-replay-nopop", false, "Remove flows from server replay state after use") + rootCmd.Flags().Bool("no-server-replay-refresh", false, "") + rootCmd.Flags().Bool("no-showhost", false, "Do not use the Host header to construct URLs for display") + rootCmd.Flags().Bool("no-ssl-insecure", false, "Verify upstream server SSL/TLS certificates") + rootCmd.Flags().Bool("options", false, "Show all options and their default values") + rootCmd.Flags().String("proxyauth", "", "Require proxy authentication") + rootCmd.Flags().BoolP("quiet", "q", false, "Quiet") + rootCmd.Flags().Bool("rawtcp", false, "Enable raw TCP connections") + rootCmd.Flags().StringP("rfile", "r", "", "Read flows from file") + rootCmd.Flags().StringP("save-stream-file", "w", "", "Stream flows to file as they arrive") + rootCmd.Flags().StringArrayP("scripts", "s", []string{}, "Execute a script") + rootCmd.Flags().Bool("server", false, "Start a proxy server") + rootCmd.Flags().StringP("server-replay", "S", "", "Replay server responses from a saved file") + rootCmd.Flags().Bool("server-replay-kill-extra", false, "Kill extra requests during replay") + rootCmd.Flags().Bool("server-replay-nopop", false, "Don't remove flows from server replay state after use") + rootCmd.Flags().Bool("server-replay-refresh", false, "Refresh server replay responses by adjusting date, expires and last-modified headers") + rootCmd.Flags().StringArray("set", []string{}, "Set an option") + rootCmd.Flags().Bool("showhost", false, "Use the Host header to construct URLs for display") + rootCmd.Flags().BoolP("ssl-insecure", "k", false, "Do not verify upstream server SSL/TLS certificates") + rootCmd.Flags().String("stickyauth", "", "Set sticky auth filter") + rootCmd.Flags().String("stickycookie", "", "Set sticky cookie filter") + rootCmd.Flags().String("tcp-hosts", "", "Generic TCP SSL proxy mode for all hosts that match the pattern") + rootCmd.Flags().String("upstream-auth", "", "Add HTTP Basic authentication to upstream proxy and reverse proxy requests") + rootCmd.Flags().BoolP("verbose", "v", false, "Increase log verbosity") + rootCmd.Flags().Bool("version", false, "show version number and exit") + rootCmd.Flags().String("view-filter", "", "Limit the view to matching flows") + + // TODO complete flag completions + carapace.Gen(rootCmd).FlagCompletion(carapace.ActionMap{ + "certs": carapace.ActionMultiParts("=", func(c carapace.Context) carapace.Action { + if len(c.Parts) < 2 { + return carapace.ActionFiles() + } + return carapace.ActionValues() + }), + "client-replay": carapace.ActionFiles(), + "console-layout": mitmproxy.ActionConsoleLayouts(), + "listen-port": net.ActionPorts(), + "mode": mitmproxy.ActionModes(), + "modify-body": mitmproxy.ActionModifyBodyPattern(), + "modify-headers": mitmproxy.ActionModifyHeaderPattern(), + "rfile": carapace.ActionFiles(), + "save-stream-file": mitmproxy.ActionAppendableFiles(), + "scripts": carapace.ActionFiles(), + "server-replay": carapace.ActionFiles(), + "set": carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if splitted := strings.SplitN(c.CallbackValue, "=", 2); len(splitted) > 1 { + c.CallbackValue = splitted[1] + return mitmproxy.ActionOptionValues(splitted[0]).Invoke(c).Prefix(splitted[0] + "=").ToA().NoSpace() + } + return mitmproxy.ActionOptionNames().NoSpace() + }), + }) +} diff --git a/completers/mitmproxy_completer/main.go b/completers/mitmproxy_completer/main.go new file mode 100644 index 0000000000..fec4b65db8 --- /dev/null +++ b/completers/mitmproxy_completer/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/rsteube/carapace-bin/completers/mitmproxy_completer/cmd" + +func main() { + cmd.Execute() +} diff --git a/pkg/actions/tools/mitmproxy/console.go b/pkg/actions/tools/mitmproxy/console.go new file mode 100644 index 0000000000..bb6ee28dc9 --- /dev/null +++ b/pkg/actions/tools/mitmproxy/console.go @@ -0,0 +1,11 @@ +package mitmproxy + +import "github.com/rsteube/carapace" + +// ActionConsoleLayouts completes console layouts +// +// horizontal +// single +func ActionConsoleLayouts() carapace.Action { + return carapace.ActionValues("horizontal", "single", "vertical") +} diff --git a/pkg/actions/tools/mitmproxy/file.go b/pkg/actions/tools/mitmproxy/file.go new file mode 100644 index 0000000000..c838c91b80 --- /dev/null +++ b/pkg/actions/tools/mitmproxy/file.go @@ -0,0 +1,21 @@ +package mitmproxy + +import ( + "strings" + + "github.com/rsteube/carapace" +) + +// ActionAppendableFiles completes files with optional `+` prefix +// +// path/to/file +// +path/to/file +func ActionAppendableFiles() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if strings.HasPrefix(c.CallbackValue, "+") { + c.CallbackValue = strings.TrimPrefix(c.CallbackValue, "+") + return carapace.ActionFiles().Invoke(c).Prefix("+").ToA() + } + return carapace.ActionFiles() + }) +} diff --git a/pkg/actions/tools/mitmproxy/filter.go b/pkg/actions/tools/mitmproxy/filter.go new file mode 100644 index 0000000000..765b9f4563 --- /dev/null +++ b/pkg/actions/tools/mitmproxy/filter.go @@ -0,0 +1,39 @@ +package mitmproxy + +import "github.com/rsteube/carapace" + +func ActionFlowFilters() carapace.Action { + return carapace.ActionValuesDescribed( + "a", "Match asset in response: CSS, JavaScript, images, fonts", + "all", "Match all flows", + "b", "Body", + "bq", "Request body", + "bs", "Response body", + "c", "HTTP response code", + "comment", "Flow comment", + "d", "Domain", + "dns", "Match DNS flows", + "dst", "Match destination address", + "e", "Match error", + "h", "Header", + "hq", "Request header", + "hs", "Response header", + "http", "Match HTTP flows", + "m", "Method", + "marked", "Match marked flows", + "marker", "Match marked flows with specified marker", + "meta", "Flow metadata", + "q", "Match request with no response", + "replay", "Match replayed flows", + "replayq", "Match replayed client request", + "replays", "Match replayed server response", + "s", "Match response", + "src", "Match source address", + "t", "Content-type header", + "tcp", "Match TCP flows", + "tq", "Request Content-Type header", + "ts", "Response Content-Type header", + "u", "URL", + "websocket", "Match WebSocket flows", + ) +} diff --git a/pkg/actions/tools/mitmproxy/mode.go b/pkg/actions/tools/mitmproxy/mode.go new file mode 100644 index 0000000000..d44f2ceaf3 --- /dev/null +++ b/pkg/actions/tools/mitmproxy/mode.go @@ -0,0 +1,32 @@ +package mitmproxy + +import ( + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/pkg/actions/net" +) + +// ActionModes completes modes +// +// regular +// reverse +func ActionModes() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return carapace.Batch( + carapace.ActionValues("regular", "transparent", "socks5"), + carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return carapace.ActionValues("reverse", "upstream").Invoke(c).Suffix(":").ToA() + case 1: + return carapace.ActionValues("http", "https").Invoke(c).Suffix("://").ToA() + case 2: + return carapace.ActionValues() + case 3: + return net.ActionPorts() + default: + return carapace.ActionValues() + } + }), + ).ToA() + }) +} diff --git a/pkg/actions/tools/mitmproxy/option.go b/pkg/actions/tools/mitmproxy/option.go new file mode 100644 index 0000000000..041fdf466d --- /dev/null +++ b/pkg/actions/tools/mitmproxy/option.go @@ -0,0 +1,273 @@ +package mitmproxy + +import ( + "strings" + + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/pkg/actions/net" + "github.com/rsteube/carapace-bin/pkg/actions/net/http" + "github.com/rsteube/carapace-bin/pkg/actions/net/ssh" + "github.com/rsteube/carapace/pkg/style" +) + +// ActionOptionNames completion option names +// +// anticomp (Try to convince servers to send us un-compressed data) +// block_global (Block connections from public IP addresses) +func ActionOptionNames() carapace.Action { + return carapace.ActionValuesDescribed( + "add_upstream_certs_to_client_chain", "Add all certificates of the upstream server to the certificate chain", + "allow_hosts", "Opposite of --ignore-hosts", + "anticache", "Strip out request headers that might cause the server to return", + "anticomp", "Try to convince servers to send us un-compressed data", + "block_global", "Block connections from public IP addresses", + "block_list", "Block matching requests and return an empty response", + "block_private", "Block connections from local (private) IP addresses", + "body_size_limit", "Byte size limit of HTTP request and response bodies", + "cert_passphrase", "Passphrase for decrypting the private key provided in the --cert option", + "certs", "SSL certificates of the form \"[domain=]path\"", + "ciphers_client", "Set supported ciphers for client <-> mitmproxy connections", + "ciphers_server", "Set supported ciphers for mitmproxy <-> server connections using OpenSSL syntax", + "client_certs", "Client certificate file or directory", + "client_replay", "Replay client requests from a saved file", + "client_replay_concurrency", "Concurrency limit on in-flight client replay requests", + "command_history", "Persist command history between mitmproxy invocations", + "confdir", "Location of the default mitmproxy configuration files", + "connect_addr", "Set the local IP address that mitmproxy should use when connecting to upstream servers", + "connection_strategy", "Determine when server connections should be established", + "console_default_contentview", "The default content view mode", + "console_eventlog_verbosity", "EventLog verbosity", + "console_flowlist_layout", "Set the flowlist layout", + "console_focus_follow", "Focus follows new flows", + "console_layout", "Console layout", + "console_layout_headers", "Show layout component headers", + "console_mouse", "Console mouse interaction", + "console_palette", "Color palette", + "console_palette_transparent", "Set transparent background for palette", + "console_strip_trailing_newlines", "Strip trailing newlines from edited request/response bodies", + "content_view_lines_cutoff", "Flow content view lines limit", + "dns_listen_host", "Address to bind DNS server to", + "dns_listen_port", "DNS server service port", + "dns_mode", "DNS mode", + "dns_server", "Start a DNS server", + "export_preserve_original_ip", "make an effort to connect to the same IP as in the original request", + "http2", "Enable/disable HTTP/2 support", + "http2_ping_keepalive", "Send a PING frame if an HTTP/2 connection is idle for specified number of seconds", + "ignore_hosts", "Ignore host and forward all traffic without processing it", + "intercept", "Intercept filter expression", + "intercept_active", "Intercept toggle", + "keep_host_header", "Reverse Proxy: Keep the original host header instead of rewriting it to the reverse proxy target", + "key_size", "TLS key size for certificates and CA", + "listen_host", "Address to bind proxy to", + "listen_port", "Proxy service port", + "map_local", "Map remote resources to a local file using a pattern of the form \"[/flow-filter]/url-regex/file-or-directory-path\"", + "map_remote", "Map remote resources to another remote URL using a pattern of the form \"[/flow-filter]/url-regex/replacement\"", + "mode", "proxy mode", + "modify_body", "Replacement pattern of the form \"[/flow-filter]/regex/[@]replacement\"", + "modify_headers", "Header modify pattern of the form \"[/flow-filter]/header-name/[@]header-value\"", + "normalize_outbound_headers", "Normalize outgoing HTTP/2 header names", + "onboarding", "Toggle the mitmproxy onboarding app", + "onboarding_host", "Onboarding app domain", + "onboarding_port", "Port to serve the onboarding app from", + "proxy_debug", "Enable debug logs in the proxy core", + "proxyauth", "Require proxy authentication", + "rawtcp", "Enable/disable raw TCP connections", + "readfile_filter", "Read only matching flows", + "rfile", "Read flows from file", + "save_stream_file", "Stream flows to file as they arrive", + "save_stream_filter", "Filter which flows are written to file", + "scripts", "Execute a script", + "server", "Start a proxy server", + "server_replay", "Replay server responses from a saved file", + "server_replay_ignore_content", "Ignore request content while searching for a saved flow to replay", + "server_replay_ignore_host", "Ignore request destination host while searching for a saved flow to replay", + "server_replay_ignore_params", "Request parameters to be ignored while searching for a saved flow to replay", + "server_replay_ignore_payload_params", "Request payload parameters to be ignored while searching for a saved flow to replay", + "server_replay_ignore_port", "Ignore request destination port while searching for a saved flow to replay", + "server_replay_kill_extra", "Kill extra requests during replay", + "server_replay_nopop", "Don't remove flows from server replay state after use", + "server_replay_refresh", "Refresh server replay responses by adjusting headers", + "server_replay_use_headers", "Request headers that need to match while searching for a saved flow to replay", + "showhost", "Use the Host header to construct URLs for display", + "ssl_insecure", "Do not verify upstream server SSL/TLS certificates", + "ssl_verify_upstream_trusted_ca", "Path to a PEM formatted trusted CA certificate", + "ssl_verify_upstream_trusted_confdir", "Path to a directory of trusted CA certificates for upstream server verification", + "stickyauth", "Set sticky auth filter", + "stickycookie", "Set sticky cookie filter. Matched against requests", + "stream_large_bodies", "Stream data to the client if response body exceeds the given threshold", + "tcp_hosts", "Generic TCP SSL proxy mode for all hosts that match the pattern", + "tls_version_client_max", "Set the maximum TLS version for client connections", + "tls_version_client_min", "Set the minimum TLS version for client connections", + "tls_version_server_max", "Set the maximum TLS version for server connections", + "tls_version_server_min", "Set the minimum TLS version for server connections", + "upstream_auth", "Add HTTP Basic authentication to upstream proxy and reverse proxy requests", + "upstream_cert", "Connect to upstream server to look up certificate details", + "validate_inbound_headers", "Make sure that incoming HTTP requests are not malformed", + "view_filter", "Limit the view to matching flows", + "view_order", "Flow sort order", + "view_order_reversed", "Reverse the sorting order", + "websocket", "Enable/disable WebSocket support", + ) +} + +// ActionOptionValues completes option validate_inbound_headers +// +// auto +// css +func ActionOptionValues(option string) carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + actionBool := carapace.ActionValues("true", "false").StyleF(style.ForKeyword) + actionTlsVersions := carapace.ActionValues("UNBOUNDED", "SSL3", "TLS1", "TLS1_1", "TLS1_2", "TLS1_3") + return map[string]carapace.Action{ + "add_upstream_certs_to_client_chain": actionBool, + "allow_hosts": carapace.ActionValues(), + "anticache": actionBool, + "anticomp": actionBool, + "block_global": actionBool, + "block_list": carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if len(c.CallbackValue) > 0 { + return carapace.ActionMultiParts(c.CallbackValue[:1], func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 2: + return http.ActionStatusCodes() + default: + return carapace.ActionValues() + } + }) + } + return carapace.ActionValues() + }), + "block_private": actionBool, + "body_size_limit": carapace.ActionValues(), + "cert_passphrase": carapace.ActionValues(), + "certs": carapace.ActionMultiParts("=", func(c carapace.Context) carapace.Action { + if len(c.Parts) > 0 { + return carapace.ActionFiles() + } + return carapace.ActionValues() + }), + "ciphers_client": ssh.ActionCiphers(), + "ciphers_server": ssh.ActionCiphers(), + "client_certs": carapace.ActionFiles(), + "client_replay": carapace.ActionFiles(), + "client_replay_concurrency": carapace.ActionValues(), + "command_history": actionBool, + "confdir": carapace.ActionDirectories(), + "connect_addr": carapace.ActionValues(), + "connection_strategy": carapace.ActionValues("eager", "lazy"), + "console_default_contentview": carapace.ActionValues("auto", "raw", "hex", "graphql", "json", "xml/html", "wbxml", "javascript", + "css", "url-encoded", "multipart form", "image", "query", "protocol buffer", "msgpack", "grpc/protocol buffer"), + "console_eventlog_verbosity": carapace.ActionValues("error", "warn", "info", "alert", "debug").StyleF(style.ForLogLevel), + "console_flowlist_layout": carapace.ActionValues("default", "list", "table"), + "console_focus_follow": actionBool, + "console_layout": ActionConsoleLayouts(), + "console_layout_headers": actionBool, + "console_mouse": actionBool, + "console_palette": carapace.ActionValues("dark", "light", "lowdark", "lowlight", "solarized_dark", "solarized_light"), + "console_palette_transparent": actionBool, + "console_strip_trailing_newlines": actionBool, + "content_view_lines_cutoff": carapace.ActionValues(), + "dns_listen_host": carapace.ActionValues(), + "dns_listen_port": net.ActionPorts(), + "dns_mode": carapace.Batch( + carapace.ActionValuesDescribed( + "regular", "requests will be resolved using the local resolver", + "transparent", "transparent mode", + ), + carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return carapace.ActionValuesDescribed("reverse", "forward queries to another DNS server transparent").Invoke(c).Suffix(":").ToA() + case 1: + return net.ActionIpv4Addresses() + case 2: + return net.ActionPorts() + default: + return carapace.ActionValues() + + } + }), + ).ToA(), + "dns_server": actionBool, + "export_preserve_original_ip": actionBool, + "http2": actionBool, + "http2_ping_keepalive": carapace.ActionValues(), + "ignore_hosts": carapace.ActionValues(), + "intercept": carapace.ActionValues(), // TODO completion? + "intercept_active": actionBool, + "keep_host_header": actionBool, + "key_size": carapace.ActionValues(), + "listen_host": carapace.ActionValues(), + "listen_port": net.ActionPorts(), + "map_local": carapace.ActionValues(), // TODO completion + "map_remote": carapace.ActionValues(), + "mode": ActionModes(), + "modify_body": ActionModifyBodyPattern(), + "modify_headers": ActionModifyHeaderPattern(), + "normalize_outbound_headers": actionBool, + "onboarding": actionBool, + "onboarding_host": carapace.ActionValues(), + "onboarding_port": net.ActionPorts(), + "proxy_debug": actionBool, + "proxyauth": carapace.ActionValues(), + "rawtcp": actionBool, + "readfile_filter": actionOptionFlowFilters(), + "rfile": carapace.ActionFiles(), + "save_stream_file": ActionAppendableFiles(), + "save_stream_filter": actionOptionFlowFilters(), + "scripts": carapace.ActionFiles(), + "server": actionBool, + "server_replay": carapace.ActionFiles(), + "server_replay_ignore_content": actionBool, + "server_replay_ignore_host": actionBool, + "server_replay_ignore_params": carapace.ActionValues(), + "server_replay_ignore_payload_params": carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return http.ActionMediaTypes().Invoke(c).ToMultiPartsA("/") + }), + "server_replay_ignore_port": actionBool, + "server_replay_kill_extra": actionBool, + "server_replay_nopop": actionBool, + "server_replay_refresh": actionBool, + "server_replay_use_headers": http.ActionRequestHeaderNames(), + "showhost": actionBool, + "ssl_insecure": actionBool, + "ssl_verify_upstream_trusted_ca": carapace.ActionFiles(), + "ssl_verify_upstream_trusted_confdir": carapace.ActionDirectories(), + "stickyauth": carapace.ActionValues(), // TODO completion? + "stickycookie": carapace.ActionValues(), // TODO completion? + "stream_large_bodies": carapace.ActionValues(), + "tcp_hosts": carapace.ActionValues(), + "tls_version_client_max": actionTlsVersions, + "tls_version_client_min": actionTlsVersions, + "tls_version_server_max": actionTlsVersions, + "tls_version_server_min": actionTlsVersions, + "upstream_auth": carapace.ActionValues(), + "upstream_cert": actionBool, + "validate_inbound_headers": actionBool, + "view_filter": carapace.ActionValues(), // TODO completion? + "view_order": carapace.ActionValues("time", "method", "url", "size"), + "view_order_reversed": actionBool, + "websocket": actionBool, + }[option] + }) +} + +func actionOptionFiles() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if strings.HasPrefix(c.CallbackValue, "@") { + c.CallbackValue = strings.TrimPrefix(c.CallbackValue, "@") + return carapace.ActionFiles().Invoke(c).Prefix("@").ToA() + } + return carapace.ActionValues() + }) +} + +func actionOptionFlowFilters() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if index := strings.LastIndex(c.CallbackValue, "~"); index != -1 { + return ActionFlowFilters().Invoke(c).Prefix(c.CallbackValue[:index+1]).ToA() + } + return carapace.ActionValues() + }) + +} diff --git a/pkg/actions/tools/mitmproxy/pattern.go b/pkg/actions/tools/mitmproxy/pattern.go new file mode 100644 index 0000000000..781d2cc76f --- /dev/null +++ b/pkg/actions/tools/mitmproxy/pattern.go @@ -0,0 +1,56 @@ +package mitmproxy + +import ( + "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bin/pkg/actions/net/http" +) + +// ActionModifyBodyPattern completes body modification patterns +// +// _~all_@file +// _~s_.*_@file +func ActionModifyBodyPattern() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if len(c.CallbackValue) > 1 { + return carapace.ActionMultiParts(c.CallbackValue[:1], func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 1: + return actionOptionFlowFilters() + case 2: + return actionOptionFiles() + + case 3: + return actionOptionFiles() + } + return carapace.ActionValues() + }) + } + return carapace.ActionValues() + }) +} + +// ActionModifyHeaderPattern completes header modification patterns +// +// _Accept-Language_av,af,am_@file +// _Accept-Encoding_zstd_@dist/ +func ActionModifyHeaderPattern() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if len(c.CallbackValue) > 1 { + return carapace.ActionMultiParts(c.CallbackValue[:1], func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 1: + return actionOptionFlowFilters() + case 2: + return http.ActionRequestHeaderNames() + case 3: + return carapace.Batch( + http.ActionRequestHeaderValues(c.Parts[2]), + actionOptionFiles(), + ).ToA() + } + return carapace.ActionValues() + }) + } + return carapace.ActionValues() + }) +}