java-topology/defects/dogecoin-0001/test/test_dogecoin_0001.py

69 lines
3 KiB
Python

#!/usr/bin/env python3
"""
Unit test for dogecoin-0001: SOCKS5 proxy password logged in plaintext (CWE-312 / MOAD-0004)
Validates that the patched LogPrint line masks the password.
"""
import re
import unittest
# Original line (DEFECTIVE): logs password in plaintext
ORIGINAL_LINE = 'LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\\n", auth->username, auth->password);'
# Patched line: masks the password
PATCHED_LINE = 'LogPrint("proxy", "SOCKS5 sending proxy authentication %s:***\\n", auth->username);'
def read_source(path="src/netbase.cpp"):
"""Read the source file and return the relevant log line."""
try:
with open(path, "r") as f:
for line in f:
if "SOCKS5 sending proxy authentication" in line:
return line.strip()
except FileNotFoundError:
return None
return None
class TestDogecoin0001(unittest.TestCase):
"""Test that proxy password is not logged in plaintext."""
def test_original_line_contains_password_format_specifier(self):
"""The original defective line has two %s format specifiers (username AND password)."""
# Count format specifiers in the format string portion
fmt_match = re.search(r'"SOCKS5 sending proxy authentication ([^"]*)"', ORIGINAL_LINE)
self.assertIsNotNone(fmt_match, "Could not find format string in original line")
fmt_str = fmt_match.group(1)
specifier_count = fmt_str.count("%s")
self.assertEqual(specifier_count, 2, "Original line should have 2 format specifiers (username + password)")
def test_patched_line_masks_password(self):
"""The patched line should only have one %s (username) and mask the password with ***."""
fmt_match = re.search(r'"SOCKS5 sending proxy authentication ([^"]*)"', PATCHED_LINE)
self.assertIsNotNone(fmt_match, "Could not find format string in patched line")
fmt_str = fmt_match.group(1)
specifier_count = fmt_str.count("%s")
self.assertEqual(specifier_count, 1, "Patched line should have only 1 format specifier (username only)")
self.assertIn("***", fmt_str, "Patched line should mask password with ***")
def test_patched_line_does_not_reference_auth_password(self):
"""The patched line should not reference auth->password at all."""
self.assertNotIn("auth->password", PATCHED_LINE,
"Patched line must not reference auth->password")
def test_patched_line_preserves_username(self):
"""The patched line should still log the username for diagnostic purposes."""
self.assertIn("auth->username", PATCHED_LINE,
"Patched line should still reference auth->username")
def test_original_line_exposes_password(self):
"""The original line references auth->password, exposing it to logs."""
self.assertIn("auth->password", ORIGINAL_LINE,
"Original line should reference auth->password (confirming the defect)")
if __name__ == "__main__":
unittest.main()