forked from claw/flaskpaste
ci: add security headers audit to pipeline
This commit is contained in:
@@ -191,6 +191,9 @@ jobs:
|
||||
- name: Race condition tests
|
||||
run: python tests/security/race_condition_test.py
|
||||
|
||||
- name: Security headers audit
|
||||
run: python tests/security/headers_audit.py
|
||||
|
||||
memory:
|
||||
name: Memory Leak Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -191,7 +191,7 @@ Not tested (no signature defined):
|
||||
[ ] Add remaining MIME test results to security assessment
|
||||
[ ] Document rate limiting behavior under attack
|
||||
[ ] Create threat model diagram
|
||||
[ ] Add security headers audit to CI pipeline
|
||||
[x] Add security headers audit to CI pipeline
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
108
tests/security/headers_audit.py
Normal file
108
tests/security/headers_audit.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Security headers audit for FlaskPaste.
|
||||
|
||||
Verifies all required security headers are present and correctly configured.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, ".")
|
||||
|
||||
from app import create_app
|
||||
|
||||
# Required headers and their expected values
|
||||
REQUIRED_HEADERS = {
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Frame-Options": "DENY",
|
||||
"Content-Security-Policy": "default-src 'none'",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin",
|
||||
"Permissions-Policy": "geolocation=(), microphone=(), camera=()",
|
||||
"Cache-Control": "no-store, no-cache, must-revalidate",
|
||||
"Pragma": "no-cache",
|
||||
}
|
||||
|
||||
# Headers that should NOT be present
|
||||
FORBIDDEN_HEADERS = [
|
||||
"X-Powered-By",
|
||||
"Server",
|
||||
]
|
||||
|
||||
# Endpoints to test
|
||||
TEST_ENDPOINTS = [
|
||||
("/", "GET", 200),
|
||||
("/health", "GET", 200),
|
||||
("/challenge", "GET", 200),
|
||||
("/nonexistent", "GET", 400), # Error response
|
||||
]
|
||||
|
||||
|
||||
def run_audit():
|
||||
"""Run security headers audit."""
|
||||
print("=" * 60)
|
||||
print("SECURITY HEADERS AUDIT")
|
||||
print("=" * 60)
|
||||
|
||||
app = create_app("testing")
|
||||
client = app.test_client()
|
||||
|
||||
results = {"passed": 0, "failed": 0, "warnings": 0}
|
||||
|
||||
for endpoint, method, expected_status in TEST_ENDPOINTS:
|
||||
print(f"\n[{method} {endpoint}]")
|
||||
print("-" * 40)
|
||||
|
||||
if method == "GET":
|
||||
resp = client.get(endpoint)
|
||||
elif method == "POST":
|
||||
resp = client.post(endpoint, data=b"test")
|
||||
else:
|
||||
continue
|
||||
|
||||
# Check required headers
|
||||
for header, expected in REQUIRED_HEADERS.items():
|
||||
actual = resp.headers.get(header, "")
|
||||
if expected in actual:
|
||||
print(f" ✓ {header}")
|
||||
results["passed"] += 1
|
||||
else:
|
||||
print(f" ✗ {header}")
|
||||
print(f" Expected: {expected}")
|
||||
print(f" Got: {actual or '(missing)'}")
|
||||
results["failed"] += 1
|
||||
|
||||
# Check forbidden headers
|
||||
for header in FORBIDDEN_HEADERS:
|
||||
if header in resp.headers:
|
||||
print(f" ⚠ {header} should not be present")
|
||||
results["warnings"] += 1
|
||||
else:
|
||||
print(f" ✓ No {header} header")
|
||||
results["passed"] += 1
|
||||
|
||||
# Check X-Request-ID
|
||||
if "X-Request-ID" in resp.headers:
|
||||
print(" ✓ X-Request-ID present")
|
||||
results["passed"] += 1
|
||||
else:
|
||||
print(" ✗ X-Request-ID missing")
|
||||
results["failed"] += 1
|
||||
|
||||
# Summary
|
||||
print("\n" + "=" * 60)
|
||||
print("SUMMARY")
|
||||
print("=" * 60)
|
||||
print(f" Passed: {results['passed']}")
|
||||
print(f" Failed: {results['failed']}")
|
||||
print(f" Warnings: {results['warnings']}")
|
||||
|
||||
total = results["passed"] + results["failed"]
|
||||
if results["failed"] == 0:
|
||||
print(f"\n{results['passed']}/{total} checks passed")
|
||||
return 0
|
||||
else:
|
||||
print(f"\nFAILED: {results['failed']}/{total} checks failed")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(run_audit())
|
||||
Reference in New Issue
Block a user