yabause: 1 CWE-312 defect, MOAD 0001/0002/0003/0005 CLEAN vita3k-0001: unit test added for pre-existing CWE-407 patch
217 lines
7.8 KiB
Java
217 lines
7.8 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Unit test for yabause CWE-312 defect yabause-0001.
|
|
*
|
|
* yabause-0001: In yabause/src/netlink.c, NetlinkWriteByte() handles the
|
|
* Saturn NetLink modem dial-up login sequence. When NETLINK_DEBUG is
|
|
* compiled in, two NETLINK_LOG calls write the Saturn internet credentials
|
|
* verbatim into the debug log:
|
|
*
|
|
* Line 582: NETLINK_LOG("login response: %s", inbuffer+inbufferstart)
|
|
* Line 592: NETLINK_LOG("password response: %s", inbuffer+inbufferstart)
|
|
*
|
|
* This test models the C logging logic and verifies the redaction fix.
|
|
*/
|
|
public class YabauseNetlinkCredentialLogTest {
|
|
|
|
/** Connection state machine, mirroring NL_CONNECTSTATUS_* enum. */
|
|
enum ConnectStatus {
|
|
IDLE, LOGIN1, LOGIN2, LOGIN3, CONNECTED
|
|
}
|
|
|
|
/** Simulates the NETLINK_LOG debug output sink. */
|
|
static class NetlinkLog {
|
|
private final List<String> entries = new ArrayList<>();
|
|
|
|
void log(String msg) {
|
|
entries.add(msg);
|
|
}
|
|
|
|
boolean anyEntryContains(String substring) {
|
|
for (String e : entries) {
|
|
if (e.contains(substring)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
List<String> getEntries() {
|
|
return Collections.unmodifiableList(entries);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// DEFECTIVE: logs login name and password verbatim
|
|
// ---------------------------------------------------------------
|
|
|
|
static ConnectStatus defectiveHandleCarriageReturn(
|
|
NetlinkLog log, ConnectStatus status, String inbuffer) {
|
|
|
|
if (status == ConnectStatus.LOGIN1) {
|
|
// Line 582 — logs login name verbatim
|
|
log.log(String.format("login response: %s", inbuffer));
|
|
return ConnectStatus.LOGIN2;
|
|
}
|
|
if (status == ConnectStatus.LOGIN2) {
|
|
// Line 592 — logs password verbatim
|
|
log.log(String.format("password response: %s", inbuffer));
|
|
return ConnectStatus.LOGIN3;
|
|
}
|
|
if (status == ConnectStatus.LOGIN3) {
|
|
log.log(String.format("shell response: %s", inbuffer));
|
|
return ConnectStatus.CONNECTED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// FIXED: redacts credentials, logs byte count only
|
|
// ---------------------------------------------------------------
|
|
|
|
static ConnectStatus fixedHandleCarriageReturn(
|
|
NetlinkLog log, ConnectStatus status, String inbuffer) {
|
|
|
|
if (status == ConnectStatus.LOGIN1) {
|
|
// Patched line 582: redacted
|
|
log.log(String.format("login response: [REDACTED %d bytes]",
|
|
inbuffer.length()));
|
|
return ConnectStatus.LOGIN2;
|
|
}
|
|
if (status == ConnectStatus.LOGIN2) {
|
|
// Patched line 592: redacted
|
|
log.log(String.format("password response: [REDACTED %d bytes]",
|
|
inbuffer.length()));
|
|
return ConnectStatus.LOGIN3;
|
|
}
|
|
if (status == ConnectStatus.LOGIN3) {
|
|
log.log(String.format("shell response: %s", inbuffer));
|
|
return ConnectStatus.CONNECTED;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
// Test cases
|
|
// ---------------------------------------------------------------
|
|
|
|
static class TestCase {
|
|
final String loginName;
|
|
final String password;
|
|
|
|
TestCase(String loginName, String password) {
|
|
this.loginName = loginName;
|
|
this.password = password;
|
|
}
|
|
}
|
|
|
|
static final TestCase[] CASES = {
|
|
new TestCase("saturn_user", "hunter2"),
|
|
new TestCase("netlink_player", "S3cr3tP@ss"),
|
|
new TestCase("segaNet001", "CorrectHorseBattery"),
|
|
};
|
|
|
|
public static void main(String[] args) {
|
|
int pass = 0;
|
|
int fail = 0;
|
|
|
|
for (TestCase tc : CASES) {
|
|
// --- DEFECTIVE path ---
|
|
NetlinkLog defLog = new NetlinkLog();
|
|
ConnectStatus s = ConnectStatus.LOGIN1;
|
|
|
|
s = defectiveHandleCarriageReturn(defLog, s, tc.loginName);
|
|
s = defectiveHandleCarriageReturn(defLog, s, tc.password);
|
|
defectiveHandleCarriageReturn(defLog, s, "$ ");
|
|
|
|
boolean defExpLogin = defLog.anyEntryContains(tc.loginName);
|
|
boolean defExpPass = defLog.anyEntryContains(tc.password);
|
|
|
|
if (defExpLogin) {
|
|
System.out.println("PASS (defect confirmed): login name '"
|
|
+ tc.loginName + "' appears in defective log");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (defect NOT confirmed): login name '"
|
|
+ tc.loginName + "' missing from defective log");
|
|
fail++;
|
|
}
|
|
|
|
if (defExpPass) {
|
|
System.out.println("PASS (defect confirmed): password '"
|
|
+ tc.password + "' appears in defective log");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (defect NOT confirmed): password '"
|
|
+ tc.password + "' missing from defective log");
|
|
fail++;
|
|
}
|
|
|
|
// --- FIXED path ---
|
|
NetlinkLog fixLog = new NetlinkLog();
|
|
s = ConnectStatus.LOGIN1;
|
|
|
|
s = fixedHandleCarriageReturn(fixLog, s, tc.loginName);
|
|
s = fixedHandleCarriageReturn(fixLog, s, tc.password);
|
|
fixedHandleCarriageReturn(fixLog, s, "$ ");
|
|
|
|
boolean fixExpLogin = fixLog.anyEntryContains(tc.loginName);
|
|
boolean fixExpPass = fixLog.anyEntryContains(tc.password);
|
|
|
|
if (!fixExpLogin) {
|
|
System.out.println("PASS (fix confirmed): login name '"
|
|
+ tc.loginName + "' NOT in fixed log");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (fix broken): login name '"
|
|
+ tc.loginName + "' still appears in fixed log");
|
|
fail++;
|
|
}
|
|
|
|
if (!fixExpPass) {
|
|
System.out.println("PASS (fix confirmed): password '"
|
|
+ tc.password + "' NOT in fixed log");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (fix broken): password '"
|
|
+ tc.password + "' still appears in fixed log");
|
|
fail++;
|
|
}
|
|
|
|
// Redaction markers must be present
|
|
boolean hasLoginRedact = fixLog.anyEntryContains("login response: [REDACTED");
|
|
boolean hasPassRedact = fixLog.anyEntryContains("password response: [REDACTED");
|
|
|
|
if (hasLoginRedact) {
|
|
System.out.println("PASS (login redaction marker present)");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (login redaction marker missing)");
|
|
fail++;
|
|
}
|
|
|
|
if (hasPassRedact) {
|
|
System.out.println("PASS (password redaction marker present)");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (password redaction marker missing)");
|
|
fail++;
|
|
}
|
|
|
|
// Shell response (non-credential) must still log verbatim
|
|
boolean shellVisible = fixLog.anyEntryContains("shell response: $ ");
|
|
if (shellVisible) {
|
|
System.out.println("PASS (non-credential shell response visible)");
|
|
pass++;
|
|
} else {
|
|
System.out.println("FAIL (non-credential shell response missing)");
|
|
fail++;
|
|
}
|
|
}
|
|
|
|
System.out.println();
|
|
System.out.println("Results: " + pass + " passed, " + fail + " failed");
|
|
if (fail > 0) {
|
|
System.exit(1);
|
|
}
|
|
}
|
|
}
|