java-topology/defects/rpcs3/test/Rpcs3NpPasswordTest.java

92 lines
4.1 KiB
Java

import java.util.*;
/**
* Unit test for RPCS3 MOAD-0004 defect.
*
* rpcs3-0004: np_structs_extra.cpp print_SceNpMatching2CreateJoinRoomRequest
* logs raw SceNpMatching2SessionPassword bytes via buf_to_hexstring
* at WARNING level (CWE-312: Cleartext Storage of Sensitive Information).
*
* The fix: replace buf_to_hexstring with a redacted placeholder so password
* material never enters our log stream.
*/
public class Rpcs3NpPasswordTest {
// Simulates the defective log serializer: formats raw bytes as hex
static String logPassword_defective(byte[] passwordData) {
StringBuilder sb = new StringBuilder();
for (byte b : passwordData) {
sb.append(String.format("%02x", b));
}
return sb.toString(); // raw hex of secret bytes in log output
}
// Simulates the fixed log serializer: redacts the password
static String logPassword_fixed(byte[] passwordData) {
return "[REDACTED " + passwordData.length + " bytes]";
}
// Checks whether a log entry contains any of the secret bytes
static boolean logContainsSecret(String logEntry, byte[] secret) {
// Build hex representation of secret
StringBuilder hex = new StringBuilder();
for (byte b : secret) hex.append(String.format("%02x", b));
return logEntry.contains(hex.toString());
}
public static void main(String[] args) {
int passed = 0;
int failed = 0;
// Representative SceNpMatching2SessionPassword: 8 bytes
byte[] password = new byte[]{0x4e, 0x50, 0x33, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF, 0x01};
// --- Test 1: defective path exposes raw bytes ---
{
String logEntry = logPassword_defective(password);
boolean exposesSecret = logContainsSecret(logEntry, password);
boolean ok = exposesSecret; // defective SHOULD contain the secret (confirming the defect)
System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 defect confirmed: raw bytes in log = \"" + logEntry + "\"");
if (ok) passed++; else failed++;
}
// --- Test 2: fixed path does NOT expose raw bytes ---
{
String logEntry = logPassword_fixed(password);
boolean exposesSecret = logContainsSecret(logEntry, password);
boolean ok = !exposesSecret;
System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: secret absent from log = \"" + logEntry + "\"");
if (ok) passed++; else failed++;
}
// --- Test 3: fixed path still indicates presence of password (not null suppression) ---
{
String logEntry = logPassword_fixed(password);
boolean ok = logEntry.contains("REDACTED") && logEntry.contains("8 bytes");
System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: placeholder present = \"" + logEntry + "\"");
if (ok) passed++; else failed++;
}
// --- Test 4: null/empty password handled gracefully ---
{
byte[] emptyPassword = new byte[0];
String logEntry = logPassword_fixed(emptyPassword);
boolean ok = logEntry.contains("REDACTED") && logEntry.contains("0 bytes");
System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: empty password placeholder = \"" + logEntry + "\"");
if (ok) passed++; else failed++;
}
// --- Test 5: all-zero password still redacted (not treated as absent) ---
{
byte[] zeroPassword = new byte[8]; // all zeros
String logEntry = logPassword_fixed(zeroPassword);
boolean exposesSecret = logEntry.contains("00000000"); // would be all zeros hex
boolean ok = !exposesSecret && logEntry.contains("REDACTED");
System.out.println((ok ? "PASS" : "FAIL") + " rpcs3-0004 fix: all-zero password redacted = \"" + logEntry + "\"");
if (ok) passed++; else failed++;
}
System.out.printf("%n%d/%d tests passed%n", passed, passed + failed);
if (failed > 0) System.exit(1);
}
}