Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

101 lines
3.9 KiB
Python

# allow: no-sut-import — black-box HTTP test; drives real routes through the Flask test client
"""
End-to-end CSRF flow test for browser-facing API endpoints.
Verifies the full flow: login → CSRF token → API call with CSRF enabled,
ensuring the narrowed CSRF exemptions (only api_v1 exempt) work correctly
for browser-facing endpoints like /api/start_research.
"""
import re
import uuid
import pytest
class TestCSRFEndToEndFlow:
"""Test the complete CSRF flow from login through API usage."""
@pytest.fixture
def csrf_app(self, app):
"""Wrap the root conftest app fixture with CSRF enabled."""
app.config["WTF_CSRF_ENABLED"] = True
app.config["WTF_CSRF_CHECK_DEFAULT"] = True
return app
def test_full_csrf_flow_login_through_api_call(self, csrf_app):
"""Test complete browser-like flow: register → get CSRF token → API call."""
client = csrf_app.test_client()
with client:
# Step 1: GET /auth/login to get a CSRF token for registration
response = client.get("/auth/login")
assert response.status_code == 200
csrf_match = re.search(
r'name="csrf_token" value="([^"]+)"', response.data.decode()
)
assert csrf_match is not None, (
"Could not find CSRF token in login page"
)
form_csrf_token = csrf_match.group(1)
# Step 2: Register (auto-logs in the user)
username = f"csrf_e2e_{uuid.uuid4().hex[:12]}"
password = "TestPass123!"
register_response = client.post(
"/auth/register",
data={
"username": username,
"password": password,
"confirm_password": password,
"acknowledge": "true",
"csrf_token": form_csrf_token,
},
follow_redirects=False,
)
assert register_response.status_code == 302, (
f"Registration should redirect, got {register_response.status_code}"
)
# Step 3: Get API CSRF token from /auth/csrf-token
csrf_response = client.get("/auth/csrf-token")
assert csrf_response.status_code == 200
csrf_data = csrf_response.get_json()
assert csrf_data is not None and "csrf_token" in csrf_data
api_csrf_token = csrf_data["csrf_token"]
# Step 4: POST /api/start_research WITH CSRF token — should pass CSRF
response = client.post(
"/api/start_research",
json={"query": "test csrf flow"},
headers={"X-CSRFToken": api_csrf_token},
)
# Expect 200 (success) or 400 from business logic — but not a CSRF rejection
assert response.status_code in (200, 400), (
f"Expected 200 or business-logic 400, got {response.status_code}"
)
if response.status_code == 400:
data = response.get_json()
error_msg = data.get("error", "") if data else ""
assert "csrf" not in error_msg.lower(), (
f"CSRF should have passed but got: {error_msg}"
)
assert "session cookie" not in error_msg.lower(), (
f"CSRF should have passed but got: {error_msg}"
)
# Step 5: POST /api/start_research WITHOUT CSRF token — should be rejected
response = client.post(
"/api/start_research",
json={"query": "test csrf flow"},
)
assert response.status_code == 400
data = response.get_json()
assert data is not None and "error" in data
error_lower = data["error"].lower()
assert "csrf" in error_lower or "session cookie" in error_lower, (
f"Expected CSRF error but got: {data['error']}"
)