java-topology/defects/linapple-0001/test/test_linapple_0001.py
russell@unturf.com 20b8a57f89 fuse: all 5 MOADs CLEAN
linapple: 1 CWE-312 defect (MOAD-0004), MOAD 0001/0002/0003/0005 CLEAN
2026-03-31 19:15:33 -04:00

95 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""
Unit test for linapple-0001: FTP user:password credential logged verbatim (CWE-312 / MOAD-0004)
LoadConfiguration() in src/Applewin.cpp reads g_sFTPUserPass (format: "user:password")
from config and immediately prints it verbatim via printf to stdout at every startup.
Fix: extract only the username portion; mask the password with ***.
"""
import re
import unittest
# Original defective line
ORIGINAL_LINE = 'printf("Ready login = %s\\n", g_sFTPUserPass);'
# Patched lines (the masked version)
PATCHED_FMT = 'printf("Ready login = %.*s:***\\n", user_len, g_sFTPUserPass);'
def mask_userpass(userpass: str) -> str:
"""Simulate the patched masking logic in C."""
colon = userpass.find(':')
if colon >= 0:
return userpass[:colon] + ':***'
return userpass + ':***'
def original_leak(userpass: str) -> str:
"""Simulate the original defective print."""
return userpass
class TestLinapple0001(unittest.TestCase):
"""Verify that FTP password is not logged in plaintext."""
def test_original_exposes_full_credential(self):
"""Original printf prints user:password verbatim."""
cred = "alice:s3cr3tpass"
output = original_leak(cred)
self.assertIn("s3cr3tpass", output,
"Defect: password visible in original output")
# Confirm the source line uses %s with full g_sFTPUserPass
self.assertIn("%s", ORIGINAL_LINE)
self.assertIn("g_sFTPUserPass", ORIGINAL_LINE)
def test_patch_masks_password(self):
"""Patched version must show only username, not password."""
cred = "alice:s3cr3tpass"
output = mask_userpass(cred)
self.assertEqual(output, "alice:***",
"Fix: output should be user:***")
self.assertNotIn("s3cr3tpass", output,
"Fix: password must not appear in output")
def test_patch_preserves_username(self):
"""Username must remain visible for diagnostics."""
cred = "bob:hunter2"
output = mask_userpass(cred)
self.assertTrue(output.startswith("bob:"),
"Fix: username should be preserved")
def test_patch_handles_no_colon(self):
"""If no colon, treat entire string as username."""
cred = "anonymous"
output = mask_userpass(cred)
self.assertIn("anonymous", output)
self.assertIn("***", output)
def test_patch_handles_anonymous_default(self):
"""Default anonymous:mymail@hotmail.com must have email masked."""
cred = "anonymous:mymail@hotmail.com"
output = mask_userpass(cred)
self.assertNotIn("mymail@hotmail.com", output,
"Fix: email used as password must not appear in output")
self.assertEqual(output, "anonymous:***")
def test_patched_fmt_string_uses_precision(self):
"""Patched printf uses %%.*s to limit output to username length."""
self.assertIn("%.*s", PATCHED_FMT,
"Patched line should use %%.*s for length-limited username")
self.assertIn(":***", PATCHED_FMT,
"Patched line should contain literal :*** mask")
def test_original_format_string_leaks(self):
"""Original format string has single %%s — prints entire user:password."""
fmt_match = re.search(r'"Ready login = ([^"]*)"', ORIGINAL_LINE)
self.assertIsNotNone(fmt_match)
fmt_str = fmt_match.group(1)
self.assertEqual(fmt_str.count("%s"), 1,
"Original has one %%s which prints entire credential string")
if __name__ == "__main__":
unittest.main()