java-topology/defects/kronos-0002/test/KronosNetlinkPasswordLogTest.java
russell@unturf.com f30b6bdb52 kronos: 2 CWE-407/CWE-312 defects; mesen-s: all 5 MOADs CLEAN
kronos-0001 (CWE-407, MEDIUM): SH2HandleBreakpoints() in sh2core.h
  linearly scans codebreakpoint[] on every SH2 instruction fetch in
  debug interpreter. MAX_BREAKPOINTS=10, O(10) per fetch at 28.6 MHz
  emulated = 286M extra comparisons/s. Fix: sorted_bp_addrs[] +
  binary search, O(log N), 2.54x fewer comparisons measured.

kronos-0002 (CWE-312, LOW): netlink.c:553 logs password response
  verbatim via NETLINK_LOG when compiled with -DNETLINK_DEBUG.
  Fix: replace %s format with literal [REDACTED].

kronos MOAD-0002/0003/0005: CLEAN
mesen-s: all 5 MOADs CLEAN (CheatManager unordered_map O(1),
  BreakpointManager guarded by _hasBreakpoint fast-path,
  password hashed before network use, no TLS credential leakage)

8/8 tests PASS
2026-03-31 19:56:06 -04:00

144 lines
6 KiB
Java

/**
* KronosNetlinkPasswordLogTest — kronos-0002 (CWE-312)
*
* Defect: yabause/src/utils/src/netlink.c, line 553
*
* NETLINK_LOG("password response: %s",
* NetlinkArea->inbuffer + NetlinkArea->inbufferstart);
*
* When compiled with -DNETLINK_DEBUG, NETLINK_LOG expands to:
* DebugPrintf(MainLog, __FILE__, __LINE__, fmt, ...)
* which writes to the main debug log file (DEBUG_STDOUT or a named file).
* Our Saturn modem internet-login password is written verbatim to disk.
*
* Line 543 also logs the login name verbatim ("login response: %s").
*
* Fix: replace the %s format with a literal "[REDACTED]" so the credential
* never enters the log:
* NETLINK_LOG("password response: [REDACTED]");
*
* This test models a credential-denylist log formatter and confirms:
* 1. The vulnerable pattern exposes the secret.
* 2. The patched pattern suppresses the secret.
* 3. Non-credential log lines pass through unchanged.
*/
public class KronosNetlinkPasswordLogTest {
// -----------------------------------------------------------------------
// Log formatter model
// -----------------------------------------------------------------------
/** Vulnerable: passes format+args through, like the original NETLINK_LOG. */
static String vulnerableLog(String fmt, Object... args) {
return String.format(fmt, args);
}
/**
* Patched: credential denylist applied at serialization.
* Any message matching "password response:" has its suffix replaced.
* This simulates the fix: the call site produces only the literal string,
* but a defensive serializer layer also guards against future regressions.
*/
static String patchedLog(String fmt, Object... args) {
String raw = String.format(fmt, args);
String marker = "password response:";
int idx = raw.indexOf(marker);
if (idx >= 0) {
return raw.substring(0, idx + marker.length()) + " [REDACTED]";
}
return raw;
}
// -----------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------
static void check(boolean cond, String msg) {
if (!cond) throw new AssertionError("FAIL: " + msg);
}
// -----------------------------------------------------------------------
// Test cases
// -----------------------------------------------------------------------
static void testVulnerableExposesPassword() {
String password = "s3cr3tPassw0rd!";
String line = vulnerableLog("password response: %s", password);
check(line.contains(password),
"vulnerable logger should expose the password in log line");
System.out.println("PASS testVulnerableExposesPassword");
}
static void testPatchedRedactsPassword() {
String password = "s3cr3tPassw0rd!";
// Patched call site: no %s argument — literal string only.
String line = patchedLog("password response: [REDACTED]");
check(!line.contains(password),
"patched logger must not contain the actual password");
check(line.contains("[REDACTED]"),
"patched logger must contain [REDACTED] marker");
System.out.println("PASS testPatchedRedactsPassword");
}
static void testNonPasswordLinePassesThrough() {
String username = "myusername";
String line = patchedLog("login response: %s", username);
check(line.contains(username),
"non-password messages pass through unmodified");
System.out.println("PASS testNonPasswordLinePassesThrough");
}
static void testShellResponseNotRedacted() {
// "shell response:" is the post-auth shell prompt echo, not a credential
String line = patchedLog("shell response: %s", "$ ");
check(line.contains("$ "),
"shell response is not a credential — passes through");
System.out.println("PASS testShellResponseNotRedacted");
}
static void testEmptyPassword() {
// Empty password still must not appear verbatim after "password response:"
String line = patchedLog("password response: [REDACTED]");
check(line.contains("[REDACTED]"), "REDACTED marker present for empty password");
System.out.println("PASS testEmptyPassword");
}
static void testMultiplePasswordsAllRedacted() {
String[] passwords = {"hunter2", "correcthorsebatterystaple", "Pa$$w0rd1"};
for (String pwd : passwords) {
String vulnerable = vulnerableLog("password response: %s", pwd);
String patched = patchedLog("password response: [REDACTED]");
check(vulnerable.contains(pwd),
"vulnerable exposes: " + pwd);
check(!patched.contains(pwd),
"patched suppresses: " + pwd);
}
System.out.println("PASS testMultiplePasswordsAllRedacted: 3 passwords all suppressed");
}
static void testDebugOnlyScope() {
// The defect only activates when NETLINK_DEBUG is compiled in.
// In production builds NETLINK_LOG is a no-op.
// Confirm the patched literal produces no sensitive content even if active.
String line = patchedLog("password response: [REDACTED]");
// Must not contain any printable credential — only the marker
check(line.trim().endsWith("[REDACTED]"),
"patched line ends with exactly [REDACTED], nothing after");
System.out.println("PASS testDebugOnlyScope");
}
// -----------------------------------------------------------------------
// Entry point
// -----------------------------------------------------------------------
public static void main(String[] args) {
testVulnerableExposesPassword();
testPatchedRedactsPassword();
testNonPasswordLinePassesThrough();
testShellResponseNotRedacted();
testEmptyPassword();
testMultiplePasswordsAllRedacted();
testDebugOnlyScope();
System.out.println("ALL TESTS PASSED");
}
}