mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 18:18:25 +00:00
map local: minor fixes
This commit is contained in:
parent
c98f12c4f3
commit
fb743c7da7
@ -1,7 +1,7 @@
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
import re
|
import re
|
||||||
import typing
|
import typing
|
||||||
import urllib
|
import urllib.parse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from werkzeug.security import safe_join
|
from werkzeug.security import safe_join
|
||||||
@ -59,19 +59,19 @@ def file_candidates(url: str, spec: MapLocalSpec) -> typing.List[Path]:
|
|||||||
else:
|
else:
|
||||||
suffix = re.split(spec.regex, url, maxsplit=1)[1]
|
suffix = re.split(spec.regex, url, maxsplit=1)[1]
|
||||||
suffix = suffix.split("?")[0] # remove query string
|
suffix = suffix.split("?")[0] # remove query string
|
||||||
suffix = suffix.strip("/").replace("\\", "/")
|
suffix = suffix.strip("/")
|
||||||
|
|
||||||
if suffix:
|
if suffix:
|
||||||
decoded_suffix = urllib.parse.unquote(suffix)
|
decoded_suffix = urllib.parse.unquote(suffix)
|
||||||
simplified_suffix = re.sub(r"[^0-9a-zA-Z\-_.=(),/]", "_", decoded_suffix)
|
|
||||||
|
|
||||||
suffix_candidates = [decoded_suffix, f"{decoded_suffix}/index.html"]
|
suffix_candidates = [decoded_suffix, f"{decoded_suffix}/index.html"]
|
||||||
if decoded_suffix != simplified_suffix:
|
|
||||||
suffix_candidates.append(simplified_suffix)
|
escaped_suffix = re.sub(r"[^0-9a-zA-Z\-_.=(),/]", "_", decoded_suffix)
|
||||||
suffix_candidates.append(f"{simplified_suffix}/index.html")
|
if decoded_suffix != escaped_suffix:
|
||||||
|
suffix_candidates.extend([escaped_suffix, f"{escaped_suffix}/index.html"])
|
||||||
try:
|
try:
|
||||||
return [
|
return [
|
||||||
_safe_path_join(spec.local_path, suff) for suff in suffix_candidates
|
_safe_path_join(spec.local_path, x)
|
||||||
|
for x in suffix_candidates
|
||||||
]
|
]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return []
|
return []
|
||||||
@ -110,40 +110,42 @@ class MapLocal:
|
|||||||
|
|
||||||
url = flow.request.pretty_url
|
url = flow.request.pretty_url
|
||||||
|
|
||||||
any_spec_matches = False
|
all_candidates = []
|
||||||
for spec in self.replacements:
|
for spec in self.replacements:
|
||||||
if spec.matches(flow) and re.search(spec.regex, url):
|
if spec.matches(flow) and re.search(spec.regex, url):
|
||||||
any_spec_matches = True
|
|
||||||
|
|
||||||
local_file: typing.Optional[Path] = None
|
|
||||||
tested_paths = []
|
|
||||||
|
|
||||||
if spec.local_path.is_file():
|
if spec.local_path.is_file():
|
||||||
local_file = spec.local_path
|
candidates = [spec.local_path]
|
||||||
elif spec.local_path.is_dir():
|
else:
|
||||||
tested_paths.append(spec.local_path)
|
candidates = file_candidates(url, spec)
|
||||||
for candidate in file_candidates(url, spec):
|
all_candidates.extend(candidates)
|
||||||
tested_paths.append(candidate)
|
|
||||||
|
local_file = None
|
||||||
|
for candidate in candidates:
|
||||||
if candidate.is_file():
|
if candidate.is_file():
|
||||||
local_file = candidate
|
local_file = candidate
|
||||||
break
|
break
|
||||||
|
|
||||||
headers = {"Server": version.MITMPROXY}
|
if local_file:
|
||||||
|
headers = {
|
||||||
|
"Server": version.MITMPROXY
|
||||||
|
}
|
||||||
mimetype = mimetypes.guess_type(str(local_file))[0]
|
mimetype = mimetypes.guess_type(str(local_file))[0]
|
||||||
if mimetype:
|
if mimetype:
|
||||||
headers = {"Content-Type": mimetype}
|
headers["Content-Type"] = mimetype
|
||||||
if local_file:
|
|
||||||
try:
|
try:
|
||||||
flow.response = http.HTTPResponse.make(
|
contents = local_file.read_bytes()
|
||||||
200,
|
|
||||||
local_file.read_bytes(),
|
|
||||||
headers
|
|
||||||
)
|
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
ctx.log.warn(f"Could not read file: {e}")
|
ctx.log.warn(f"Could not read file: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
flow.response = http.HTTPResponse.make(
|
||||||
|
200,
|
||||||
|
contents,
|
||||||
|
headers
|
||||||
|
)
|
||||||
# only set flow.response once, for the first matching rule
|
# only set flow.response once, for the first matching rule
|
||||||
return
|
return
|
||||||
if any_spec_matches:
|
if all_candidates:
|
||||||
flow.response = http.HTTPResponse.make(404)
|
flow.response = http.HTTPResponse.make(404)
|
||||||
ctx.log.warn(f"None of the local file candidates exist: {*tested_paths,}")
|
ctx.log.info(f"None of the local file candidates exist: {', '.join(str(x) for x in all_candidates)}")
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -20,7 +21,8 @@ from mitmproxy.test import tflow
|
|||||||
("http://example.com/foo/bar.jpg", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
("http://example.com/foo/bar.jpg", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
||||||
("https://example.com/foo/bar.jpg", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
("https://example.com/foo/bar.jpg", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
||||||
("https://example.com/foo/bar.jpg?query", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
("https://example.com/foo/bar.jpg?query", ":example.com/foo:/tmp", ["/tmp/bar.jpg", "/tmp/bar.jpg/index.html"]),
|
||||||
("https://example.com/foo/bar/baz.jpg", ":example.com/foo:/tmp", ["/tmp/bar/baz.jpg", "/tmp/bar/baz.jpg/index.html"]),
|
("https://example.com/foo/bar/baz.jpg", ":example.com/foo:/tmp",
|
||||||
|
["/tmp/bar/baz.jpg", "/tmp/bar/baz.jpg/index.html"]),
|
||||||
("https://example.com/foo/bar.jpg", ":/foo/bar.jpg:/tmp", ["/tmp/index.html"]),
|
("https://example.com/foo/bar.jpg", ":/foo/bar.jpg:/tmp", ["/tmp/index.html"]),
|
||||||
] + [
|
] + [
|
||||||
# URL decode and special characters
|
# URL decode and special characters
|
||||||
@ -61,12 +63,12 @@ from mitmproxy.test import tflow
|
|||||||
] + [
|
] + [
|
||||||
# test directory traversal detection
|
# test directory traversal detection
|
||||||
("https://example.com/../../../../../../etc/passwd", ":example.com:/tmp", []),
|
("https://example.com/../../../../../../etc/passwd", ":example.com:/tmp", []),
|
||||||
# those get already sanitized to benign versions before they reach our detection:
|
# this is slightly hacky, but werkzeug's behavior differs per system.
|
||||||
("https://example.com/C:\\foo.txt", ":example.com:/tmp", [
|
("https://example.com/C:\\foo.txt", ":example.com:/tmp", [] if sys.platform == "win32" else [
|
||||||
"/tmp/C:/foo.txt",
|
"/tmp/C:\\foo.txt",
|
||||||
"/tmp/C:/foo.txt/index.html",
|
"/tmp/C:\\foo.txt/index.html",
|
||||||
"/tmp/C_/foo.txt",
|
"/tmp/C__foo.txt",
|
||||||
"/tmp/C_/foo.txt/index.html"
|
"/tmp/C__foo.txt/index.html"
|
||||||
]),
|
]),
|
||||||
("https://example.com//etc/passwd", ":example.com:/tmp", ["/tmp/etc/passwd", "/tmp/etc/passwd/index.html"]),
|
("https://example.com//etc/passwd", ":example.com:/tmp", ["/tmp/etc/passwd", "/tmp/etc/passwd/index.html"]),
|
||||||
]
|
]
|
||||||
|
Loading…
Reference in New Issue
Block a user