authcaptureproxy 1.3.8__tar.gz → 1.3.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/PKG-INFO +1 -1
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/auth_capture_proxy.py +30 -17
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/helper.py +4 -7
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/pyproject.toml +1 -1
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/CHANGELOG.md +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/LICENSE.txt +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/README.md +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/__init__.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/cli.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/const.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/examples/__init__.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/examples/amazon_waf.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/examples/modifiers.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/examples/testers.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/interceptor.py +0 -0
- {authcaptureproxy-1.3.8 → authcaptureproxy-1.3.9}/authcaptureproxy/stackoverflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authcaptureproxy
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: A Python project to create a proxy to capture authentication information from a webpage. This is useful to capture oauth login details without access to a third-party oauth.
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE.txt
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import logging
|
|
6
|
-
import posixpath
|
|
7
6
|
import re
|
|
8
7
|
from functools import partial
|
|
8
|
+
from html import escape, unescape
|
|
9
9
|
from json import JSONDecodeError
|
|
10
10
|
from ssl import SSLContext
|
|
11
11
|
from typing import Any, Callable, Dict, List, Optional, Set, Text, Tuple, Union
|
|
@@ -684,7 +684,6 @@ class AuthCaptureProxy:
|
|
|
684
684
|
# Resolve relative form actions BEFORE modifiers run.
|
|
685
685
|
if text and content_type == "text/html" and resp and resp.url:
|
|
686
686
|
_resp_url = URL(str(resp.url))
|
|
687
|
-
_resp_dir = _resp_url.path.rsplit("/", 1)[0] + "/" if "/" in _resp_url.path else "/"
|
|
688
687
|
|
|
689
688
|
def _resolve_form_action(form_match):
|
|
690
689
|
"""Resolve relative action URLs only inside <form> tags."""
|
|
@@ -696,10 +695,13 @@ class AuthCaptureProxy:
|
|
|
696
695
|
if action and not action.startswith(
|
|
697
696
|
("http://", "https://", "//", "#", "javascript:", "/")
|
|
698
697
|
):
|
|
699
|
-
|
|
698
|
+
resolved_url = _resp_url.join(URL(unescape(action)))
|
|
700
699
|
_proxy_base = self.access_url().path.rstrip("/")
|
|
701
700
|
abs_url = str(
|
|
702
|
-
self.access_url()
|
|
701
|
+
self.access_url()
|
|
702
|
+
.with_path(_proxy_base + resolved_url.path)
|
|
703
|
+
.with_query(resolved_url.query)
|
|
704
|
+
.with_fragment(resolved_url.fragment)
|
|
703
705
|
)
|
|
704
706
|
_LOGGER.debug(
|
|
705
707
|
"Resolved relative form action '%s' -> '%s' (page: %s)",
|
|
@@ -707,7 +709,11 @@ class AuthCaptureProxy:
|
|
|
707
709
|
abs_url,
|
|
708
710
|
_resp_url.path,
|
|
709
711
|
)
|
|
710
|
-
return
|
|
712
|
+
return (
|
|
713
|
+
form_tag[: action_m.start(2)]
|
|
714
|
+
+ escape(abs_url, quote=True)
|
|
715
|
+
+ form_tag[action_m.end(2) :]
|
|
716
|
+
)
|
|
711
717
|
return form_tag
|
|
712
718
|
|
|
713
719
|
text = re.sub(
|
|
@@ -893,18 +899,25 @@ class AuthCaptureProxy:
|
|
|
893
899
|
result["Referer"] = self._swap_proxy_and_host(
|
|
894
900
|
result.get("Referer", ""), domain_only=True
|
|
895
901
|
)
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
"
|
|
903
|
-
"
|
|
904
|
-
"
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
902
|
+
# Remove hop-by-hop and reverse-proxy/CDN headers that must not be forwarded
|
|
903
|
+
# upstream. Besides leaking client info, these can carry non-ASCII values (e.g.
|
|
904
|
+
# Cloudflare's "cf-ipcity: Mérida") that break httpx header encoding. Matching is
|
|
905
|
+
# case-insensitive because HTTP/2 front-ends (e.g. Cloudflare) lowercase header
|
|
906
|
+
# names, so exact-case matching would silently miss them.
|
|
907
|
+
proxy_headers = {
|
|
908
|
+
"content-length",
|
|
909
|
+
"x-forwarded-for",
|
|
910
|
+
"x-forwarded-host",
|
|
911
|
+
"x-forwarded-port",
|
|
912
|
+
"x-forwarded-proto",
|
|
913
|
+
"x-forwarded-scheme",
|
|
914
|
+
"x-forwarded-server",
|
|
915
|
+
"x-real-ip",
|
|
916
|
+
"cdn-loop",
|
|
917
|
+
}
|
|
918
|
+
for item in list(result):
|
|
919
|
+
lowered = item.lower()
|
|
920
|
+
if lowered in proxy_headers or lowered.startswith("cf-"):
|
|
908
921
|
result.pop(item)
|
|
909
922
|
result.update(self.headers if self.headers else {})
|
|
910
923
|
_LOGGER.debug("Final headers %s", result)
|
|
@@ -5,7 +5,6 @@ Python Package for auth capture proxy.
|
|
|
5
5
|
Helper files.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import ast
|
|
9
8
|
import json
|
|
10
9
|
import logging
|
|
11
10
|
import re
|
|
@@ -39,11 +38,11 @@ def print_resp(resp: Union[httpx.Response, ClientResponse]) -> None:
|
|
|
39
38
|
method = resp.request.method
|
|
40
39
|
status = resp.status_code
|
|
41
40
|
reason = resp.reason_phrase
|
|
42
|
-
headers =
|
|
41
|
+
headers: Dict[str, Any] = dict(resp.request.headers)
|
|
43
42
|
cookies = {}
|
|
44
43
|
if headers.get("cookie"):
|
|
45
44
|
cookie: SimpleCookie = SimpleCookie()
|
|
46
|
-
cookie.load(headers
|
|
45
|
+
cookie.load(headers["cookie"])
|
|
47
46
|
for key, morsel in cookie.items():
|
|
48
47
|
cookies[key] = morsel.value
|
|
49
48
|
headers["cookie"] = cookies
|
|
@@ -74,13 +73,11 @@ def print_resp_aiohttp(resp: ClientResponse) -> None:
|
|
|
74
73
|
method = resp.request_info.method
|
|
75
74
|
status = resp.status
|
|
76
75
|
reason = resp.reason
|
|
77
|
-
headers =
|
|
78
|
-
str(resp.request_info.headers).replace("<CIMultiDictProxy(", "{").replace(")>", "}")
|
|
79
|
-
)
|
|
76
|
+
headers: Dict[str, Any] = dict(resp.request_info.headers)
|
|
80
77
|
cookies = {}
|
|
81
78
|
if headers.get("Cookie"):
|
|
82
79
|
cookie: SimpleCookie = SimpleCookie()
|
|
83
|
-
cookie.load(headers
|
|
80
|
+
cookie.load(headers["Cookie"])
|
|
84
81
|
for key, morsel in cookie.items():
|
|
85
82
|
cookies[key] = morsel.value
|
|
86
83
|
headers["Cookie"] = cookies
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "authcaptureproxy"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.9"
|
|
8
8
|
description = "A Python project to create a proxy to capture authentication information from a webpage. This is useful to capture oauth login details without access to a third-party oauth."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|