Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for ~b, ~bq, and ~bs filters in mitmweb. #6538

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -19,6 +19,9 @@
([#4860](https://github.com/mitmproxy/mitmproxy/pull/4860), @Speedlulu)
* Enhance documentation and add alert log messages when stream_large_bodies and modify_body are set
([#6514](https://github.com/mitmproxy/mitmproxy/pull/6514), @rosydawn6)
* Added support for `~b`, `~bq`, and `~bs` filters in `mitmweb`.
([#6538](https://github.com/mitmproxy/mitmproxy/pull/6538), @dsphper)



## 14 November 2023: mitmproxy 10.1.5
Expand Down
19 changes: 19 additions & 0 deletions mitmproxy/tools/web/app.py
Expand Up @@ -354,6 +354,24 @@ async def post(self):
bio.close()


class FilterFlows(RequestHandler):
def get(self) -> None:
self.set_header("Content-Type", "application/json")

def get_matched_ids(match):
return [f.id for f in self.view if match(f)]

try:
match = flowfilter.parse(self.request.arguments["filter"][0].decode())
matched_ids = get_matched_ids(match)
except (ValueError, KeyError, IndexError):
matched_ids = [] # return empty list if error occurs

self.set_status(200)
# Write the list of incrIds to the response
self.write(json.dumps(matched_ids))


class ClearAll(RequestHandler):
def post(self):
self.view.clear()
Expand Down Expand Up @@ -685,6 +703,7 @@ def __init__(
(r"/events(?:\.json)?", Events),
(r"/flows(?:\.json)?", Flows),
(r"/flows/dump", DumpFlows),
(r"/flows/filter", FilterFlows),
(r"/flows/resume", ResumeFlows),
(r"/flows/kill", KillFlows),
(r"/flows/(?P<flow_id>[0-9a-f\-]+)", FlowHandler),
Expand Down
2 changes: 1 addition & 1 deletion mitmproxy/tools/web/static/app.css

Large diffs are not rendered by default.

122 changes: 64 additions & 58 deletions mitmproxy/tools/web/static/app.js

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions test/mitmproxy/tools/web/test_app.py
Expand Up @@ -241,6 +241,14 @@ def test_flows_dump_filter_error(self):
resp = self.fetch("/flows/dump?filter=[")
assert resp.code == 400

def test_flows_filter(self):
resp = self.fetch("/flows/filter?filter=foo")
assert b"[]" == resp.body

def test_flows_filter_error(self):
resp = self.fetch("/flows/filter?filter=~b")
assert b"[]" == resp.body

def test_clear(self):
events = self.events.data.copy()
flows = list(self.view)
Expand Down
18 changes: 18 additions & 0 deletions web/src/js/__tests__/components/Header/FilterInputSpec.tsx
Expand Up @@ -4,6 +4,10 @@ import FilterInput from "../../../components/Header/FilterInput";
import FilterDocs from "../../../components/Header/FilterDocs";
import TestUtil from "react-dom/test-utils";
import ReactDOM from "react-dom";
import { fetchApi } from "../../../utils";
import fetchMock, { enableFetchMocks } from "jest-fetch-mock";

enableFetchMocks();

describe("FilterInput Component", () => {
it("should render correctly", () => {
Expand Down Expand Up @@ -106,4 +110,18 @@ describe("FilterInput Component", () => {
filterInput.select();
expect(input.select).toBeCalled();
});

it("should handle fetchFilterData", async () => {
expect(filterInput.fetchFilterData("~bs ")).toEqual(
Promise.resolve({})
);
});

it("should handle change2", () => {
fetchMock.mockOnceIf("/flows/filter?filter=~b%20x", "[]");
fetchMock.mockResponses("{}");
let mockEvent = { target: { value: "~b x" } };
filterInput.onChange(mockEvent);
expect(filterInput.state.value).toEqual("~b x");
});
});
41 changes: 37 additions & 4 deletions web/src/js/components/Header/FilterInput.tsx
Expand Up @@ -3,6 +3,7 @@ import ReactDOM from "react-dom";
import classnames from "classnames";
import Filt from "../../filt/filt";
import FilterDocs from "./FilterDocs";
import { fetchApi } from "../../utils";

type FilterInputProps = {
type: string;
Expand Down Expand Up @@ -71,13 +72,39 @@ export default class FilterInput extends Component<
}
}

fetchFilterData(filterParam) {
return fetchApi(
`/flows/filter?filter=${encodeURIComponent(filterParam)}`
)
.then((response) => {
return response.json();
})
.catch((error) => {});
}

onChange(e) {
const value = e.target.value;
this.setState({ value });

// Only propagate valid filters upwards.
if (this.isValid(value)) {
this.props.onChange(value);
// Check if input starts with "~bs", "~b" or "~bq" and has more characters after it
if (
value.includes("~bs ") ||
value.includes("~b ") ||
value.includes("~bq ")
) {
// Use the new fetchFilterData function
this.fetchFilterData(value).then((data) => {
// @ts-ignore
window.filtFilterList = data;
// Only propagate valid filters upwards.
if (this.isValid(value)) {
this.props.onChange(value);
}
});
} else {
// Only propagate valid filters upwards.
if (this.isValid(value)) {
this.props.onChange(value);
}
}
}

Expand Down Expand Up @@ -119,6 +146,12 @@ export default class FilterInput extends Component<
ReactDOM.findDOMNode(this.refs.input).select();
}

componentDidMount() {
if (this.state.value) {
this.onChange({ target: { value: this.state.value } });
}
}

render() {
const { type, color, placeholder } = this.props;
const { value, focus, mousefocus } = this.state;
Expand Down
22 changes: 15 additions & 7 deletions web/src/js/filt/filt.js
Expand Up @@ -2077,34 +2077,42 @@ export default (function() {
return false;
}
assetFilter.desc = "is asset";

// ~b
function body(regex){
regex = new RegExp(regex, "i");
function bodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
bodyFilter.desc = "body filters " + regex;
return bodyFilter;
}

// ~bq
function requestBody(regex){
regex = new RegExp(regex, "i");
function requestBodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
requestBodyFilter.desc = "request body filters " + regex;
return requestBodyFilter;
}

// ~bs
function responseBody(regex){
regex = new RegExp(regex, "i");
function responseBodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
responseBodyFilter.desc = "response body filters " + regex;
return responseBodyFilter;
}

Expand Down
22 changes: 15 additions & 7 deletions web/src/js/filt/filt.peg
Expand Up @@ -62,34 +62,42 @@ function assetFilter(flow) {
return false;
}
assetFilter.desc = "is asset";

// ~b
function body(regex){
regex = new RegExp(regex, "i");
function bodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
bodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
bodyFilter.desc = "body filters " + regex;
return bodyFilter;
}

// ~bq
function requestBody(regex){
regex = new RegExp(regex, "i");
function requestBodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
requestBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
requestBodyFilter.desc = "request body filters " + regex;
return requestBodyFilter;
}

// ~bs
function responseBody(regex){
regex = new RegExp(regex, "i");
function responseBodyFilter(flow){
return true;
if (window.filtFilterList && window.filtFilterList.includes(flow.id)) {
return true
}
return false;
}
responseBodyFilter.desc = "body filters are not implemented yet, see https://github.com/mitmproxy/mitmproxy/issues/3609";
responseBodyFilter.desc = "response body filters " + regex;
return responseBodyFilter;
}

Expand Down