map local: minor fixes

This commit is contained in:
Maximilian Hils 2020-07-17 20:25:48 +02:00
parent c98f12c4f3
commit fb743c7da7
2 changed files with 44 additions and 40 deletions

View File

@ -1,7 +1,7 @@
import mimetypes
import re
import typing
import urllib
import urllib.parse
from pathlib import Path
from werkzeug.security import safe_join
@ -59,19 +59,19 @@ def file_candidates(url: str, spec: MapLocalSpec) -> typing.List[Path]:
else:
suffix = re.split(spec.regex, url, maxsplit=1)[1]
suffix = suffix.split("?")[0] # remove query string
suffix = suffix.strip("/").replace("\\", "/")
suffix = suffix.strip("/")
if 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"]
if decoded_suffix != simplified_suffix:
suffix_candidates.append(simplified_suffix)
suffix_candidates.append(f"{simplified_suffix}/index.html")
escaped_suffix = re.sub(r"[^0-9a-zA-Z\-_.=(),/]", "_", decoded_suffix)
if decoded_suffix != escaped_suffix:
suffix_candidates.extend([escaped_suffix, f"{escaped_suffix}/index.html"])
try:
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:
return []
@ -110,40 +110,42 @@ class MapLocal:
url = flow.request.pretty_url
any_spec_matches = False
all_candidates = []
for spec in self.replacements:
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():
local_file = spec.local_path
elif spec.local_path.is_dir():
tested_paths.append(spec.local_path)
for candidate in file_candidates(url, spec):
tested_paths.append(candidate)
if candidate.is_file():
local_file = candidate
break
candidates = [spec.local_path]
else:
candidates = file_candidates(url, spec)
all_candidates.extend(candidates)
local_file = None
for candidate in candidates:
if candidate.is_file():
local_file = candidate
break
headers = {"Server": version.MITMPROXY}
mimetype = mimetypes.guess_type(str(local_file))[0]
if mimetype:
headers = {"Content-Type": mimetype}
if local_file:
headers = {
"Server": version.MITMPROXY
}
mimetype = mimetypes.guess_type(str(local_file))[0]
if mimetype:
headers["Content-Type"] = mimetype
try:
flow.response = http.HTTPResponse.make(
200,
local_file.read_bytes(),
headers
)
contents = local_file.read_bytes()
except IOError as e:
ctx.log.warn(f"Could not read file: {e}")
continue
flow.response = http.HTTPResponse.make(
200,
contents,
headers
)
# only set flow.response once, for the first matching rule
return
if any_spec_matches:
if all_candidates:
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)}")

View File

@ -1,3 +1,4 @@
import sys
from pathlib import Path
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"]),
("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/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"]),
] + [
# URL decode and special characters
@ -61,12 +63,12 @@ from mitmproxy.test import tflow
] + [
# test directory traversal detection
("https://example.com/../../../../../../etc/passwd", ":example.com:/tmp", []),
# those get already sanitized to benign versions before they reach our detection:
("https://example.com/C:\\foo.txt", ":example.com:/tmp", [
"/tmp/C:/foo.txt",
"/tmp/C:/foo.txt/index.html",
"/tmp/C_/foo.txt",
"/tmp/C_/foo.txt/index.html"
# this is slightly hacky, but werkzeug's behavior differs per system.
("https://example.com/C:\\foo.txt", ":example.com:/tmp", [] if sys.platform == "win32" else [
"/tmp/C:\\foo.txt",
"/tmp/C:\\foo.txt/index.html",
"/tmp/C__foo.txt",
"/tmp/C__foo.txt/index.html"
]),
("https://example.com//etc/passwd", ":example.com:/tmp", ["/tmp/etc/passwd", "/tmp/etc/passwd/index.html"]),
]