mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-22 07:08:10 +00:00
commit
aad92c9d5a
@ -10,6 +10,7 @@ class Filter:
|
||||
self.filter: flowfilter.TFilter = None
|
||||
|
||||
def configure(self, updated):
|
||||
if "flowfilter" in updated:
|
||||
self.filter = flowfilter.parse(ctx.options.flowfilter)
|
||||
|
||||
def load(self, l):
|
||||
|
@ -28,8 +28,6 @@ def parse_spec(option: str) -> BlockSpec:
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid HTTP status code: {status}")
|
||||
flow_filter = flowfilter.parse(flow_patt)
|
||||
if not flow_filter:
|
||||
raise ValueError(f"Invalid filter pattern: {flow_patt}")
|
||||
if not RESPONSES.get(status_code):
|
||||
raise ValueError(f"Invalid HTTP status code: {status}")
|
||||
|
||||
|
@ -59,11 +59,10 @@ class Dumper:
|
||||
def configure(self, updated):
|
||||
if "dumper_filter" in updated:
|
||||
if ctx.options.dumper_filter:
|
||||
try:
|
||||
self.filter = flowfilter.parse(ctx.options.dumper_filter)
|
||||
if not self.filter:
|
||||
raise exceptions.OptionsError(
|
||||
"Invalid filter expression: %s" % ctx.options.dumper_filter
|
||||
)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
else:
|
||||
self.filter = None
|
||||
|
||||
|
@ -21,9 +21,10 @@ class Intercept:
|
||||
def configure(self, updated):
|
||||
if "intercept" in updated:
|
||||
if ctx.options.intercept:
|
||||
try:
|
||||
self.filt = flowfilter.parse(ctx.options.intercept)
|
||||
if not self.filt:
|
||||
raise exceptions.OptionsError(f"Invalid interception filter: {ctx.options.intercept}")
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
ctx.options.intercept_active = True
|
||||
else:
|
||||
self.filt = None
|
||||
|
@ -30,14 +30,13 @@ class ReadFile:
|
||||
|
||||
def configure(self, updated):
|
||||
if "readfile_filter" in updated:
|
||||
filt = None
|
||||
if ctx.options.readfile_filter:
|
||||
filt = flowfilter.parse(ctx.options.readfile_filter)
|
||||
if not filt:
|
||||
raise exceptions.OptionsError(
|
||||
"Invalid readfile filter: %s" % ctx.options.readfile_filter
|
||||
)
|
||||
self.filter = filt
|
||||
try:
|
||||
self.filter = flowfilter.parse(ctx.options.readfile_filter)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
else:
|
||||
self.filter = None
|
||||
|
||||
async def load_flows(self, fo: typing.IO[bytes]) -> int:
|
||||
cnt = 0
|
||||
|
@ -48,11 +48,10 @@ class Save:
|
||||
# We're already streaming - stop the previous stream and restart
|
||||
if "save_stream_filter" in updated:
|
||||
if ctx.options.save_stream_filter:
|
||||
try:
|
||||
self.filt = flowfilter.parse(ctx.options.save_stream_filter)
|
||||
if not self.filt:
|
||||
raise exceptions.OptionsError(
|
||||
"Invalid filter specification: %s" % ctx.options.save_stream_filter
|
||||
)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
else:
|
||||
self.filt = None
|
||||
if "save_stream_file" in updated or "save_stream_filter" in updated:
|
||||
|
@ -19,12 +19,10 @@ class StickyAuth:
|
||||
def configure(self, updated):
|
||||
if "stickyauth" in updated:
|
||||
if ctx.options.stickyauth:
|
||||
flt = flowfilter.parse(ctx.options.stickyauth)
|
||||
if not flt:
|
||||
raise exceptions.OptionsError(
|
||||
"stickyauth: invalid filter expression: %s" % ctx.options.stickyauth
|
||||
)
|
||||
self.flt = flt
|
||||
try:
|
||||
self.flt = flowfilter.parse(ctx.options.stickyauth)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
else:
|
||||
self.flt = None
|
||||
|
||||
|
@ -43,12 +43,10 @@ class StickyCookie:
|
||||
def configure(self, updated):
|
||||
if "stickycookie" in updated:
|
||||
if ctx.options.stickycookie:
|
||||
flt = flowfilter.parse(ctx.options.stickycookie)
|
||||
if not flt:
|
||||
raise exceptions.OptionsError(
|
||||
"stickycookie: invalid filter expression: %s" % ctx.options.stickycookie
|
||||
)
|
||||
self.flt = flt
|
||||
try:
|
||||
self.flt = flowfilter.parse(ctx.options.stickycookie)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
else:
|
||||
self.flt = None
|
||||
|
||||
|
@ -115,8 +115,6 @@ class OrderKeySize(_OrderKey):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
matchall = flowfilter.parse("~http | ~tcp")
|
||||
|
||||
orders = [
|
||||
("t", "time"),
|
||||
("m", "method"),
|
||||
@ -129,7 +127,7 @@ class View(collections.abc.Sequence):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._store = collections.OrderedDict()
|
||||
self.filter = matchall
|
||||
self.filter = flowfilter.match_all
|
||||
# Should we show only marked flows?
|
||||
self.show_marked = False
|
||||
|
||||
@ -326,15 +324,14 @@ class View(collections.abc.Sequence):
|
||||
"""
|
||||
filt = None
|
||||
if filter_expr:
|
||||
try:
|
||||
filt = flowfilter.parse(filter_expr)
|
||||
if not filt:
|
||||
raise exceptions.CommandError(
|
||||
"Invalid interception filter: %s" % filter_expr
|
||||
)
|
||||
except ValueError as e:
|
||||
raise exceptions.CommandError(str(e)) from e
|
||||
self.set_filter(filt)
|
||||
|
||||
def set_filter(self, flt: typing.Optional[flowfilter.TFilter]):
|
||||
self.filter = flt or matchall
|
||||
self.filter = flt or flowfilter.match_all
|
||||
self._refilter()
|
||||
|
||||
# View Updates
|
||||
@ -454,9 +451,10 @@ class View(collections.abc.Sequence):
|
||||
ids = flow_spec[1:].split(",")
|
||||
return [i for i in self._store.values() if i.id in ids]
|
||||
else:
|
||||
try:
|
||||
filt = flowfilter.parse(flow_spec)
|
||||
if not filt:
|
||||
raise exceptions.CommandError("Invalid flow filter: %s" % flow_spec)
|
||||
except ValueError as e:
|
||||
raise exceptions.CommandError(str(e)) from e
|
||||
return [i for i in self._store.values() if filt(i)]
|
||||
|
||||
@command.command("view.flows.create")
|
||||
@ -547,11 +545,10 @@ class View(collections.abc.Sequence):
|
||||
if "view_filter" in updated:
|
||||
filt = None
|
||||
if ctx.options.view_filter:
|
||||
try:
|
||||
filt = flowfilter.parse(ctx.options.view_filter)
|
||||
if not filt:
|
||||
raise exceptions.OptionsError(
|
||||
"Invalid interception filter: %s" % ctx.options.view_filter
|
||||
)
|
||||
except ValueError as e:
|
||||
raise exceptions.OptionsError(str(e)) from e
|
||||
self.set_filter(filt)
|
||||
if "view_order" in updated:
|
||||
if ctx.options.view_order not in self.orders:
|
||||
|
@ -35,7 +35,7 @@
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
from typing import Callable, ClassVar, Optional, Sequence, Type
|
||||
from typing import ClassVar, Sequence, Type, Protocol, Union
|
||||
import pyparsing as pp
|
||||
|
||||
from mitmproxy import flow, http, tcp
|
||||
@ -135,6 +135,14 @@ class FResp(_Action):
|
||||
return bool(f.response)
|
||||
|
||||
|
||||
class FAll(_Action):
|
||||
code = "all"
|
||||
help = "Match all flows"
|
||||
|
||||
def __call__(self, f: flow.Flow):
|
||||
return True
|
||||
|
||||
|
||||
class _Rex(_Action):
|
||||
flags = 0
|
||||
is_binary = True
|
||||
@ -504,6 +512,7 @@ filter_unary: Sequence[Type[_Action]] = [
|
||||
FResp,
|
||||
FTCP,
|
||||
FWebSocket,
|
||||
FAll,
|
||||
]
|
||||
filter_rex: Sequence[Type[_Rex]] = [
|
||||
FBod,
|
||||
@ -583,21 +592,31 @@ def _make():
|
||||
|
||||
|
||||
bnf = _make()
|
||||
TFilter = Callable[[flow.Flow], bool]
|
||||
|
||||
|
||||
def parse(s: str) -> Optional[TFilter]:
|
||||
class TFilter(Protocol):
|
||||
pattern: str
|
||||
|
||||
def __call__(self, f: flow.Flow) -> bool:
|
||||
... # pragma: no cover
|
||||
|
||||
|
||||
def parse(s: str) -> TFilter:
|
||||
"""
|
||||
Parse a filter expression and return the compiled filter function.
|
||||
If the filter syntax is invalid, `ValueError` is raised.
|
||||
"""
|
||||
if not s:
|
||||
raise ValueError("Empty filter expression")
|
||||
try:
|
||||
flt = bnf.parseString(s, parseAll=True)[0]
|
||||
flt.pattern = s
|
||||
return flt
|
||||
except pp.ParseException:
|
||||
return None
|
||||
except ValueError:
|
||||
return None
|
||||
except (pp.ParseException, ValueError) as e:
|
||||
raise ValueError(f"Invalid filter expression: {s!r}") from e
|
||||
|
||||
|
||||
def match(flt, flow):
|
||||
def match(flt: Union[str, TFilter], flow: flow.Flow) -> bool:
|
||||
"""
|
||||
Matches a flow against a compiled filter expression.
|
||||
Returns True if matched, False if not.
|
||||
@ -607,13 +626,15 @@ def match(flt, flow):
|
||||
"""
|
||||
if isinstance(flt, str):
|
||||
flt = parse(flt)
|
||||
if not flt:
|
||||
raise ValueError("Invalid filter expression.")
|
||||
if flt:
|
||||
return flt(flow)
|
||||
return True
|
||||
|
||||
|
||||
match_all: TFilter = parse("~all")
|
||||
"""A filter function that matches all flows"""
|
||||
|
||||
|
||||
help = []
|
||||
for a in filter_unary:
|
||||
help.append(
|
||||
|
@ -58,6 +58,7 @@ def flow_to_json(flow: mitmproxy.flow.Flow) -> dict:
|
||||
"type": flow.type,
|
||||
"modified": flow.modified(),
|
||||
"marked": emoji.get(flow.marked, "🔴") if flow.marked else "",
|
||||
"comment": flow.comment,
|
||||
}
|
||||
|
||||
if flow.client_conn:
|
||||
|
@ -2,10 +2,6 @@ import typing
|
||||
from mitmproxy import flowfilter
|
||||
|
||||
|
||||
def _match_all(flow) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def parse_spec(option: str) -> typing.Tuple[flowfilter.TFilter, str, str]:
|
||||
"""
|
||||
Parse strings in the following format:
|
||||
@ -17,12 +13,10 @@ def parse_spec(option: str) -> typing.Tuple[flowfilter.TFilter, str, str]:
|
||||
parts = rem.split(sep, 2)
|
||||
if len(parts) == 2:
|
||||
subject, replacement = parts
|
||||
return _match_all, subject, replacement
|
||||
return flowfilter.match_all, subject, replacement
|
||||
elif len(parts) == 3:
|
||||
patt, subject, replacement = parts
|
||||
flow_filter = flowfilter.parse(patt)
|
||||
if not flow_filter:
|
||||
raise ValueError(f"Invalid filter pattern: {patt}")
|
||||
return flow_filter, subject, replacement
|
||||
else:
|
||||
raise ValueError("Invalid number of parameters (2 or 3 are expected)")
|
||||
|
@ -43,8 +43,9 @@ class TestReadFile:
|
||||
rf = readfile.ReadFile()
|
||||
with taddons.context(rf) as tctx:
|
||||
tctx.configure(rf, readfile_filter="~q")
|
||||
with pytest.raises(Exception, match="Invalid readfile filter"):
|
||||
with pytest.raises(Exception, match="Invalid filter expression"):
|
||||
tctx.configure(rf, readfile_filter="~~")
|
||||
tctx.configure(rf, readfile_filter="")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read(self, tmpdir, data, corrupt_data):
|
||||
|
@ -16,7 +16,7 @@ class TestStickyCookie:
|
||||
def test_config(self):
|
||||
sc = stickycookie.StickyCookie()
|
||||
with taddons.context(sc) as tctx:
|
||||
with pytest.raises(Exception, match="invalid filter"):
|
||||
with pytest.raises(Exception, match="Invalid filter expression"):
|
||||
tctx.configure(sc, stickycookie="~b")
|
||||
|
||||
tctx.configure(sc, stickycookie="foo")
|
||||
|
@ -268,7 +268,7 @@ def test_resolve():
|
||||
assert m(tctx.command(v.resolve, "@unmarked")) == ["PUT", "GET", "PUT"]
|
||||
assert m(tctx.command(v.resolve, "@all")) == ["GET", "PUT", "GET", "PUT"]
|
||||
|
||||
with pytest.raises(exceptions.CommandError, match="Invalid flow filter"):
|
||||
with pytest.raises(exceptions.CommandError, match="Invalid filter expression"):
|
||||
tctx.command(v.resolve, "~")
|
||||
|
||||
|
||||
@ -608,7 +608,7 @@ def test_configure():
|
||||
v = view.View()
|
||||
with taddons.context(v) as tctx:
|
||||
tctx.configure(v, view_filter="~q")
|
||||
with pytest.raises(Exception, match="Invalid interception filter"):
|
||||
with pytest.raises(Exception, match="Invalid filter expression"):
|
||||
tctx.configure(v, view_filter="~~")
|
||||
|
||||
tctx.configure(v, view_order="method")
|
||||
|
@ -13,10 +13,14 @@ class TestParsing:
|
||||
assert c.getvalue()
|
||||
|
||||
def test_parse_err(self):
|
||||
assert flowfilter.parse("~h [") is None
|
||||
with pytest.raises(ValueError, match="Empty filter"):
|
||||
flowfilter.parse("")
|
||||
with pytest.raises(ValueError, match="Invalid filter"):
|
||||
flowfilter.parse("~b")
|
||||
with pytest.raises(ValueError, match="Invalid filter"):
|
||||
flowfilter.parse("~h [")
|
||||
|
||||
def test_simple(self):
|
||||
assert not flowfilter.parse("~b")
|
||||
assert flowfilter.parse("~q")
|
||||
assert flowfilter.parse("~c 10")
|
||||
assert flowfilter.parse("~m foobar")
|
||||
@ -568,6 +572,8 @@ class TestMatchingDummyFlow:
|
||||
f = self.flow()
|
||||
f.server_conn = tflow.tserver_conn()
|
||||
|
||||
assert self.q("~all", f)
|
||||
|
||||
assert not self.q("~a", f)
|
||||
|
||||
assert not self.q("~b whatever", f)
|
||||
|
@ -39,15 +39,18 @@ def console(monkeypatch):
|
||||
return m
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
def test_integration(tdata, console):
|
||||
console.type(f":view.flows.load {tdata.path('mitmproxy/data/dumpfile-7.mitm')}<enter>")
|
||||
console.type("<enter><tab><tab>")
|
||||
console.type("<space><tab><tab>") # view second flow
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
def test_options_home_end(console):
|
||||
console.type("O<home><end>")
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
def test_keybindings_home_end(console):
|
||||
console.type("K<home><end>")
|
@ -48,6 +48,7 @@ def test_generate_tflow_js(tdata):
|
||||
]
|
||||
tf_http.request.trailers = Headers(trailer="qvalue")
|
||||
tf_http.response.trailers = Headers(trailer="qvalue")
|
||||
tf_http.comment = "I'm a comment!"
|
||||
|
||||
tf_tcp = tflow.ttcpflow(err=True)
|
||||
tf_tcp.id = "2ea7012b-21b5-4f8f-98cd-d49819954001"
|
||||
|
@ -16,5 +16,5 @@ def test_parse_spec():
|
||||
with pytest.raises(ValueError, match="Invalid number of parameters"):
|
||||
parse_spec("/")
|
||||
|
||||
with pytest.raises(ValueError, match="Invalid filter pattern"):
|
||||
with pytest.raises(ValueError, match="Invalid filter expression"):
|
||||
parse_spec("/~b/one/two")
|
||||
|
@ -64,6 +64,9 @@
|
||||
&.selected {
|
||||
background-color: #e0ebf5 !important;
|
||||
}
|
||||
&.selected.highlighted {
|
||||
background-color: #7bbefc !important;
|
||||
}
|
||||
|
||||
&.highlighted {
|
||||
background-color: #ffeb99;
|
||||
|
@ -22,6 +22,7 @@ export function THTTPFlow(): Required<HTTPFlow> {
|
||||
"tls_established": true,
|
||||
"tls_version": "TLSv1.2"
|
||||
},
|
||||
"comment": "I'm a comment!",
|
||||
"error": {
|
||||
"msg": "error",
|
||||
"timestamp": 946681207.0
|
||||
@ -180,6 +181,7 @@ export function TTCPFlow(): Required<TCPFlow> {
|
||||
"tls_established": true,
|
||||
"tls_version": "TLSv1.2"
|
||||
},
|
||||
"comment": "",
|
||||
"error": {
|
||||
"msg": "error",
|
||||
"timestamp": 946681207.0
|
||||
|
@ -83,7 +83,7 @@ describe('updateUrlFromStore', () => {
|
||||
},
|
||||
store = mockStore(state)
|
||||
updateUrlFromStore(store)
|
||||
expect(history.replaceState).toBeCalledWith(undefined, '', '/#/flows/123/request?s=~u foo')
|
||||
expect(history.replaceState).toBeCalledWith(undefined, '', '/#/flows/123/request?s=~u%20foo')
|
||||
})
|
||||
})
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,4 @@
|
||||
// PEG.js filter rules - see https://pegjs.org/
|
||||
|
||||
{
|
||||
function or(first, second) {
|
||||
// Add explicit function names to ease debugging.
|
||||
@ -9,6 +8,7 @@ function or(first, second) {
|
||||
orFilter.desc = first.desc + " or " + second.desc;
|
||||
return orFilter;
|
||||
}
|
||||
|
||||
function and(first, second) {
|
||||
function andFilter() {
|
||||
return first.apply(this, arguments) && second.apply(this, arguments);
|
||||
@ -16,6 +16,7 @@ function and(first, second) {
|
||||
andFilter.desc = first.desc + " and " + second.desc;
|
||||
return andFilter;
|
||||
}
|
||||
|
||||
function not(expr) {
|
||||
function notFilter() {
|
||||
return !expr.apply(this, arguments);
|
||||
@ -23,6 +24,7 @@ function not(expr) {
|
||||
notFilter.desc = "not " + expr.desc;
|
||||
return notFilter;
|
||||
}
|
||||
|
||||
function binding(expr) {
|
||||
function bindingFilter() {
|
||||
return expr.apply(this, arguments);
|
||||
@ -30,22 +32,20 @@ function binding(expr) {
|
||||
bindingFilter.desc = "(" + expr.desc + ")";
|
||||
return bindingFilter;
|
||||
}
|
||||
function trueFilter(flow) {
|
||||
|
||||
// ~all
|
||||
function allFilter(flow) {
|
||||
return true;
|
||||
}
|
||||
trueFilter.desc = "true";
|
||||
function falseFilter(flow) {
|
||||
return false;
|
||||
}
|
||||
falseFilter.desc = "false";
|
||||
allFilter.desc = "all flows";
|
||||
|
||||
// ~a
|
||||
var ASSET_TYPES = [
|
||||
new RegExp("text/javascript"),
|
||||
new RegExp("application/x-javascript"),
|
||||
new RegExp("application/javascript"),
|
||||
new RegExp("text/css"),
|
||||
new RegExp("image/.*"),
|
||||
new RegExp("application/x-shockwave-flash")
|
||||
new RegExp("image/.*")
|
||||
];
|
||||
function assetFilter(flow) {
|
||||
if (flow.response) {
|
||||
@ -60,13 +60,8 @@ function assetFilter(flow) {
|
||||
return false;
|
||||
}
|
||||
assetFilter.desc = "is asset";
|
||||
function responseCode(code){
|
||||
function responseCodeFilter(flow){
|
||||
return flow.response && flow.response.status_code === code;
|
||||
}
|
||||
responseCodeFilter.desc = "resp. code is " + code;
|
||||
return responseCodeFilter;
|
||||
}
|
||||
|
||||
// ~b
|
||||
function body(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function bodyFilter(flow){
|
||||
@ -75,6 +70,8 @@ function body(regex){
|
||||
bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
|
||||
return bodyFilter;
|
||||
}
|
||||
|
||||
// ~bq
|
||||
function requestBody(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function requestBodyFilter(flow){
|
||||
@ -83,6 +80,8 @@ function requestBody(regex){
|
||||
requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
|
||||
return requestBodyFilter;
|
||||
}
|
||||
|
||||
// ~bs
|
||||
function responseBody(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function responseBodyFilter(flow){
|
||||
@ -91,6 +90,27 @@ function responseBody(regex){
|
||||
responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmweb/issues/10";
|
||||
return responseBodyFilter;
|
||||
}
|
||||
|
||||
// ~c
|
||||
function responseCode(code){
|
||||
function responseCodeFilter(flow){
|
||||
return flow.response && flow.response.status_code === code;
|
||||
}
|
||||
responseCodeFilter.desc = "resp. code is " + code;
|
||||
return responseCodeFilter;
|
||||
}
|
||||
|
||||
// ~comment
|
||||
function comment(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function commentFilter(flow){
|
||||
return regex.test(flow.comment)
|
||||
}
|
||||
commentFilter.desc = "comment matches " + regex;
|
||||
return commentFilter;
|
||||
}
|
||||
|
||||
// ~d
|
||||
function domain(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function domainFilter(flow){
|
||||
@ -99,6 +119,8 @@ function domain(regex){
|
||||
domainFilter.desc = "domain matches " + regex;
|
||||
return domainFilter;
|
||||
}
|
||||
|
||||
// ~dst
|
||||
function destination(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function destinationFilter(flow){
|
||||
@ -109,10 +131,14 @@ function destination(regex){
|
||||
destinationFilter.desc = "destination address matches " + regex;
|
||||
return destinationFilter;
|
||||
}
|
||||
|
||||
// ~e
|
||||
function errorFilter(flow){
|
||||
return !!flow.error;
|
||||
}
|
||||
errorFilter.desc = "has error";
|
||||
|
||||
// ~h
|
||||
function header(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function headerFilter(flow){
|
||||
@ -125,6 +151,8 @@ function header(regex){
|
||||
headerFilter.desc = "header matches " + regex;
|
||||
return headerFilter;
|
||||
}
|
||||
|
||||
// ~hq
|
||||
function requestHeader(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function requestHeaderFilter(flow){
|
||||
@ -133,6 +161,8 @@ function requestHeader(regex){
|
||||
requestHeaderFilter.desc = "req. header matches " + regex;
|
||||
return requestHeaderFilter;
|
||||
}
|
||||
|
||||
// ~hs
|
||||
function responseHeader(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function responseHeaderFilter(flow){
|
||||
@ -141,10 +171,30 @@ function responseHeader(regex){
|
||||
responseHeaderFilter.desc = "resp. header matches " + regex;
|
||||
return responseHeaderFilter;
|
||||
}
|
||||
|
||||
// ~http
|
||||
function httpFilter(flow){
|
||||
return flow.type === "http";
|
||||
}
|
||||
httpFilter.desc = "is an HTTP Flow";
|
||||
|
||||
// ~marked
|
||||
function markedFilter(flow){
|
||||
return flow.marked;
|
||||
}
|
||||
markedFilter.desc = "is marked";
|
||||
|
||||
// ~marker
|
||||
function marker(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function markerFilter(flow){
|
||||
return regex.test(flow.marked)
|
||||
}
|
||||
markerFilter.desc = "marker matches " + regex;
|
||||
return markerFilter;
|
||||
}
|
||||
|
||||
// ~m
|
||||
function method(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function methodFilter(flow){
|
||||
@ -153,18 +203,32 @@ function method(regex){
|
||||
methodFilter.desc = "method matches " + regex;
|
||||
return methodFilter;
|
||||
}
|
||||
function markedFilter(flow){
|
||||
return flow.marked;
|
||||
}
|
||||
markedFilter.desc = "is marked";
|
||||
|
||||
// ~q
|
||||
function noResponseFilter(flow){
|
||||
return flow.request && !flow.response;
|
||||
}
|
||||
noResponseFilter.desc = "has no response";
|
||||
function responseFilter(flow){
|
||||
return !!flow.response;
|
||||
|
||||
// ~replayq
|
||||
function clientReplayFilter(flow){
|
||||
return flow.is_replay === "request";
|
||||
}
|
||||
responseFilter.desc = "has response";
|
||||
clientReplayFilter.desc = "request has been replayed";
|
||||
|
||||
// ~replays
|
||||
function serverReplayFilter(flow){
|
||||
return flow.is_replay === "response";
|
||||
}
|
||||
serverReplayFilter.desc = "response has been replayed";
|
||||
|
||||
// ~replay
|
||||
function replayFilter(flow){
|
||||
return !!flow.is_replay;
|
||||
}
|
||||
replayFilter.desc = "flow has been replayed";
|
||||
|
||||
// ~src
|
||||
function source(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function sourceFilter(flow){
|
||||
@ -175,6 +239,40 @@ function source(regex){
|
||||
sourceFilter.desc = "source address matches " + regex;
|
||||
return sourceFilter;
|
||||
}
|
||||
|
||||
// ~s
|
||||
function responseFilter(flow){
|
||||
return !!flow.response;
|
||||
}
|
||||
responseFilter.desc = "has response";
|
||||
|
||||
// ~tcp
|
||||
function tcpFilter(flow){
|
||||
return flow.type === "tcp";
|
||||
}
|
||||
tcpFilter.desc = "is a TCP Flow";
|
||||
|
||||
// ~tq
|
||||
function requestContentType(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function requestContentTypeFilter(flow){
|
||||
return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request));
|
||||
}
|
||||
requestContentTypeFilter.desc = "req. content type matches " + regex;
|
||||
return requestContentTypeFilter;
|
||||
}
|
||||
|
||||
// ~ts
|
||||
function responseContentType(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function responseContentTypeFilter(flow){
|
||||
return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response));
|
||||
}
|
||||
responseContentTypeFilter.desc = "resp. content type matches " + regex;
|
||||
return responseContentTypeFilter;
|
||||
}
|
||||
|
||||
// ~t
|
||||
function contentType(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function contentTypeFilter(flow){
|
||||
@ -187,26 +285,8 @@ function contentType(regex){
|
||||
contentTypeFilter.desc = "content type matches " + regex;
|
||||
return contentTypeFilter;
|
||||
}
|
||||
function tcpFilter(flow){
|
||||
return flow.type === "tcp";
|
||||
}
|
||||
tcpFilter.desc = "is a TCP Flow";
|
||||
function requestContentType(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function requestContentTypeFilter(flow){
|
||||
return flow.request && regex.test(flowutils.RequestUtils.getContentType(flow.request));
|
||||
}
|
||||
requestContentTypeFilter.desc = "req. content type matches " + regex;
|
||||
return requestContentTypeFilter;
|
||||
}
|
||||
function responseContentType(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function responseContentTypeFilter(flow){
|
||||
return flow.response && regex.test(flowutils.ResponseUtils.getContentType(flow.response));
|
||||
}
|
||||
responseContentTypeFilter.desc = "resp. content type matches " + regex;
|
||||
return responseContentTypeFilter;
|
||||
}
|
||||
|
||||
// ~u
|
||||
function url(regex){
|
||||
regex = new RegExp(regex, "i");
|
||||
function urlFilter(flow){
|
||||
@ -215,6 +295,8 @@ function url(regex){
|
||||
urlFilter.desc = "url matches " + regex;
|
||||
return urlFilter;
|
||||
}
|
||||
|
||||
// ~websocket
|
||||
function websocketFilter(flow){
|
||||
return flow.type === "websocket";
|
||||
}
|
||||
@ -250,19 +332,18 @@ BindingExpr
|
||||
{ return binding(expr); }
|
||||
/ Expr
|
||||
|
||||
/* All the filters except "~s" and "~src" are arranged in the ascending order as
|
||||
given in the docs(https://mitmproxy.org/docs/latest/concepts-filters/).
|
||||
"~s" and "~src" are so arranged as "~s" caused problems in the evaluation of
|
||||
"~src". */
|
||||
/* All the filters are generally arranged in the order as they are described
|
||||
on https://docs.mitmproxy.org/dev/concepts-filters/, with the exception of
|
||||
single-letter filters, which are moved to the bottom so that they evaluate properly */
|
||||
|
||||
Expr
|
||||
= "true" { return trueFilter; }
|
||||
/ "false" { return falseFilter; }
|
||||
= "~all" { return allFilter; }
|
||||
/ "~a" { return assetFilter; }
|
||||
/ "~b" ws+ s:StringLiteral { return body(s); }
|
||||
/ "~bq" ws+ s:StringLiteral { return requestBody(s); }
|
||||
/ "~bs" ws+ s:StringLiteral { return responseBody(s); }
|
||||
/ "~c" ws+ s:IntegerLiteral { return responseCode(s); }
|
||||
/ "~comment" ws+ s:StringLiteral { return comment(s); }
|
||||
/ "~d" ws+ s:StringLiteral { return domain(s); }
|
||||
/ "~dst" ws+ s:StringLiteral { return destination(s); }
|
||||
/ "~e" { return errorFilter; }
|
||||
@ -270,15 +351,19 @@ Expr
|
||||
/ "~hq" ws+ s:StringLiteral { return requestHeader(s); }
|
||||
/ "~hs" ws+ s:StringLiteral { return responseHeader(s); }
|
||||
/ "~http" { return httpFilter; }
|
||||
/ "~m" ws+ s:StringLiteral { return method(s); }
|
||||
/ "~marked" { return markedFilter; }
|
||||
/ "~marker" ws+ s:StringLiteral { return marker(s); }
|
||||
/ "~m" ws+ s:StringLiteral { return method(s); }
|
||||
/ "~q" { return noResponseFilter; }
|
||||
/ "~replayq" { return clientReplayFilter; }
|
||||
/ "~replays" { return serverReplayFilter; }
|
||||
/ "~replay" { return replayFilter; }
|
||||
/ "~src" ws+ s:StringLiteral { return source(s); }
|
||||
/ "~s" { return responseFilter; }
|
||||
/ "~t" ws+ s:StringLiteral { return contentType(s); }
|
||||
/ "~tcp" { return tcpFilter; }
|
||||
/ "~tq" ws+ s:StringLiteral { return requestContentType(s); }
|
||||
/ "~ts" ws+ s:StringLiteral { return responseContentType(s); }
|
||||
/ "~t" ws+ s:StringLiteral { return contentType(s); }
|
||||
/ "~u" ws+ s:StringLiteral { return url(s); }
|
||||
/ "~websocket" { return websocketFilter; }
|
||||
/ s:StringLiteral { return url(s); }
|
||||
|
@ -8,6 +8,7 @@ interface _Flow {
|
||||
type: string
|
||||
modified: boolean
|
||||
marked: string
|
||||
comment: string
|
||||
client_conn: Client
|
||||
server_conn?: Server
|
||||
error?: Error
|
||||
|
@ -33,7 +33,8 @@ export function updateStoreFromUrl(store) {
|
||||
query
|
||||
.split("&")
|
||||
.forEach((x) => {
|
||||
const [key, value] = x.split("=", 2)
|
||||
let [key, value] = x.split("=", 2)
|
||||
value = decodeURIComponent(value)
|
||||
switch (key) {
|
||||
case Query.SEARCH:
|
||||
store.dispatch(setFilter(value))
|
||||
@ -66,7 +67,7 @@ export function updateUrlFromStore(store) {
|
||||
}
|
||||
const queryStr = Object.keys(query)
|
||||
.filter(k => query[k])
|
||||
.map(k => `${k}=${query[k]}`)
|
||||
.map(k => `${k}=${encodeURIComponent(query[k])}`)
|
||||
.join("&")
|
||||
|
||||
let url
|
||||
|
Loading…
Reference in New Issue
Block a user