aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/src/content/concepts-certificates.md2
-rw-r--r--docs/src/content/concepts-filters.md5
-rw-r--r--docs/src/content/concepts-protocols.md4
-rw-r--r--docs/src/content/tute-clientreplay.md5
-rw-r--r--examples/simple/wsgi_flask_app.py6
-rw-r--r--mitmproxy/command.py13
-rw-r--r--mitmproxy/optmanager.py3
-rw-r--r--mitmproxy/tools/_main.py12
-rw-r--r--mitmproxy/tools/console/commandexecutor.py5
-rw-r--r--mitmproxy/tools/console/common.py2
-rw-r--r--mitmproxy/tools/console/consoleaddons.py35
-rw-r--r--mitmproxy/tools/web/static/app.js2
-rw-r--r--mitmproxy/types.py6
-rw-r--r--test/mitmproxy/test_command.py9
-rw-r--r--web/src/js/__tests__/components/Header/__snapshots__/OptionMenuSpec.js.snap2
-rw-r--r--web/src/js/components/Header/OptionMenu.jsx2
16 files changed, 65 insertions, 48 deletions
diff --git a/docs/src/content/concepts-certificates.md b/docs/src/content/concepts-certificates.md
index 4e2ae47a..4e9aa652 100644
--- a/docs/src/content/concepts-certificates.md
+++ b/docs/src/content/concepts-certificates.md
@@ -48,7 +48,7 @@ documentation for some common platforms. The mitmproxy CA cert is located in
- [Windows (automated)](https://technet.microsoft.com/en-us/library/cc732443.aspx)
{{< highlight bash >}}
-certutil.exe -importpfx Root mitmproxy-ca-cert.p12
+certutil -addstore root mitmproxy-ca-cert.cer
{{< / highlight >}}
- [Mac OS X](https://support.apple.com/kb/PH20129)
diff --git a/docs/src/content/concepts-filters.md b/docs/src/content/concepts-filters.md
index f0b9a4b2..b9c89456 100644
--- a/docs/src/content/concepts-filters.md
+++ b/docs/src/content/concepts-filters.md
@@ -49,3 +49,8 @@ Requests whose body contains the string "test":
Anything but requests with a text/html content type:
!(~q & ~t "text/html")
+
+Replace entire GET string in a request (quotes required to make it work):
+
+ ":~q ~m GET:.*:/replacement.html"
+
diff --git a/docs/src/content/concepts-protocols.md b/docs/src/content/concepts-protocols.md
index c79274bf..dc9d84fb 100644
--- a/docs/src/content/concepts-protocols.md
+++ b/docs/src/content/concepts-protocols.md
@@ -73,6 +73,8 @@ If an endpoint sends a PING to mitmproxy, a PONG will be sent back immediately
PING (without a payload) is sent to the other endpoint. Unsolicited PONG's are
not forwarded. All PING's and PONG's are logged (with payload if present).
+Please note that message interception, modification or replay are not possible yet.
+
## Raw TCP / TCP Proxy / Fallback
In case mitmproxy does not handle a specific protocol, you can exempt
@@ -83,7 +85,7 @@ but differs in two important aspects:
* The raw TCP messages are printed to the event log.
* SSL connections will be intercepted.
-Please note that message interception or modification are not possible yet. If
+Please note that message interception, modification or replay are not possible yet. If
you are not interested in the raw TCP messages, you should use the ignore
domains feature.
diff --git a/docs/src/content/tute-clientreplay.md b/docs/src/content/tute-clientreplay.md
index 1bf69031..275edfe6 100644
--- a/docs/src/content/tute-clientreplay.md
+++ b/docs/src/content/tute-clientreplay.md
@@ -31,9 +31,8 @@ mitmdump -w wireless-login
## 2. Point your browser at the mitmdump instance.
-I use a tiny Firefox addon called [Toggle
-Proxy](https://addons.mozilla.org/en-us/firefox/addon/toggle-proxy-51740/) to
-switch quickly to and from mitmproxy. I'm assuming you've already [configured
+There is a Firefox addon called [FoxyProxy](https://addons.mozilla.org/fi/firefox/addon/foxyproxy-standard/) that
+lets you switch quickly to and from mitmproxy. I'm assuming you've already [configured
your browser with mitmproxy's SSL certificate authority]({{< relref
"concepts-certificates" >}}).
diff --git a/examples/simple/wsgi_flask_app.py b/examples/simple/wsgi_flask_app.py
index 4be38000..bbde6913 100644
--- a/examples/simple/wsgi_flask_app.py
+++ b/examples/simple/wsgi_flask_app.py
@@ -14,12 +14,12 @@ def hello_world() -> str:
return 'Hello World!'
-def load(l):
+addons = [
# Host app at the magic domain "proxapp.local" on port 80. Requests to this
# domain and port combination will now be routed to the WSGI app instance.
- return wsgiapp.WSGIApp(app, "proxapp.local", 80)
-
+ wsgiapp.WSGIApp(app, "proxapp.local", 80)
# SSL works too, but the magic domain needs to be resolvable from the mitmproxy machine due to mitmproxy's design.
# mitmproxy will connect to said domain and use serve its certificate (unless --no-upstream-cert is set)
# but won't send any data.
# mitmproxy.ctx.master.apps.add(app, "example.com", 443)
+]
diff --git a/mitmproxy/command.py b/mitmproxy/command.py
index 48f9051e..49cb959e 100644
--- a/mitmproxy/command.py
+++ b/mitmproxy/command.py
@@ -103,7 +103,7 @@ class Command:
except TypeError:
expected = f'Expected: {str(self.signature.parameters)}'
received = f'Received: {str(args)}'
- raise exceptions.CommandError(f"Command argument mismatch: \n\t{expected}\n\t{received}")
+ raise exceptions.CommandError(f"Command argument mismatch: \n {expected}\n {received}")
for name, value in bound_arguments.arguments.items():
convert_to = self.signature.parameters[name].annotation
@@ -149,9 +149,10 @@ class CommandManager:
if not i.startswith("__"):
o = getattr(addon, i)
try:
- is_command = hasattr(o, "command_name")
+ # hasattr is not enough, see https://github.com/mitmproxy/mitmproxy/issues/3794
+ is_command = isinstance(getattr(o, "command_name", None), str)
except Exception:
- pass # hasattr may raise if o implements __getattr__.
+ pass # getattr may raise if o implements __getattr__.
else:
if is_command:
try:
@@ -241,7 +242,7 @@ class CommandManager:
raise exceptions.CommandError("Unknown command: %s" % command_name)
return self.commands[command_name].func(*args)
- def _call_strings(self, command_name: str, args: typing.Sequence[str]) -> typing.Any:
+ def call_strings(self, command_name: str, args: typing.Sequence[str]) -> typing.Any:
"""
Call a command using a list of string arguments. May raise CommandError.
"""
@@ -262,7 +263,7 @@ class CommandManager:
for part in parts
if part.type != mitmproxy.types.Space
]
- return self._call_strings(command_name, args)
+ return self.call_strings(command_name, args)
def dump(self, out=sys.stdout) -> None:
cmds = list(self.commands.values())
@@ -284,7 +285,7 @@ def parsearg(manager: CommandManager, spec: str, argtype: type) -> typing.Any:
try:
return t.parse(manager, argtype, spec)
except exceptions.TypeError as e:
- raise exceptions.CommandError from e
+ raise exceptions.CommandError(str(e)) from e
def command(name: typing.Optional[str] = None):
diff --git a/mitmproxy/optmanager.py b/mitmproxy/optmanager.py
index f42aa645..ac85f83a 100644
--- a/mitmproxy/optmanager.py
+++ b/mitmproxy/optmanager.py
@@ -320,8 +320,7 @@ class OptManager:
update = {}
for optname, optval in self.deferred.items():
if optname in self._options:
- if isinstance(optval, str):
- optval = self.parse_setval(self._options[optname], optval)
+ optval = self.parse_setval(self._options[optname], optval)
update[optname] = optval
self.update(**update)
for k in update.keys():
diff --git a/mitmproxy/tools/_main.py b/mitmproxy/tools/_main.py
index b98bbe90..c1dd6179 100644
--- a/mitmproxy/tools/_main.py
+++ b/mitmproxy/tools/_main.py
@@ -113,12 +113,12 @@ def run(
opts.update(**extra(args))
loop = asyncio.get_event_loop()
- for signame in ('SIGINT', 'SIGTERM'):
- try:
- loop.add_signal_handler(getattr(signal, signame), getattr(master, "prompt_for_exit", master.shutdown))
- except NotImplementedError:
- # Not supported on Windows
- pass
+ try:
+ loop.add_signal_handler(signal.SIGINT, getattr(master, "prompt_for_exit", master.shutdown))
+ loop.add_signal_handler(signal.SIGTERM, master.shutdown)
+ except NotImplementedError:
+ # Not supported on Windows
+ pass
# Make sure that we catch KeyboardInterrupts on Windows.
# https://stackoverflow.com/a/36925722/934719
diff --git a/mitmproxy/tools/console/commandexecutor.py b/mitmproxy/tools/console/commandexecutor.py
index c738e349..1c6d5aa6 100644
--- a/mitmproxy/tools/console/commandexecutor.py
+++ b/mitmproxy/tools/console/commandexecutor.py
@@ -2,6 +2,7 @@ import typing
from mitmproxy import exceptions
from mitmproxy import flow
+from mitmproxy import ctx
from mitmproxy.tools.console import overlay
from mitmproxy.tools.console import signals
@@ -15,8 +16,8 @@ class CommandExecutor:
if cmd.strip():
try:
ret = self.master.commands.execute(cmd)
- except exceptions.CommandError as v:
- signals.status_message.send(message=str(v))
+ except exceptions.CommandError as e:
+ ctx.log.error(str(e))
else:
if ret:
if type(ret) == typing.Sequence[flow.Flow]:
diff --git a/mitmproxy/tools/console/common.py b/mitmproxy/tools/console/common.py
index 325c5740..3dce8363 100644
--- a/mitmproxy/tools/console/common.py
+++ b/mitmproxy/tools/console/common.py
@@ -543,7 +543,7 @@ def format_flow(f, focus, extended=False, hostheader=False, cols=False, layout='
duration = None
if f.response.timestamp_end and f.request.timestamp_start:
- duration = f.response.timestamp_end - f.request.timestamp_start
+ duration = max([f.response.timestamp_end - f.request.timestamp_start, 0])
d.update(dict(
resp_code=f.response.status_code,
diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py
index 7fcd9b48..905653e7 100644
--- a/mitmproxy/tools/console/consoleaddons.py
+++ b/mitmproxy/tools/console/consoleaddons.py
@@ -160,7 +160,7 @@ class ConsoleAddon:
fv = self.master.window.current("options")
if not fv:
raise exceptions.CommandError("Not viewing options.")
- self.master.commands.execute("options.reset.one %s" % fv.current_name())
+ self.master.commands.call_strings("options.reset.one", [fv.current_name()])
@command.command("console.nav.start")
def nav_start(self) -> None:
@@ -248,12 +248,11 @@ class ConsoleAddon:
def callback(opt):
# We're now outside of the call context...
- repl = cmd + " " + " ".join(args)
- repl = repl.replace("{choice}", opt)
+ repl = [arg.replace("{choice}", opt) for arg in args]
try:
- self.master.commands.execute(repl)
+ self.master.commands.call_strings(cmd, repl)
except exceptions.CommandError as e:
- signals.status_message.send(message=str(e))
+ ctx.log.error(str(e))
self.master.overlay(
overlay.Chooser(self.master, prompt, choices, "", callback)
@@ -276,12 +275,11 @@ class ConsoleAddon:
def callback(opt):
# We're now outside of the call context...
- repl = " ".join(command_lexer.quote(x) for x in args)
- repl = repl.replace("{choice}", opt)
+ repl = [arg.replace("{choice}", opt) for arg in args]
try:
- self.master.commands.execute(subcmd + " " + repl)
+ self.master.commands.call_strings(subcmd, repl)
except exceptions.CommandError as e:
- signals.status_message.send(message=str(e))
+ ctx.log.error(str(e))
self.master.overlay(
overlay.Chooser(self.master, prompt, choices, "", callback)
@@ -454,8 +452,9 @@ class ConsoleAddon:
url = edited_url.rstrip(b"\n")
flow.request.url = url.decode()
elif flow_part in ["method", "status_code", "reason"]:
- self.master.commands.execute(
- "console.command flow.set @focus %s " % flow_part
+ self.master.commands.call_strings(
+ "console.command",
+ ["flow.set", "@focus", flow_part]
)
def _grideditor(self):
@@ -539,10 +538,12 @@ class ConsoleAddon:
raise exceptions.CommandError("Invalid flowview mode.")
try:
- cmd = 'view.settings.setval @focus flowview_mode_%s %s' % (idx, mode)
- self.master.commands.execute(cmd)
+ self.master.commands.call_strings(
+ "view.settings.setval",
+ ["@focus", "flowview_mode_%s" % (idx,), mode]
+ )
except exceptions.CommandError as e:
- signals.status_message.send(message=str(e))
+ ctx.log.error(str(e))
@command.command("console.flowview.mode.options")
def flowview_mode_options(self) -> typing.Sequence[str]:
@@ -561,8 +562,10 @@ class ConsoleAddon:
raise exceptions.CommandError("Not viewing a flow.")
idx = fv.body.tab_offset
- cmd = 'view.settings.getval @focus flowview_mode_%s %s' % (idx, self.master.options.console_default_contentview)
- return self.master.commands.execute(cmd)
+ return self.master.commands.call_strings(
+ "view.settings.getval",
+ ["@focus", "flowview_mode_%s" % (idx,), self.master.options.console_default_contentview]
+ )
@command.command("console.key.contexts")
def key_contexts(self) -> typing.Sequence[str]:
diff --git a/mitmproxy/tools/web/static/app.js b/mitmproxy/tools/web/static/app.js
index e429a344..c5301c07 100644
--- a/mitmproxy/tools/web/static/app.js
+++ b/mitmproxy/tools/web/static/app.js
@@ -101,7 +101,7 @@
"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function MenuToggle(e){var t=e.value,n=e.onChange,r=e.children;return React.createElement("div",{className:"menu-entry"},React.createElement("label",null,React.createElement("input",{type:"checkbox",checked:t,onChange:n}),r))}function SettingsToggle(e){var t=e.setting,n=e.children,r=e.settings,g=e.updateSettings;return React.createElement(MenuToggle,{value:r[t]||!1,onChange:function(){return g(_defineProperty({},t,!r[t]))}},n)}function EventlogToggle(e){var t=e.toggleVisibility,n=e.eventLogVisible;return React.createElement(MenuToggle,{value:n,onChange:t},"Display Event Log")}Object.defineProperty(exports,"__esModule",{value:!0}),exports.MenuToggle=MenuToggle,exports.SettingsToggle=SettingsToggle,exports.EventlogToggle=EventlogToggle;var _propTypes=require("prop-types"),_propTypes2=_interopRequireDefault(_propTypes),_reactRedux=require("react-redux"),_settings=require("../../ducks/settings"),_eventLog=require("../../ducks/eventLog");MenuToggle.propTypes={value:_propTypes2.default.bool.isRequired,onChange:_propTypes2.default.func.isRequired,children:_propTypes2.default.node.isRequired},SettingsToggle.propTypes={setting:_propTypes2.default.string.isRequired,children:_propTypes2.default.node.isRequired},exports.SettingsToggle=SettingsToggle=(0,_reactRedux.connect)(function(e){return{settings:e.settings}},{updateSettings:_settings.update})(SettingsToggle),exports.EventlogToggle=EventlogToggle=(0,_reactRedux.connect)(function(e){return{eventLogVisible:e.eventLog.visible}},{toggleVisibility:_eventLog.toggleVisibility})(EventlogToggle);
},{"../../ducks/eventLog":55,"../../ducks/settings":59,"prop-types":"prop-types","react-redux":"react-redux"}],35:[function(require,module,exports){
-"use strict";function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&(t[a]=e[a]);return t.default=e,t}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function OptionMenu(e){var t=e.openOptions;return _react2.default.createElement("div",null,_react2.default.createElement(_HideInStatic2.default,null,_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_Button2.default,{title:"Open Options",icon:"fa-cogs text-primary",onClick:t},"Edit Options ",_react2.default.createElement("sup",null,"alpha"))),_react2.default.createElement("div",{className:"menu-legend"},"Options Editor")),_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"anticache"},"Strip cache headers ",_react2.default.createElement(_DocsLink2.default,{resource:"features/anticache.html"})),_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"showhost"},"Use host header for display"),_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"ssl_insecure"},"Verify server certificates")),_react2.default.createElement("div",{className:"menu-legend"},"Quick Options"))),_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_MenuToggle.EventlogToggle,null)),_react2.default.createElement("div",{className:"menu-legend"},"View Options")))}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_reactRedux=require("react-redux"),_MenuToggle=require("./MenuToggle"),_Button=require("../common/Button"),_Button2=_interopRequireDefault(_Button),_DocsLink=require("../common/DocsLink"),_DocsLink2=_interopRequireDefault(_DocsLink),_HideInStatic=require("../common/HideInStatic"),_HideInStatic2=_interopRequireDefault(_HideInStatic),_modal=require("../../ducks/ui/modal"),modalActions=_interopRequireWildcard(_modal);OptionMenu.title="Options",exports.default=(0,_reactRedux.connect)(null,{openOptions:function(){return modalActions.setActiveModal("OptionModal")}})(OptionMenu);
+"use strict";function _interopRequireWildcard(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)Object.prototype.hasOwnProperty.call(e,a)&&(t[a]=e[a]);return t.default=e,t}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function OptionMenu(e){var t=e.openOptions;return _react2.default.createElement("div",null,_react2.default.createElement(_HideInStatic2.default,null,_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_Button2.default,{title:"Open Options",icon:"fa-cogs text-primary",onClick:t},"Edit Options ",_react2.default.createElement("sup",null,"alpha"))),_react2.default.createElement("div",{className:"menu-legend"},"Options Editor")),_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"anticache"},"Strip cache headers ",_react2.default.createElement(_DocsLink2.default,{resource:"features/anticache.html"})),_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"showhost"},"Use host header for display"),_react2.default.createElement(_MenuToggle.SettingsToggle,{setting:"ssl_insecure"},"Don't verify server certificates")),_react2.default.createElement("div",{className:"menu-legend"},"Quick Options"))),_react2.default.createElement("div",{className:"menu-group"},_react2.default.createElement("div",{className:"menu-content"},_react2.default.createElement(_MenuToggle.EventlogToggle,null)),_react2.default.createElement("div",{className:"menu-legend"},"View Options")))}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_reactRedux=require("react-redux"),_MenuToggle=require("./MenuToggle"),_Button=require("../common/Button"),_Button2=_interopRequireDefault(_Button),_DocsLink=require("../common/DocsLink"),_DocsLink2=_interopRequireDefault(_DocsLink),_HideInStatic=require("../common/HideInStatic"),_HideInStatic2=_interopRequireDefault(_HideInStatic),_modal=require("../../ducks/ui/modal"),modalActions=_interopRequireWildcard(_modal);OptionMenu.title="Options",exports.default=(0,_reactRedux.connect)(null,{openOptions:function(){return modalActions.setActiveModal("OptionModal")}})(OptionMenu);
},{"../../ducks/ui/modal":64,"../common/Button":45,"../common/DocsLink":46,"../common/HideInStatic":49,"./MenuToggle":34,"react":"react","react-redux":"react-redux"}],36:[function(require,module,exports){
"use strict";function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function MainView(e){var t=e.hasSelection;return _react2.default.createElement("div",{className:"main-view"},_react2.default.createElement(_FlowTable2.default,null),t&&_react2.default.createElement(_Splitter2.default,{key:"splitter"}),t&&_react2.default.createElement(_FlowView2.default,{key:"flowDetails"}))}Object.defineProperty(exports,"__esModule",{value:!0});var _react=require("react"),_react2=_interopRequireDefault(_react),_propTypes=require("prop-types"),_propTypes2=_interopRequireDefault(_propTypes),_reactRedux=require("react-redux"),_Splitter=require("./common/Splitter"),_Splitter2=_interopRequireDefault(_Splitter),_FlowTable=require("./FlowTable"),_FlowTable2=_interopRequireDefault(_FlowTable),_FlowView=require("./FlowView"),_FlowView2=_interopRequireDefault(_FlowView);MainView.propTypes={hasSelection:_propTypes2.default.bool.isRequired},exports.default=(0,_reactRedux.connect)(function(e){return{hasSelection:!!e.flows.byId[e.flows.selected[0]]}},{})(MainView);
diff --git a/mitmproxy/types.py b/mitmproxy/types.py
index ac992217..0d4ffd69 100644
--- a/mitmproxy/types.py
+++ b/mitmproxy/types.py
@@ -134,7 +134,7 @@ class _IntType(_BaseType):
try:
return int(s)
except ValueError as e:
- raise exceptions.TypeError from e
+ raise exceptions.TypeError(str(e)) from e
def is_valid(self, manager: "CommandManager", typ: typing.Any, val: typing.Any) -> bool:
return isinstance(val, int)
@@ -328,7 +328,7 @@ class _FlowType(_BaseFlowType):
try:
flows = manager.execute("view.flows.resolve %s" % (s))
except exceptions.CommandError as e:
- raise exceptions.TypeError from e
+ raise exceptions.TypeError(str(e)) from e
if len(flows) != 1:
raise exceptions.TypeError(
"Command requires one flow, specification matched %s." % len(flows)
@@ -347,7 +347,7 @@ class _FlowsType(_BaseFlowType):
try:
return manager.execute("view.flows.resolve %s" % (s))
except exceptions.CommandError as e:
- raise exceptions.TypeError from e
+ raise exceptions.TypeError(str(e)) from e
def is_valid(self, manager: "CommandManager", typ: typing.Any, val: typing.Any) -> bool:
try:
diff --git a/test/mitmproxy/test_command.py b/test/mitmproxy/test_command.py
index a432f9e3..7158935a 100644
--- a/test/mitmproxy/test_command.py
+++ b/test/mitmproxy/test_command.py
@@ -512,9 +512,15 @@ class TAttr:
raise IOError
+class TAttr2:
+ def __getattr__(self, item):
+ return TAttr2()
+
+
class TCmds(TAttr):
def __init__(self):
self.TAttr = TAttr()
+ self.TAttr2 = TAttr2()
@command.command("empty")
def empty(self) -> None:
@@ -524,7 +530,8 @@ class TCmds(TAttr):
@pytest.mark.asyncio
async def test_collect_commands():
"""
- This tests for the error thrown by hasattr()
+ This tests for errors thrown by getattr() or __getattr__ implementations
+ that return an object for .command_name.
"""
with taddons.context() as tctx:
c = command.CommandManager(tctx.master)
diff --git a/web/src/js/__tests__/components/Header/__snapshots__/OptionMenuSpec.js.snap b/web/src/js/__tests__/components/Header/__snapshots__/OptionMenuSpec.js.snap
index 83d9355c..106f1732 100644
--- a/web/src/js/__tests__/components/Header/__snapshots__/OptionMenuSpec.js.snap
+++ b/web/src/js/__tests__/components/Header/__snapshots__/OptionMenuSpec.js.snap
@@ -76,7 +76,7 @@ exports[`OptionMenu Component should render correctly 1`] = `
onChange={[Function]}
type="checkbox"
/>
- Verify server certificates
+ Don't verify server certificates
</label>
</div>
</div>
diff --git a/web/src/js/components/Header/OptionMenu.jsx b/web/src/js/components/Header/OptionMenu.jsx
index 765129ed..4eb319ec 100644
--- a/web/src/js/components/Header/OptionMenu.jsx
+++ b/web/src/js/components/Header/OptionMenu.jsx
@@ -31,7 +31,7 @@ function OptionMenu({ openOptions }) {
Use host header for display
</SettingsToggle>
<SettingsToggle setting="ssl_insecure">
- Verify server certificates
+ Don't verify server certificates
</SettingsToggle>
</div>
<div className="menu-legend">Quick Options</div>