7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
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
347 lines
12 KiB
Python
347 lines
12 KiB
Python
"""
|
|
API Security Tests
|
|
|
|
Tests that verify API endpoints follow security best practices,
|
|
based on OWASP API Security Top 10.
|
|
"""
|
|
|
|
import pytest
|
|
from tests.test_utils import add_src_to_path
|
|
|
|
add_src_to_path()
|
|
|
|
|
|
class TestAPISecurityOWASPTop10:
|
|
"""Test API security based on OWASP API Security Top 10 2023."""
|
|
|
|
@pytest.fixture
|
|
def client(self):
|
|
"""Create a test client."""
|
|
from local_deep_research.web.app import create_app
|
|
|
|
app, _ = create_app() # Unpack tuple (app, socket_service)
|
|
app.config["TESTING"] = True
|
|
app.config["WTF_CSRF_ENABLED"] = False
|
|
# Enable CORS for testing (tests expect open CORS)
|
|
app.config["SECURITY_CORS_ALLOWED_ORIGINS"] = "*"
|
|
return app.test_client()
|
|
|
|
# API1:2023 - Broken Object Level Authorization (BOLA)
|
|
@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
|
|
def test_api1_broken_object_level_authorization(self, client):
|
|
"""
|
|
Test that users can only access their own objects.
|
|
|
|
BOLA/IDOR (Insecure Direct Object Reference):
|
|
- User A tries to access User B's research by changing research_id
|
|
- API should verify that user owns the requested object
|
|
"""
|
|
# Example vulnerable endpoint:
|
|
# GET /api/v1/quick_summary/{research_id}
|
|
# Without checking if current user owns research_id
|
|
|
|
# Test accessing research with different IDs
|
|
# Should return 403 Forbidden if not owned by user
|
|
# Should return 404 Not Found to avoid info leakage
|
|
|
|
# For LDR with per-user databases, this is mitigated by architecture
|
|
assert True # Architecture-level protection
|
|
|
|
# API2:2023 - Broken Authentication
|
|
def test_api2_broken_authentication(self, client):
|
|
"""
|
|
Test that API authentication is secure.
|
|
|
|
Common issues:
|
|
- Weak password requirements
|
|
- Credential stuffing
|
|
- Missing rate limiting
|
|
- Weak token generation
|
|
"""
|
|
# Test that protected API endpoints require authentication
|
|
protected_endpoints = [
|
|
"/api/v1/quick_summary",
|
|
"/api/v1/settings",
|
|
]
|
|
|
|
for endpoint in protected_endpoints:
|
|
# Try accessing without authentication
|
|
response = client.get(endpoint)
|
|
# Should be one of the auth-block codes:
|
|
# 401 Unauthorized, 403 Forbidden, 404 Not Found (route hidden),
|
|
# 405 Method Not Allowed (endpoint only accepts POST).
|
|
# MUST NOT be 200 — that would mean the endpoint is reachable
|
|
# without authentication, which is what this OWASP API2 test
|
|
# exists to catch.
|
|
assert response.status_code in {401, 403, 404, 405}, (
|
|
f"{endpoint} returned {response.status_code} without auth"
|
|
)
|
|
|
|
# API4:2023 - Unrestricted Resource Consumption
|
|
def test_api4_unrestricted_resource_consumption(self, client):
|
|
"""
|
|
Test protection against resource exhaustion attacks.
|
|
|
|
Attack vectors:
|
|
- Extremely large requests
|
|
- Too many simultaneous requests
|
|
- Expensive operations without limits
|
|
"""
|
|
# Test large payload
|
|
large_query = "test query " * 100000 # Very large query
|
|
response = client.post(
|
|
"/api/v1/quick_summary",
|
|
json={"query": large_query},
|
|
content_type="application/json",
|
|
)
|
|
|
|
# Should have request size limit (413 Payload Too Large)
|
|
# Or validate query length (400/422)
|
|
# Current implementation may return 500 for edge cases
|
|
assert response.status_code == 401, response.status_code
|
|
|
|
# Test rate limiting (if implemented)
|
|
# Make many requests rapidly
|
|
for i in range(100):
|
|
response = client.get("/api/v1/health")
|
|
# Should eventually rate limit (429 Too Many Requests)
|
|
|
|
# Rate limiting may not be enabled in development
|
|
pass
|
|
|
|
# API5:2023 - Broken Function Level Authorization
|
|
def test_api5_broken_function_level_authorization(self, client):
|
|
"""
|
|
Test that administrative functions require admin privileges.
|
|
|
|
Common issues:
|
|
- Admin endpoints accessible to regular users
|
|
- Missing authorization checks on sensitive functions
|
|
"""
|
|
# Admin functions that should require elevated privileges:
|
|
admin_endpoints = [
|
|
"/api/v1/admin/users",
|
|
"/api/v1/admin/settings",
|
|
"/api/v1/admin/logs",
|
|
]
|
|
|
|
for endpoint in admin_endpoints:
|
|
response = client.get(endpoint)
|
|
# Should return 401 Unauthorized, 403 Forbidden, or 404 Not Found
|
|
assert response.status_code == 404, response.status_code
|
|
|
|
# API6:2023 - Unrestricted Access to Sensitive Business Flows
|
|
@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
|
|
def test_api6_unrestricted_sensitive_flows(self, client):
|
|
"""
|
|
Test protection of sensitive business logic flows.
|
|
|
|
Examples:
|
|
- Account deletion without verification
|
|
- Mass data export without limits
|
|
- Automated scraping/abuse
|
|
"""
|
|
# For LDR, sensitive flows might include:
|
|
# - Deleting all research history
|
|
# - Exporting all data
|
|
# - Automated research generation (resource intensive)
|
|
|
|
# These should have:
|
|
# - Confirmation required
|
|
# - Rate limiting
|
|
# - CAPTCHA for automated abuse prevention
|
|
|
|
pass # Implementation-specific
|
|
|
|
# API8:2023 - Security Misconfiguration
|
|
def test_api8_security_misconfiguration(self, client):
|
|
"""
|
|
Test for common security misconfigurations.
|
|
|
|
Common issues:
|
|
- Debug mode enabled in production
|
|
- Verbose error messages
|
|
- Missing security headers
|
|
- Default credentials
|
|
"""
|
|
# Test that debug mode is off
|
|
response = client.get("/api/v1/health")
|
|
data = response.get_json()
|
|
|
|
# Should not expose debug information
|
|
assert "debug" not in str(data).lower() or not data.get("debug")
|
|
|
|
# Test error responses don't leak sensitive info
|
|
response = client.get("/api/v1/nonexistent")
|
|
# Should return generic error, not stack trace
|
|
# 401 is also acceptable (auth guard rejects before routing)
|
|
assert response.status_code == 404, response.status_code
|
|
|
|
# API9:2023 - Improper Inventory Management
|
|
@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
|
|
def test_api9_improper_inventory_management(self):
|
|
"""
|
|
Test API documentation and version management.
|
|
|
|
Issues:
|
|
- Undocumented API endpoints
|
|
- Old API versions still accessible
|
|
- Deprecated endpoints without sunset dates
|
|
- Shadow APIs (forgotten endpoints)
|
|
"""
|
|
# This is primarily a documentation/process issue
|
|
# Verify:
|
|
# - API endpoints are documented
|
|
# - Old versions are deprecated properly
|
|
# - API versioning is clear (/api/v1/, /api/v2/)
|
|
|
|
assert True # Documentation/process test
|
|
|
|
# API10:2023 - Unsafe Consumption of APIs
|
|
@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
|
|
def test_api10_unsafe_consumption_of_apis(self, client):
|
|
"""
|
|
Test secure consumption of external APIs.
|
|
|
|
LDR consumes external APIs:
|
|
- Search engines
|
|
- Wikipedia
|
|
- Web scraping targets
|
|
|
|
Risks:
|
|
- Malicious responses from external APIs
|
|
- Injection attacks via external data
|
|
- Excessive trust in external data
|
|
"""
|
|
# External API responses should be:
|
|
# - Validated (schema/type checking)
|
|
# - Sanitized (remove dangerous content)
|
|
# - Size-limited (prevent memory exhaustion)
|
|
# - Timeout-protected (prevent hanging)
|
|
|
|
assert True # Implementation-specific
|
|
|
|
|
|
class TestAPIInputValidation:
|
|
"""Test API input validation."""
|
|
|
|
@pytest.fixture
|
|
def client(self):
|
|
"""Create a test client."""
|
|
from local_deep_research.web.app import create_app
|
|
|
|
app, _ = create_app() # Unpack tuple (app, socket_service)
|
|
app.config["TESTING"] = True
|
|
app.config["WTF_CSRF_ENABLED"] = False
|
|
# Enable CORS for testing (tests expect open CORS)
|
|
app.config["SECURITY_CORS_ALLOWED_ORIGINS"] = "*"
|
|
return app.test_client()
|
|
|
|
def test_json_parsing_errors_handled(self, client):
|
|
"""Test that malformed JSON is rejected gracefully."""
|
|
# Send invalid JSON
|
|
response = client.post(
|
|
"/api/v1/quick_summary",
|
|
data="{ invalid json }",
|
|
content_type="application/json",
|
|
)
|
|
|
|
# Should return 400 Bad Request (or 401 if auth guard rejects first)
|
|
assert response.status_code == 401, response.status_code
|
|
|
|
def test_missing_required_fields_rejected(self, client):
|
|
"""Test that requests with missing required fields are rejected."""
|
|
# Send request without required field
|
|
response = client.post(
|
|
"/api/v1/quick_summary",
|
|
json={}, # Missing 'query' field
|
|
content_type="application/json",
|
|
)
|
|
|
|
# Should return 400 Bad Request or 422 Unprocessable Entity
|
|
# 401 is also valid (auth guard rejects unauthenticated requests)
|
|
assert response.status_code == 401, response.status_code
|
|
|
|
def test_invalid_data_types_rejected(self, client):
|
|
"""Test that invalid data types are rejected."""
|
|
# Send wrong data type
|
|
response = client.post(
|
|
"/api/v1/quick_summary",
|
|
json={"query": 12345}, # Should be string, not number
|
|
content_type="application/json",
|
|
)
|
|
|
|
# Should validate data types and reject non-string query
|
|
# 401 is also valid (auth guard rejects unauthenticated requests)
|
|
assert response.status_code == 401, response.status_code
|
|
|
|
def test_boundary_value_validation(self, client):
|
|
"""Test validation of boundary values."""
|
|
boundary_tests = [
|
|
{"query": ""}, # Empty string
|
|
{"query": "a" * 10000}, # Very long string
|
|
{"query": None}, # Null value
|
|
]
|
|
|
|
for test_data in boundary_tests:
|
|
response = client.post(
|
|
"/api/v1/quick_summary",
|
|
json=test_data,
|
|
content_type="application/json",
|
|
)
|
|
|
|
# Should validate and reject invalid inputs (400/422)
|
|
# 401 is also valid (auth guard rejects unauthenticated requests)
|
|
assert response.status_code == 401, response.status_code
|
|
|
|
|
|
class TestAPIRateLimiting:
|
|
"""Test API rate limiting (if implemented)."""
|
|
|
|
@pytest.fixture
|
|
def client(self):
|
|
"""Create a test client."""
|
|
from local_deep_research.web.app import create_app
|
|
|
|
app, _ = create_app() # Unpack tuple (app, socket_service)
|
|
app.config["TESTING"] = True
|
|
# Enable CORS for testing (tests expect open CORS)
|
|
app.config["SECURITY_CORS_ALLOWED_ORIGINS"] = "*"
|
|
return app.test_client()
|
|
|
|
|
|
@pytest.mark.skip(reason="documentation/placeholder test - not implemented")
|
|
def test_api_security_documentation():
|
|
"""
|
|
Documentation test for API security best practices.
|
|
|
|
OWASP API Security Top 10 2023:
|
|
1. Broken Object Level Authorization (BOLA)
|
|
2. Broken Authentication
|
|
3. Broken Object Property Level Authorization
|
|
4. Unrestricted Resource Consumption
|
|
5. Broken Function Level Authorization
|
|
6. Unrestricted Access to Sensitive Business Flows
|
|
7. Server Side Request Forgery (SSRF)
|
|
8. Security Misconfiguration
|
|
9. Improper Inventory Management
|
|
10. Unsafe Consumption of APIs
|
|
|
|
LDR-Specific API Security Considerations:
|
|
- Research API endpoints handle user queries
|
|
- External data fetching (SSRF risk)
|
|
- Resource-intensive operations (DoS risk)
|
|
- Per-user database isolation (BOLA mitigation)
|
|
|
|
Recommended Security Controls:
|
|
- Input validation on all API endpoints
|
|
- Rate limiting on expensive operations
|
|
- URL whitelist for external fetching
|
|
- Request size limits
|
|
- Proper error handling (no info leakage)
|
|
- API versioning and documentation
|
|
- Authentication on protected endpoints
|
|
- Authorization checks on object access
|
|
"""
|
|
assert True # Documentation test
|