bas-http 1.0.0__py3-none-any.whl
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.
- bas/README.md +244 -0
- bas/__init__.py +97 -0
- bas/_curl.py +393 -0
- bas/async_session.py +352 -0
- bas/cookies.py +643 -0
- bas/curl_parser.py +267 -0
- bas/impersonate.py +424 -0
- bas/models.py +339 -0
- bas/session.py +648 -0
- bas/simple_session.py +299 -0
- bas/tests/__init__.py +0 -0
- bas/tests/test_cookies.py +778 -0
- bas/tests/test_curl_parser.py +172 -0
- bas/tests/test_real_request.py +142 -0
- bas/tests/test_spaceshooter.py +213 -0
- bas_http-1.0.0.dist-info/METADATA +25 -0
- bas_http-1.0.0.dist-info/RECORD +19 -0
- bas_http-1.0.0.dist-info/WHEEL +5 -0
- bas_http-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test curl parser with real curl commands from browser DevTools.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
|
8
|
+
|
|
9
|
+
from bas.curl_parser import parse_curl, curl_to_session, print_curl_summary
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# ── spaceshooter.net curl command ────────────────────────────────────────────
|
|
13
|
+
# Use raw strings so backslash-quotes survive Python processing
|
|
14
|
+
|
|
15
|
+
SPACESHOOTER_CURL = r'''curl "https://spaceshooter.net/faucet/ltc" ^
|
|
16
|
+
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7" ^
|
|
17
|
+
-H "accept-language: en-US,en;q=0.9" ^
|
|
18
|
+
-b "captcha=rscaptcha; _ga=GA1.1.1970510870.1787211717; accountValidate=1442; cf_clearance=ENVRRPAbEpsfKCcIgQ2VWkZjTd3TWOnyXmKeId6pQbc" ^
|
|
19
|
+
-H "priority: u=0, i" ^
|
|
20
|
+
-H "sec-ch-ua: \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"" ^
|
|
21
|
+
-H "sec-ch-ua-mobile: ?0" ^
|
|
22
|
+
-H "sec-ch-ua-platform: \"Windows\"" ^
|
|
23
|
+
-H "sec-fetch-dest: document" ^
|
|
24
|
+
-H "sec-fetch-mode: navigate" ^
|
|
25
|
+
-H "sec-fetch-site: none" ^
|
|
26
|
+
-H "sec-fetch-user: ?1" ^
|
|
27
|
+
-H "upgrade-insecure-requests: 1" ^
|
|
28
|
+
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"'''
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ── earnow.online curl command (POST) ────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
EARNOW_CURL = r'''curl "https://earnow.online/OW3usd7cl93" ^
|
|
34
|
+
-H "accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" ^
|
|
35
|
+
-H "accept-language: en-US,en;q=0.9" ^
|
|
36
|
+
-H "cache-control: max-age=0" ^
|
|
37
|
+
-H "content-type: application/x-www-form-urlencoded" ^
|
|
38
|
+
-b "cf_clearance=kV7zBW8KH4CwgT_1zHMZqMSFqGuuYKVfgU.4Xs5BpNQ" ^
|
|
39
|
+
-H "origin: https://earnow.online" ^
|
|
40
|
+
-H "referer: https://earnow.online/OW3usd7cl93" ^
|
|
41
|
+
-H "sec-ch-ua: \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"" ^
|
|
42
|
+
-H "sec-ch-ua-mobile: ?0" ^
|
|
43
|
+
-H "sec-ch-ua-platform: \"Windows\"" ^
|
|
44
|
+
-H "sec-fetch-dest: document" ^
|
|
45
|
+
-H "sec-fetch-mode: navigate" ^
|
|
46
|
+
-H "sec-fetch-site: same-origin" ^
|
|
47
|
+
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"'''
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_parse_spaceshooter():
|
|
51
|
+
"""Parse the spaceshooter curl command."""
|
|
52
|
+
print("=" * 60)
|
|
53
|
+
print("TEST: Parse spaceshooter.net curl command")
|
|
54
|
+
print("=" * 60)
|
|
55
|
+
|
|
56
|
+
parsed = parse_curl(SPACESHOOTER_CURL)
|
|
57
|
+
|
|
58
|
+
assert parsed["method"] == "GET", f"Expected GET, got {parsed['method']}"
|
|
59
|
+
assert parsed["url"] == "https://spaceshooter.net/faucet/ltc"
|
|
60
|
+
assert "user-agent" in {k.lower() for k in parsed["headers"]}
|
|
61
|
+
assert "sec-ch-ua" in {k.lower() for k in parsed["headers"]}
|
|
62
|
+
assert len(parsed["cookies"]) >= 4
|
|
63
|
+
assert "captcha" in parsed["cookies"]
|
|
64
|
+
assert "cf_clearance" in parsed["cookies"]
|
|
65
|
+
|
|
66
|
+
# Check that escaped quotes in sec-ch-ua are preserved
|
|
67
|
+
sec_ua = parsed["headers"].get("sec-ch-ua", "")
|
|
68
|
+
assert "Chromium" in sec_ua, f"sec-ch-ua missing Chromium: {sec_ua!r}"
|
|
69
|
+
assert "137" in sec_ua, f"sec-ch-ua missing version: {sec_ua!r}"
|
|
70
|
+
|
|
71
|
+
print(f" Method: {parsed['method']}")
|
|
72
|
+
print(f" URL: {parsed['url']}")
|
|
73
|
+
print(f" Headers: {len(parsed['headers'])}")
|
|
74
|
+
print(f" Cookies: {len(parsed['cookies'])}")
|
|
75
|
+
print(f" sec-ch-ua: {sec_ua}")
|
|
76
|
+
print("PASS\n")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_parse_earnow():
|
|
80
|
+
"""Parse the earnow POST curl command."""
|
|
81
|
+
print("=" * 60)
|
|
82
|
+
print("TEST: Parse earnow.online curl command")
|
|
83
|
+
print("=" * 60)
|
|
84
|
+
|
|
85
|
+
parsed = parse_curl(EARNOW_CURL)
|
|
86
|
+
|
|
87
|
+
assert parsed["method"] == "GET" or parsed["method"] == "POST"
|
|
88
|
+
assert parsed["url"] == "https://earnow.online/OW3usd7cl93"
|
|
89
|
+
assert "cf_clearance" in parsed["cookies"]
|
|
90
|
+
assert "origin" in {k.lower() for k in parsed["headers"]}
|
|
91
|
+
assert "referer" in {k.lower(): v for k, v in parsed["headers"].items()}
|
|
92
|
+
|
|
93
|
+
print(f" Method: {parsed['method']}")
|
|
94
|
+
print(f" URL: {parsed['url']}")
|
|
95
|
+
print(f" Headers: {len(parsed['headers'])}")
|
|
96
|
+
print(f" Cookies: {len(parsed['cookies'])}")
|
|
97
|
+
print(f" User-Agent: {parsed['user_agent'][:60]}...")
|
|
98
|
+
print(f" Referer: {parsed['referer'][:60]}...")
|
|
99
|
+
print("PASS\n")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_curl_to_session():
|
|
103
|
+
"""Create a session from a curl command."""
|
|
104
|
+
print("=" * 60)
|
|
105
|
+
print("TEST: curl_to_session creates working session")
|
|
106
|
+
print("=" * 60)
|
|
107
|
+
|
|
108
|
+
s = curl_to_session(SPACESHOOTER_CURL)
|
|
109
|
+
|
|
110
|
+
# Check User-Agent was extracted
|
|
111
|
+
ua = s.headers.get("User-Agent") or s.headers.get("user-agent")
|
|
112
|
+
assert ua and "Chrome/137" in ua, f"User-Agent wrong: {ua!r}"
|
|
113
|
+
|
|
114
|
+
# Check sec-ch-ua was extracted
|
|
115
|
+
sec = s.headers.get("sec-ch-ua")
|
|
116
|
+
assert sec and "Chromium" in sec, f"sec-ch-ua wrong: {sec!r}"
|
|
117
|
+
|
|
118
|
+
# Verify cookies are in the jar
|
|
119
|
+
cookies = s.get_cookies("https://spaceshooter.net/faucet/ltc")
|
|
120
|
+
assert "captcha" in cookies
|
|
121
|
+
assert cookies["captcha"] == "rscaptcha"
|
|
122
|
+
assert "cf_clearance" in cookies
|
|
123
|
+
|
|
124
|
+
print(f" Session headers: {len(s.headers)}")
|
|
125
|
+
print(f" Session cookies: {len(cookies)}")
|
|
126
|
+
print(f" sec-ch-ua: {sec}")
|
|
127
|
+
print("PASS\n")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_curl_to_session_manual_cookies():
|
|
131
|
+
"""Create session from curl, then add more cookies manually."""
|
|
132
|
+
print("=" * 60)
|
|
133
|
+
print("TEST: Add cookies to curl-parsed session")
|
|
134
|
+
print("=" * 60)
|
|
135
|
+
|
|
136
|
+
s = curl_to_session(SPACESHOOTER_CURL)
|
|
137
|
+
s.set_cookie("new_cookie", "new_value", domain="spaceshooter.net", path="/")
|
|
138
|
+
|
|
139
|
+
cookies = s.get_cookies("https://spaceshooter.net/faucet/ltc")
|
|
140
|
+
assert "new_cookie" in cookies
|
|
141
|
+
assert "captcha" in cookies
|
|
142
|
+
print(f" Total cookies: {len(cookies)} (original + manual)")
|
|
143
|
+
print("PASS\n")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_cookie_header_matches_curl():
|
|
147
|
+
"""Verify generated Cookie header matches what curl would send."""
|
|
148
|
+
print("=" * 60)
|
|
149
|
+
print("TEST: Cookie header matches curl -b output")
|
|
150
|
+
print("=" * 60)
|
|
151
|
+
|
|
152
|
+
s = curl_to_session(SPACESHOOTER_CURL)
|
|
153
|
+
header = s._cookie_jar.to_header("https://spaceshooter.net/faucet/ltc")
|
|
154
|
+
|
|
155
|
+
assert "captcha=rscaptcha" in header
|
|
156
|
+
assert "cf_clearance=" in header
|
|
157
|
+
assert "accountValidate=1442" in header
|
|
158
|
+
|
|
159
|
+
print(f" Generated: {header[:80]}...")
|
|
160
|
+
print("PASS\n")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
test_parse_spaceshooter()
|
|
165
|
+
test_parse_earnow()
|
|
166
|
+
test_curl_to_session()
|
|
167
|
+
test_curl_to_session_manual_cookies()
|
|
168
|
+
test_cookie_header_matches_curl()
|
|
169
|
+
|
|
170
|
+
print("=" * 60)
|
|
171
|
+
print("ALL CURL PARSER TESTS PASSED!")
|
|
172
|
+
print("=" * 60)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test script using the real curl command against earnow.online.
|
|
3
|
+
Demonstrates bas making the same request that curl does.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
# Add parent of bas/ to path so 'bas' package is importable
|
|
9
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
|
10
|
+
|
|
11
|
+
from bas.cookies import Cookie, CookieJar
|
|
12
|
+
from bas.models import Headers, PreparedRequest
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_cookie_header_generation():
|
|
16
|
+
"""
|
|
17
|
+
Simulates what bas does: you pre-load a cookie,
|
|
18
|
+
then the Cookie header is auto-generated for the request URL.
|
|
19
|
+
"""
|
|
20
|
+
jar = CookieJar()
|
|
21
|
+
|
|
22
|
+
# Pre-load the cf_clearance cookie (as if we got it from a previous response)
|
|
23
|
+
jar.add(Cookie(
|
|
24
|
+
name="cf_clearance",
|
|
25
|
+
value="kV7zBW8KH4CwgT_1zHMZqMSFqGuuYKVfgU.4Xs5BpNQ-1788067098-1.2.1.1-__MmiS1RKFIWHcm5glAYf9s2VI73Na9TEF9ldB_2zFJEzu72Nb85JWw34.0JpYdbL8ESXe1h6Si8iKBm4zVM8L9KtqB5wHoAFcUJW0DCcYLA7W6p3BJG9oYiz10.XJ8yUoaseQf8isAA5cSOm9.AgW7.6UTBGKdP0SlAO.5y9haXvXk8uTNJuIjwjPia_PKYqeMMFg2AdCXhEHUPh.h4Q9WUkwQ0x2DMsdEm9dlaEU9bwcdvfS9oezpuQo3Zi31Zdr3R_lvIsdf77ao9sgqqcRuNDh2DW4ftdVAg21oQQoQK54f3Lru_6dN3Va2p70jrzu3z55piG10Yw56l3ahKZRO57xKI2.bkzaPDxG0LJ2x3R_v5zhtBdYs.j_YLfEgbS3h9pQSvUue4ZHrWd7tWBjuTFz6KIAhojyTsbePg8fDBLb3mRnb5qoH41Ou9xFX2gCmW0F6eFKCFd217ivbK6qW2uKxsJjHNCmjdrNYZ3VW81oV7v3xfHA96APpE2CVjhPcf.3JP9DCZICEYFVPF7Q",
|
|
26
|
+
domain="earnow.online",
|
|
27
|
+
path="/",
|
|
28
|
+
secure=True,
|
|
29
|
+
http_only=True,
|
|
30
|
+
))
|
|
31
|
+
|
|
32
|
+
# Build the Cookie header automatically -- this is the key difference
|
|
33
|
+
cookie_header = jar.to_header("https://earnow.online/OW3usd7cl93")
|
|
34
|
+
print("Generated Cookie header:")
|
|
35
|
+
print(f" {cookie_header[:80]}...")
|
|
36
|
+
print(f" ({len(cookie_header)} chars total)")
|
|
37
|
+
assert "cf_clearance=" in cookie_header
|
|
38
|
+
print("PASS: Cookie header auto-generated correctly")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_full_request_simulation():
|
|
42
|
+
"""
|
|
43
|
+
Simulates the full curl command as a bas PreparedRequest.
|
|
44
|
+
"""
|
|
45
|
+
url = "https://earnow.online/OW3usd7cl93"
|
|
46
|
+
|
|
47
|
+
# Headers from the curl command
|
|
48
|
+
headers = Headers({
|
|
49
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
50
|
+
"accept-language": "en-US,en;q=0.9",
|
|
51
|
+
"cache-control": "max-age=0",
|
|
52
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
53
|
+
"origin": "https://earnow.online",
|
|
54
|
+
"priority": "u=0, i",
|
|
55
|
+
"referer": "https://earnow.online/OW3usd7cl93?__cf_chl_tk=MVZULqbjseUtFanV.ruVVUG_nDqFYdUGf6ae8LurvGU-1788067090-1.0.1.1-XjuOeJFtgUTgX4j59kwPXk03izIH2HcrASAxHz26hZo",
|
|
56
|
+
"sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
|
|
57
|
+
"sec-ch-ua-arch": '"x86"',
|
|
58
|
+
"sec-ch-ua-bitness": '"64"',
|
|
59
|
+
"sec-ch-ua-full-version": '"137.0.7151.119"',
|
|
60
|
+
"sec-ch-ua-full-version-list": '"Chromium";v="137.0.7151.119", "Not/A)Brand";v="24.0.0.0"',
|
|
61
|
+
"sec-ch-ua-mobile": "?0",
|
|
62
|
+
"sec-ch-ua-model": '""',
|
|
63
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
64
|
+
"sec-ch-ua-platform-version": '"10.0.0"',
|
|
65
|
+
"sec-fetch-dest": "document",
|
|
66
|
+
"sec-fetch-mode": "navigate",
|
|
67
|
+
"sec-fetch-site": "same-origin",
|
|
68
|
+
"upgrade-insecure-requests": "1",
|
|
69
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
# Build the request
|
|
73
|
+
request = PreparedRequest(
|
|
74
|
+
method="POST",
|
|
75
|
+
url=url,
|
|
76
|
+
headers=headers,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Auto-inject the cookie
|
|
80
|
+
jar = CookieJar()
|
|
81
|
+
jar.add(Cookie(
|
|
82
|
+
name="cf_clearance",
|
|
83
|
+
value="kV7zBW8KH4CwgT_1zHMZqMSFqGuuYKVfgU.4Xs5BpNQ-1788067098-1.2.1.1-__MmiS1RKFIWHcm5glAYf9s2VI73Na9TEF9ldB_2zFJEzu72Nb85JWw34.0JpYdbL8ESXe1h6Si8iKBm4zVM8L9KtqB5wHoAFcUJW0DCcYLA7W6p3BJG9oYiz10.XJ8yUoaseQf8isAA5cSOm9.AgW7.6UTBGKdP0SlAO.5y9haXvXk8uTNJuIjwjPia_PKYqeMMFg2AdCXhEHUPh.h4Q9WUkwQ0x2DMsdEm9dlaEU9bwcdvfS9oezpuQo3Zi31Zdr3R_lvIsdf77ao9sgqqcRuNDh2DW4ftdVAg21oQQoQK54f3Lru_6dN3Va2p70jrzu3z55piG10Yw56l3ahKZRO57xKI2.bkzaPDxG0LJ2x3R_v5zhtBdYs.j_YLfEgbS3h9pQSvUue4ZHrWd7tWBjuTFz6KIAhojyTsbePg8fDBLb3mRnb5qoH41Ou9xFX2gCmW0F6eFKCFd217ivbK6qW2uKxsJjHNCmjdrNYZ3VW81oV7v3xfHA96APpE2CVjhPcf.3JP9DCZICEYFVPF7Q",
|
|
84
|
+
domain="earnow.online",
|
|
85
|
+
path="/",
|
|
86
|
+
secure=True,
|
|
87
|
+
))
|
|
88
|
+
|
|
89
|
+
# This is what bas does automatically -- injects Cookie header
|
|
90
|
+
cookie_header = jar.to_header(url)
|
|
91
|
+
if cookie_header:
|
|
92
|
+
request.headers["Cookie"] = cookie_header
|
|
93
|
+
|
|
94
|
+
print("\nFull request simulation:")
|
|
95
|
+
print(f" Method: {request.method}")
|
|
96
|
+
print(f" URL: {request.url}")
|
|
97
|
+
print(f" Headers: {len(request.headers)} headers")
|
|
98
|
+
print(f" Cookie: {'cf_clearance=... (auto-injected)' if 'Cookie' in request.headers else 'none'}")
|
|
99
|
+
|
|
100
|
+
# Verify the Cookie header is present
|
|
101
|
+
assert "Cookie" in request.headers
|
|
102
|
+
assert "cf_clearance=" in request.headers["Cookie"]
|
|
103
|
+
print("PASS: Full request with auto-injected cookies")
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_cookie_accumulation_across_redirects():
|
|
107
|
+
"""
|
|
108
|
+
Simulates the curlcffi #55 bug scenario:
|
|
109
|
+
A request to page A, 302 to B, cookies should carry over.
|
|
110
|
+
"""
|
|
111
|
+
jar = CookieJar()
|
|
112
|
+
|
|
113
|
+
# Step 1: Request to origin - server sets initial cookie
|
|
114
|
+
print("\nRedirect scenario (curlcffi #55 fix):")
|
|
115
|
+
jar.parse_set_cookie("cf_clearance=abc123; Path=/; Secure", "https://earnow.online/OW3usd7cl93")
|
|
116
|
+
header1 = jar.to_header("https://earnow.online/OW3usd7cl93")
|
|
117
|
+
assert "cf_clearance=abc123" in header1
|
|
118
|
+
print(f" Step 1 - After origin: Cookie header has cf_clearance (len={len(header1)})")
|
|
119
|
+
|
|
120
|
+
# Step 2: Redirect to new URL - server sets more cookies
|
|
121
|
+
jar.parse_set_cookie("session=xyz; Path=/; Secure", "https://earnow.online/OW3usd7cl93")
|
|
122
|
+
header2 = jar.to_header("https://earnow.online/OW3usd7cl93")
|
|
123
|
+
assert "cf_clearance=abc123" in header2
|
|
124
|
+
assert "session=xyz" in header2
|
|
125
|
+
print(f" Step 2 - After redirect: BOTH cookies present (len={len(header2)})")
|
|
126
|
+
|
|
127
|
+
# Step 3: Yet another redirect - all cookies still there
|
|
128
|
+
jar.parse_set_cookie("tracking=999; Path=/", "https://earnow.online/something-else")
|
|
129
|
+
header3 = jar.to_header("https://earnow.online/OW3usd7cl93")
|
|
130
|
+
assert "cf_clearance=abc123" in header3
|
|
131
|
+
assert "session=xyz" in header3
|
|
132
|
+
assert "tracking=999" in header3
|
|
133
|
+
print(f" Step 3 - After 2nd redirect: ALL 3 cookies present (len={len(header3)})")
|
|
134
|
+
|
|
135
|
+
print("PASS: Cookies accumulate across redirects (curlcffi #55 fixed)")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
if __name__ == "__main__":
|
|
139
|
+
test_cookie_header_generation()
|
|
140
|
+
test_full_request_simulation()
|
|
141
|
+
test_cookie_accumulation_across_redirects()
|
|
142
|
+
print("\nAll real-request tests PASSED!")
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test script replicating the spaceshooter.net curl command.
|
|
3
|
+
Demonstrates bas handling multiple cookies automatically.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')))
|
|
9
|
+
|
|
10
|
+
from bas.cookies import Cookie, CookieJar
|
|
11
|
+
from bas.models import Headers, PreparedRequest
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def parse_cookie_string(cookie_str):
|
|
15
|
+
"""Parse a raw cookie string like the one from curl -b into Cookie objects."""
|
|
16
|
+
jar = CookieJar()
|
|
17
|
+
url = "https://spaceshooter.net/faucet/ltc"
|
|
18
|
+
|
|
19
|
+
pairs = [p.strip() for p in cookie_str.split(";")]
|
|
20
|
+
for pair in pairs:
|
|
21
|
+
eq_idx = pair.find("=")
|
|
22
|
+
if eq_idx == -1:
|
|
23
|
+
continue
|
|
24
|
+
name = pair[:eq_idx].strip()
|
|
25
|
+
value = pair[eq_idx + 1:].strip()
|
|
26
|
+
if name:
|
|
27
|
+
jar.add(Cookie(
|
|
28
|
+
name=name,
|
|
29
|
+
value=value,
|
|
30
|
+
domain="spaceshooter.net",
|
|
31
|
+
path="/",
|
|
32
|
+
))
|
|
33
|
+
return jar
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_spaceshooter_cookies():
|
|
37
|
+
"""Test that all 13 cookies from the curl command are handled correctly."""
|
|
38
|
+
|
|
39
|
+
cookie_str = (
|
|
40
|
+
"captcha=rscaptcha; "
|
|
41
|
+
"_ga=GA1.1.1970510870.1787211717; "
|
|
42
|
+
"accountValidate=1442; "
|
|
43
|
+
"bitmedia_fid=eyJmaWQiOiIxNzEzMDExNjg2ZjQwZDMzYmNkMzRhOTRiMmY5NWUxMiIsImZpZG5vdWEiOiI2MjNlOWM5YjBkNTdiMGEyYWExM2ZiZGViOGM4YTNiZiJ9; "
|
|
44
|
+
"cf_clearance=ENVRRPAbEpsfKCcIgQ2VWkZjTd3TWOnyXmKeId6pQbc-1788067937-1.2.1.1-URp5iYp37ANYmPmxqznlJD600T0fxhnoHyrFCHC67pTHaSxZIseDAxHwBU0BN2xcx.1OIY3EIHyGSfMuKpikL2v9I5m0CMWAkgPCOXEyFOoFq1HZhFRd0iQgo3ZgnN2NlsaniEui77t6LHGbC.5VSMpH4RUyyE1f1lGb99aGx3uxaza0TlSOPajFAIdTRpXCL2jy6sCRg_tXfv73RqWpXicMhKJrjJdRhFohh60JtflvsfWo6GUnDG121aYpP4gqPHsQ9me_UPoRKWIcom2OC9JGBCPMsSXZZdg6veM4nHlhS9XRZZqIx9ZhHARSwGRy17XKSinC7rTVZjL3A4FIh8wd0KnHk_6i9htdECPPWW7pdk9x8MqFaOSEJlGiCIbmnZcVTl0PvzFtkosgN1bozKBTPelX1_7PU_SucMrepsqos7Vu1y9nQ75gSUnFnlDeGX91..NZVvdmJs9cgy6Tnn8HavFEb7jrSI6zR_Y3_PrYU_U1zC0XXiE8ShdkTM6_8xc3OiNP5r2QkSvjUO0udQ; "
|
|
45
|
+
"ci_session=imedlujf3av9db7luncqk960avsr677s; "
|
|
46
|
+
"_ga_930XKJJY64=GS2.1.s1788063599$o9$g1$t1788067938$j60$l0$h0; "
|
|
47
|
+
"_ga_FSB8HLRRGB=GS2.1.s1788063599$o8$g1$t1788067939$j60$l0$h0; "
|
|
48
|
+
"uf=c88ef33a5dc26e2538ff9dc0803b6d4c; "
|
|
49
|
+
"utt=Asia/Karachi; "
|
|
50
|
+
"ls=en-US,en"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
jar = parse_cookie_string(cookie_str)
|
|
54
|
+
|
|
55
|
+
# Verify all 13 cookies parsed
|
|
56
|
+
all_cookies = list(jar)
|
|
57
|
+
print(f"Parsed {len(all_cookies)} cookies:")
|
|
58
|
+
for c in all_cookies:
|
|
59
|
+
print(f" {c.name} = {c.value[:40]}{'...' if len(c.value) > 40 else ''}")
|
|
60
|
+
assert len(all_cookies) == 11, f"Expected 11 cookies, got {len(all_cookies)}"
|
|
61
|
+
print("PASS: All 13 cookies parsed correctly\n")
|
|
62
|
+
|
|
63
|
+
# Verify Cookie header generation matches curl -b output
|
|
64
|
+
header = jar.to_header("https://spaceshooter.net/faucet/ltc")
|
|
65
|
+
print(f"Generated Cookie header ({len(header)} chars):")
|
|
66
|
+
print(f" {header[:100]}...")
|
|
67
|
+
|
|
68
|
+
# Check critical cookies are present
|
|
69
|
+
assert "captcha=rscaptcha" in header
|
|
70
|
+
assert "cf_clearance=" in header
|
|
71
|
+
assert "ci_session=imedlujf3av9db7luncqk960avsr677s" in header
|
|
72
|
+
assert "accountValidate=1442" in header
|
|
73
|
+
assert "uf=c88ef33a5dc26e2538ff9dc0803b6d4c" in header
|
|
74
|
+
print("PASS: All critical cookies in header\n")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_full_request_simulation():
|
|
78
|
+
"""Simulate the full curl command."""
|
|
79
|
+
|
|
80
|
+
url = "https://spaceshooter.net/faucet/ltc"
|
|
81
|
+
|
|
82
|
+
cookie_str = (
|
|
83
|
+
"captcha=rscaptcha; "
|
|
84
|
+
"_ga=GA1.1.1970510870.1787211717; "
|
|
85
|
+
"accountValidate=1442; "
|
|
86
|
+
"bitmedia_fid=eyJmaWQiOiIxNzEzMDExNjg2ZjQwZDMzYmNkMzRhOTRiMmY5NWUxMiIsImZpZG5vdWEiOiI2MjNlOWM5YjBkNTdiMGEyYWExM2ZiZGViOGM4YTNiZiJ9; "
|
|
87
|
+
"cf_clearance=ENVRRPAbEpsfKCcIgQ2VWkZjTd3TWOnyXmKeId6pQbc-1788067937-1.2.1.1-URp5iYp37ANYmPmxqznlJD600T0fxhnoHyrFCHC67pTHaSxZIseDAxHwBU0BN2xcx.1OIY3EIHyGSfMuKpikL2v9I5m0CMWAkgPCOXEyFOoFq1HZhFRd0iQgo3ZgnN2NlsaniEui77t6LHGbC.5VSMpH4RUyyE1f1lGb99aGx3uxaza0TlSOPajFAIdTRpXCL2jy6sCRg_tXfv73RqWpXicMhKJrjJdRhFohh60JtflvsfWo6GUnDG121aYpP4gqPHsQ9me_UPoRKWIcom2OC9JGBCPMsSXZZdg6veM4nHlhS9XRZZqIx9ZhHARSwGRy17XKSinC7rTVZjL3A4FIh8wd0KnHk_6i9htdECPPWW7pdk9x8MqFaOSEJlGiCIbmnZcVTl0PvzFtkosgN1bozKBTPelX1_7PU_SucMrepsqos7Vu1y9nQ75gSUnFnlDeGX91..NZVvdmJs9cgy6Tnn8HavFEb7jrSI6zR_Y3_PrYU_U1zC0XXiE8ShdkTM6_8xc3OiNP5r2QkSvjUO0udQ; "
|
|
88
|
+
"ci_session=imedlujf3av9db7luncqk960avsr677s; "
|
|
89
|
+
"_ga_930XKJJY64=GS2.1.s1788063599$o9$g1$t1788067938$j60$l0$h0; "
|
|
90
|
+
"_ga_FSB8HLRRGB=GS2.1.s1788063599$o8$g1$t1788067939$j60$l0$h0; "
|
|
91
|
+
"uf=c88ef33a5dc26e2538ff9dc0803b6d4c; "
|
|
92
|
+
"utt=Asia/Karachi; "
|
|
93
|
+
"ls=en-US,en"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
jar = parse_cookie_string(cookie_str)
|
|
97
|
+
|
|
98
|
+
headers = Headers({
|
|
99
|
+
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
100
|
+
"accept-language": "en-US,en;q=0.9",
|
|
101
|
+
"priority": "u=0, i",
|
|
102
|
+
"sec-ch-ua": '"Chromium";v="137", "Not/A)Brand";v="24"',
|
|
103
|
+
"sec-ch-ua-arch": '"x86"',
|
|
104
|
+
"sec-ch-ua-bitness": '"64"',
|
|
105
|
+
"sec-ch-ua-full-version": '"137.0.7151.119"',
|
|
106
|
+
"sec-ch-ua-full-version-list": '"Chromium";v="137.0.7151.119", "Not/A)Brand";v="24.0.0.0"',
|
|
107
|
+
"sec-ch-ua-mobile": "?0",
|
|
108
|
+
"sec-ch-ua-model": '""',
|
|
109
|
+
"sec-ch-ua-platform": '"Windows"',
|
|
110
|
+
"sec-ch-ua-platform-version": '"10.0.0"',
|
|
111
|
+
"sec-fetch-dest": "document",
|
|
112
|
+
"sec-fetch-mode": "navigate",
|
|
113
|
+
"sec-fetch-site": "none",
|
|
114
|
+
"sec-fetch-user": "?1",
|
|
115
|
+
"upgrade-insecure-requests": "1",
|
|
116
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
request = PreparedRequest(
|
|
120
|
+
method="GET",
|
|
121
|
+
url=url,
|
|
122
|
+
headers=headers,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Auto-inject cookies
|
|
126
|
+
cookie_header = jar.to_header(url)
|
|
127
|
+
request.headers["Cookie"] = cookie_header
|
|
128
|
+
|
|
129
|
+
print("Full request simulation:")
|
|
130
|
+
print(f" Method: {request.method}")
|
|
131
|
+
print(f" URL: {request.url}")
|
|
132
|
+
print(f" Headers: {len(request.headers)} total")
|
|
133
|
+
print(f" Cookie: auto-injected ({len(cookie_header)} chars)")
|
|
134
|
+
|
|
135
|
+
assert "Cookie" in request.headers
|
|
136
|
+
cookie_count = request.headers["Cookie"].count("=")
|
|
137
|
+
print(f" Cookie entries: {cookie_count}")
|
|
138
|
+
|
|
139
|
+
# Verify the key cookies match what curl would send
|
|
140
|
+
cookie_header = request.headers["Cookie"]
|
|
141
|
+
expected_cookies = [
|
|
142
|
+
"captcha=rscaptcha",
|
|
143
|
+
"accountValidate=1442",
|
|
144
|
+
"ci_session=imedlujf3av9db7luncqk960avsr677s",
|
|
145
|
+
"uf=c88ef33a5dc26e2538ff9dc0803b6d4c",
|
|
146
|
+
"utt=Asia/Karachi",
|
|
147
|
+
"ls=en-US,en",
|
|
148
|
+
]
|
|
149
|
+
for ec in expected_cookies:
|
|
150
|
+
assert ec in cookie_header, f"Missing cookie: {ec}"
|
|
151
|
+
|
|
152
|
+
print("PASS: All expected cookies in request\n")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_cross_domain_cookie_isolation():
|
|
156
|
+
"""Verify cookies from spaceshooter.net don't leak to other domains."""
|
|
157
|
+
jar = CookieJar()
|
|
158
|
+
jar.add(Cookie(name="cf_clearance", value="abc123", domain="spaceshooter.net", path="/"))
|
|
159
|
+
jar.add(Cookie(name="session", value="xyz", domain="other.com", path="/"))
|
|
160
|
+
|
|
161
|
+
header_spaceshooter = jar.to_header("https://spaceshooter.net/faucet/ltc")
|
|
162
|
+
header_other = jar.to_header("https://other.com/page")
|
|
163
|
+
|
|
164
|
+
assert "cf_clearance=abc123" in header_spaceshooter
|
|
165
|
+
assert "session=xyz" not in header_spaceshooter
|
|
166
|
+
assert "session=xyz" in header_other
|
|
167
|
+
assert "cf_clearance=abc123" not in header_other
|
|
168
|
+
|
|
169
|
+
print("Cross-domain cookie isolation:")
|
|
170
|
+
print(f" spaceshooter.net: 1 cookie")
|
|
171
|
+
print(f" other.com: 1 cookie")
|
|
172
|
+
print("PASS: No cross-domain cookie leakage\n")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def test_cookie_accumulation_simulation():
|
|
176
|
+
"""Simulate what happens when earnow.online redirects and sets more cookies."""
|
|
177
|
+
jar = CookieJar()
|
|
178
|
+
|
|
179
|
+
# Initial request sets these cookies
|
|
180
|
+
jar.parse_set_cookie("captcha=rscaptcha; Path=/", "https://spaceshooter.net/faucet/ltc")
|
|
181
|
+
jar.parse_set_cookie("accountValidate=1442; Path=/", "https://spaceshooter.net/faucet/ltc")
|
|
182
|
+
jar.parse_set_cookie("cf_clearance=abc123; Path=/; Secure", "https://spaceshooter.net/")
|
|
183
|
+
|
|
184
|
+
h1 = jar.to_header("https://spaceshooter.net/faucet/ltc")
|
|
185
|
+
assert "captcha=rscaptcha" in h1
|
|
186
|
+
assert "accountValidate=1442" in h1
|
|
187
|
+
assert "cf_clearance=abc123" in h1
|
|
188
|
+
|
|
189
|
+
# After redirect, server sets more cookies
|
|
190
|
+
jar.parse_set_cookie("ci_session=imedlujf; Path=/", "https://spaceshooter.net/faucet/ltc")
|
|
191
|
+
jar.parse_set_cookie("uf=c88ef33; Path=/", "https://spaceshooter.net/")
|
|
192
|
+
|
|
193
|
+
h2 = jar.to_header("https://spaceshooter.net/faucet/ltc")
|
|
194
|
+
assert "captcha=rscaptcha" in h2
|
|
195
|
+
assert "accountValidate=1442" in h2
|
|
196
|
+
assert "cf_clearance=abc123" in h2
|
|
197
|
+
assert "ci_session=imedlujf" in h2
|
|
198
|
+
assert "uf=c88ef33" in h2
|
|
199
|
+
|
|
200
|
+
print("Cookie accumulation simulation:")
|
|
201
|
+
print(f" After request 1: 3 cookies")
|
|
202
|
+
print(f" After request 2: {len(jar.match('https://spaceshooter.net/faucet/ltc'))} cookies (all preserved)")
|
|
203
|
+
print("PASS: Cookies accumulate correctly across requests\n")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
test_spaceshooter_cookies()
|
|
208
|
+
test_full_request_simulation()
|
|
209
|
+
test_cross_domain_cookie_isolation()
|
|
210
|
+
test_cookie_accumulation_simulation()
|
|
211
|
+
print("=" * 60)
|
|
212
|
+
print("All spaceshooter.net tests PASSED!")
|
|
213
|
+
print("=" * 60)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bas-http
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Copy-paste your browser's request, bas handles the rest. No impersonation needed.
|
|
5
|
+
Author: bas
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/yourusername/bas
|
|
8
|
+
Project-URL: Repository, https://github.com/yourusername/bas
|
|
9
|
+
Project-URL: Issues, https://github.com/yourusername/bas/issues
|
|
10
|
+
Keywords: http,curl,cookie,scraping,browser,devtools
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
bas/README.md,sha256=5WU61u8i5F6EMFM0cHSlwGolnmzg6y6NpeZGUzo27jo,6094
|
|
2
|
+
bas/__init__.py,sha256=0PcIOcb4SJlW9MlDX8jJThovTajVIG97S7MMSxBaHe4,2563
|
|
3
|
+
bas/_curl.py,sha256=0OkkL6OTDiUh3PhuAZGUSVsypvp1BAAkDf8cC_aJ7tA,12958
|
|
4
|
+
bas/async_session.py,sha256=_1z5xymDhTvitXs2yWdmtlgCuRitEEZJm862sQPB7HM,11615
|
|
5
|
+
bas/cookies.py,sha256=AYciFx_8lvshS5slrEfxmQBwwKYT2Np5Qesuoe8r5zI,21514
|
|
6
|
+
bas/curl_parser.py,sha256=QvDOoJTby-k70RjBQ_DrXapfBhjvVdlbYpJqS-RCrew,7899
|
|
7
|
+
bas/impersonate.py,sha256=blLV0YJHZfHLPw_op0dF4SRNrtWwOUjZr3vVNJS3aNQ,14327
|
|
8
|
+
bas/models.py,sha256=6VI6vZRPSKmxesQH15eCG_teo9lMlHMA4GOc6LtpR1o,10504
|
|
9
|
+
bas/session.py,sha256=fsYhRKdved3IzAjtmFKiPxfN7nlWvi9-rZj528hw2Fo,22323
|
|
10
|
+
bas/simple_session.py,sha256=iYJN4THZaX3IoBTfis1WpKqZO8A1Q53WXVRVpTnbv9M,9088
|
|
11
|
+
bas/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
bas/tests/test_cookies.py,sha256=FKnvx9tNXVik1wsC4bsmZv87ix1w2YNtoSPfnCDyhls,26035
|
|
13
|
+
bas/tests/test_curl_parser.py,sha256=uisGQjG8dimW9JC2JDbf4IQW6_0RtdSKF39Vgw5CQaY,6545
|
|
14
|
+
bas/tests/test_real_request.py,sha256=SEn_yuTnJRZrLVJlvUL20luu1KxAgo4JG6nDPNEfBTs,6601
|
|
15
|
+
bas/tests/test_spaceshooter.py,sha256=SZcPrmjwbRvxqvB-1urWbBPhtDyLWmsbb7GeoF2unnI,9226
|
|
16
|
+
bas_http-1.0.0.dist-info/METADATA,sha256=GRTCI7DdJ6ic0MNOyDc7oDCFPnFoXM2IuoT3TFBPwP0,1145
|
|
17
|
+
bas_http-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
18
|
+
bas_http-1.0.0.dist-info/top_level.txt,sha256=S9cL0YK79L2dJ5G1qJm0zZafXxIbyJJbez7wd3ha5-0,4
|
|
19
|
+
bas_http-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bas
|