forked from username/flaskpaste
tests: fix ruff lint errors in security tests
This commit is contained in:
@@ -10,7 +10,7 @@ from pathlib import Path
|
|||||||
# Load fpaste as a module by exec
|
# Load fpaste as a module by exec
|
||||||
fpaste_path = Path("/home/user/git/flaskpaste/fpaste")
|
fpaste_path = Path("/home/user/git/flaskpaste/fpaste")
|
||||||
fpaste_globals = {"__name__": "fpaste", "__file__": str(fpaste_path)}
|
fpaste_globals = {"__name__": "fpaste", "__file__": str(fpaste_path)}
|
||||||
exec(compile(fpaste_path.read_text(), fpaste_path, "exec"), fpaste_globals)
|
exec(compile(fpaste_path.read_text(), fpaste_path, "exec"), fpaste_globals) # noqa: S102
|
||||||
|
|
||||||
# Import from loaded module
|
# Import from loaded module
|
||||||
TRUSTED_CLIPBOARD_DIRS = fpaste_globals["TRUSTED_CLIPBOARD_DIRS"]
|
TRUSTED_CLIPBOARD_DIRS = fpaste_globals["TRUSTED_CLIPBOARD_DIRS"]
|
||||||
@@ -39,10 +39,10 @@ def test_trusted_path_validation():
|
|||||||
|
|
||||||
# Test untrusted paths
|
# Test untrusted paths
|
||||||
untrusted_tests = [
|
untrusted_tests = [
|
||||||
("/tmp/xclip", False, "tmp directory"),
|
("/tmp/xclip", False, "tmp directory"), # noqa: S108
|
||||||
("/home/user/bin/xclip", False, "user bin"),
|
("/home/user/bin/xclip", False, "user bin"),
|
||||||
("./xclip", False, "current directory"),
|
("./xclip", False, "current directory"),
|
||||||
("/var/tmp/malicious", False, "var tmp"),
|
("/var/tmp/malicious", False, "var tmp"), # noqa: S108
|
||||||
("/home/attacker/.local/bin/xclip", False, "user local"),
|
("/home/attacker/.local/bin/xclip", False, "user local"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ def test_path_injection():
|
|||||||
print("=" * 50)
|
print("=" * 50)
|
||||||
|
|
||||||
# Create a malicious "xclip" in /tmp
|
# Create a malicious "xclip" in /tmp
|
||||||
malicious_path = Path("/tmp/xclip")
|
malicious_path = Path("/tmp/xclip") # noqa: S108
|
||||||
try:
|
try:
|
||||||
malicious_path.write_text("#!/bin/sh\necho 'PWNED' > /tmp/pwned\n")
|
malicious_path.write_text("#!/bin/sh\necho 'PWNED' > /tmp/pwned\n")
|
||||||
malicious_path.chmod(0o755)
|
malicious_path.chmod(0o755)
|
||||||
@@ -73,7 +73,7 @@ def test_path_injection():
|
|||||||
original_path = os.environ.get("PATH", "")
|
original_path = os.environ.get("PATH", "")
|
||||||
|
|
||||||
# Prepend /tmp to PATH (attacker-controlled)
|
# Prepend /tmp to PATH (attacker-controlled)
|
||||||
os.environ["PATH"] = f"/tmp:{original_path}"
|
os.environ["PATH"] = f"/tmp:{original_path}" # noqa: S108
|
||||||
|
|
||||||
# Try to find clipboard command
|
# Try to find clipboard command
|
||||||
cmd = find_clipboard_command(CLIPBOARD_READ_COMMANDS)
|
cmd = find_clipboard_command(CLIPBOARD_READ_COMMANDS)
|
||||||
@@ -86,7 +86,7 @@ def test_path_injection():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
# Check if it's using the malicious path
|
# Check if it's using the malicious path
|
||||||
if cmd[0] == str(malicious_path) or cmd[0] == "/tmp/xclip":
|
if cmd[0] == str(malicious_path) or cmd[0] == "/tmp/xclip": # noqa: S108
|
||||||
print(" FAIL: Malicious /tmp/xclip was selected!")
|
print(" FAIL: Malicious /tmp/xclip was selected!")
|
||||||
print(f" Command: {cmd}")
|
print(f" Command: {cmd}")
|
||||||
return False
|
return False
|
||||||
@@ -125,8 +125,8 @@ def test_subprocess_safety():
|
|||||||
# Check subprocess.run uses list
|
# Check subprocess.run uses list
|
||||||
run_calls = re.findall(r"subprocess\.run\(([^)]+)\)", content)
|
run_calls = re.findall(r"subprocess\.run\(([^)]+)\)", content)
|
||||||
for call in run_calls:
|
for call in run_calls:
|
||||||
if not call.strip().startswith("[") and not call.strip().startswith("cmd"):
|
stripped = call.strip()
|
||||||
if "cmd" not in call: # Allow variable names like 'cmd'
|
if not stripped.startswith("[") and not stripped.startswith("cmd") and "cmd" not in call:
|
||||||
issues.append(f"Possible string command in subprocess.run: {call[:50]}")
|
issues.append(f"Possible string command in subprocess.run: {call[:50]}")
|
||||||
|
|
||||||
if issues:
|
if issues:
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ def test_antiflood_memory():
|
|||||||
max_entries = app.config.get("ANTIFLOOD_MAX_ENTRIES", 10000)
|
max_entries = app.config.get("ANTIFLOOD_MAX_ENTRIES", 10000)
|
||||||
print(f" Max entries config: {max_entries}")
|
print(f" Max entries config: {max_entries}")
|
||||||
|
|
||||||
for i in range(20000):
|
for _ in range(20000):
|
||||||
record_antiflood_request()
|
record_antiflood_request()
|
||||||
|
|
||||||
list_size = len(_antiflood_requests)
|
list_size = len(_antiflood_requests)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def run_audit():
|
|||||||
|
|
||||||
results = {"passed": 0, "failed": 0, "warnings": 0}
|
results = {"passed": 0, "failed": 0, "warnings": 0}
|
||||||
|
|
||||||
for endpoint, method, expected_status in TEST_ENDPOINTS:
|
for endpoint, method, _expected_status in TEST_ENDPOINTS:
|
||||||
print(f"\n[{method} {endpoint}]")
|
print(f"\n[{method} {endpoint}]")
|
||||||
print("-" * 40)
|
print("-" * 40)
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ BASE_URL = "http://127.0.0.1:5099"
|
|||||||
def request(url, method="GET", data=None, headers=None):
|
def request(url, method="GET", data=None, headers=None):
|
||||||
"""Make HTTP request."""
|
"""Make HTTP request."""
|
||||||
headers = headers or {}
|
headers = headers or {}
|
||||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
req = urllib.request.Request(url, data=data, headers=headers, method=method) # noqa: S310
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310
|
||||||
return resp.status, resp.read(), dict(resp.headers)
|
return resp.status, resp.read(), dict(resp.headers)
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
return e.code, e.read(), dict(e.headers)
|
return e.code, e.read(), dict(e.headers)
|
||||||
@@ -86,7 +86,7 @@ def run_tests():
|
|||||||
print("\n[Phase 1] Reconnaissance")
|
print("\n[Phase 1] Reconnaissance")
|
||||||
print("-" * 40)
|
print("-" * 40)
|
||||||
|
|
||||||
status, body, headers = request(f"{BASE_URL}/")
|
status, body, _ = request(f"{BASE_URL}/")
|
||||||
log_test("GET / returns API info", status == 200)
|
log_test("GET / returns API info", status == 200)
|
||||||
|
|
||||||
status, body, _ = request(f"{BASE_URL}/health")
|
status, body, _ = request(f"{BASE_URL}/health")
|
||||||
@@ -225,7 +225,7 @@ def run_tests():
|
|||||||
status, body, _ = request(f"{BASE_URL}/", "POST", payload.encode(), pow_headers)
|
status, body, _ = request(f"{BASE_URL}/", "POST", payload.encode(), pow_headers)
|
||||||
if status == 201:
|
if status == 201:
|
||||||
data = json.loads(body)
|
data = json.loads(body)
|
||||||
status2, content, _ = request(f"{BASE_URL}/{data['id']}/raw")
|
_, content, _ = request(f"{BASE_URL}/{data['id']}/raw")
|
||||||
log_test("SSTI payload stored safely", b"49" not in content)
|
log_test("SSTI payload stored safely", b"49" not in content)
|
||||||
paste_ids.append(data["id"])
|
paste_ids.append(data["id"])
|
||||||
pow_headers = get_pow_headers()
|
pow_headers = get_pow_headers()
|
||||||
@@ -233,7 +233,7 @@ def run_tests():
|
|||||||
# XSS in content
|
# XSS in content
|
||||||
xss_payload = b"<script>alert('xss')</script>"
|
xss_payload = b"<script>alert('xss')</script>"
|
||||||
pow_headers = get_pow_headers()
|
pow_headers = get_pow_headers()
|
||||||
status, body, headers = request(f"{BASE_URL}/", "POST", xss_payload, pow_headers)
|
status, body, _ = request(f"{BASE_URL}/", "POST", xss_payload, pow_headers)
|
||||||
if status == 201:
|
if status == 201:
|
||||||
data = json.loads(body)
|
data = json.loads(body)
|
||||||
status, content, resp_headers = request(f"{BASE_URL}/{data['id']}/raw")
|
status, content, resp_headers = request(f"{BASE_URL}/{data['id']}/raw")
|
||||||
@@ -261,11 +261,9 @@ def run_tests():
|
|||||||
print("-" * 40)
|
print("-" * 40)
|
||||||
|
|
||||||
# Make many rapid requests
|
# Make many rapid requests
|
||||||
hit_limit = False
|
for _ in range(100):
|
||||||
for i in range(100):
|
|
||||||
status, _, _ = request(f"{BASE_URL}/health")
|
status, _, _ = request(f"{BASE_URL}/health")
|
||||||
if status == 429:
|
if status == 429:
|
||||||
hit_limit = True
|
|
||||||
break
|
break
|
||||||
log_test("Rate limiting active on reads", True) # May or may not hit
|
log_test("Rate limiting active on reads", True) # May or may not hit
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ from app import create_app
|
|||||||
|
|
||||||
# Global profiler
|
# Global profiler
|
||||||
profiler = cProfile.Profile()
|
profiler = cProfile.Profile()
|
||||||
profile_output = "/tmp/flaskpaste_profile.prof"
|
profile_output = "/tmp/flaskpaste_profile.prof" # noqa: S108
|
||||||
stats_output = "/tmp/flaskpaste_profile_stats.txt"
|
stats_output = "/tmp/flaskpaste_profile_stats.txt" # noqa: S108
|
||||||
|
|
||||||
|
|
||||||
def save_profile():
|
def save_profile():
|
||||||
|
|||||||
Reference in New Issue
Block a user