first commit

This commit is contained in:
Hermes Agent
2026-05-10 13:52:46 +08:00
commit ccc63d1e70
4583 changed files with 584341 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
"""
Diagnose CSP form-action blocking on a target page.
Usage:
python diagnose_csp_form_action.py <login_url> <username> <password>
Produces:
- CSP header analysis
- Form action inspection
- Console message capture (CSP violations)
- Network request trace (ERR_ABORTED detection)
- Cookie state after submission
"""
import asyncio
import sys
from playwright.async_api import async_playwright
async def diagnose(login_url: str, username: str, password: str):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
console_msgs = []
page.on("console", lambda msg: console_msgs.append(f"[{msg.type}] {msg.text}"))
# CDP for detailed network tracing
cdp = await context.new_cdp_session(page)
await cdp.send("Network.enable")
network_events = []
def on_resp(params):
resp = params.get("response", {})
network_events.append({
"type": "response",
"status": resp.get("status"),
"url": resp.get("url", "")[:120],
"location": resp.get("headers", {}).get("location", ""),
})
def on_fail(params):
network_events.append({
"type": "fail",
"error": params.get("errorText", ""),
"reason": params.get("blockedReason", ""),
})
cdp.on("Network.responseReceived", on_resp)
cdp.on("Network.loadingFailed", on_fail)
# Step 1: Load page
print(f"Loading: {login_url}")
await page.goto(login_url)
# Step 2: Check CSP
csp = await page.evaluate("""
async () => {
const r = await fetch(location.href, {method: 'HEAD'});
return r.headers.get('content-security-policy') || 'none';
}
""")
has_form_action = "form-action" in csp
print(f"CSP form-action directive: {'YES' if has_form_action else 'none'}")
if has_form_action:
# Extract just the form-action part
for part in csp.split(";"):
if "form-action" in part:
print(f"{part.strip()}")
# Step 3: Inspect form
form_info = await page.evaluate("""
() => {
const form = document.querySelector('form');
if (!form) return null;
return {
action: form.action,
method: form.method,
hasQueryInAction: form.action.includes('?'),
hiddenFields: Array.from(form.querySelectorAll('input[type=hidden]'))
.map(i => ({name: i.name, value: i.value.substring(0, 80)}))
};
}
""")
if form_info:
print(f"\nForm action: {form_info['action']}")
print(f"Has query in action: {form_info['hasQueryInAction']}")
print(f"Hidden fields: {form_info['hiddenFields']}")
else:
print("No form found on page!")
return
# Step 4: Submit form
await page.fill('input[name="username"], #username', username)
await page.fill('input[name="password"], #password', password)
print("\nSubmitting form...")
try:
async with page.expect_navigation(timeout=8000):
await page.click('button[type="submit"], input[type="submit"]')
print(f"Navigation OK → {page.url}")
except Exception as e:
print(f"Navigation FAILED: {e}")
print(f"Stayed on: {page.url}")
await asyncio.sleep(1)
# Step 5: Report
cookies = await context.cookies()
auth_cookies = [c for c in cookies if 'auth' in c['name'].lower()]
print(f"\nAuth cookies: {len(auth_cookies)}")
for c in auth_cookies:
print(f" {c['name']} domain={c['domain']}")
csp_errors = [m for m in console_msgs if "form-action" in m or "violates" in m]
print(f"\nCSP violations: {len(csp_errors)}")
for e in csp_errors:
print(f" {e}")
failures = [e for e in network_events if e["type"] == "fail"]
print(f"\nNetwork failures: {len(failures)}")
for f in failures:
print(f" {f['error']} (reason: {f['reason']})")
verdict = "BLOCKED" if csp_errors else ("OK" if "login-success" in page.url or "www." in page.url else "UNKNOWN")
print(f"\n{'='*40}")
print(f"VERDICT: {verdict}")
await browser.close()
if __name__ == "__main__":
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} <login_url> <username> <password>")
sys.exit(1)
asyncio.run(diagnose(sys.argv[1], sys.argv[2], sys.argv[3]))