import java.util.*; import java.util.regex.*; /** * openoffice-0002: CurlSession::curlDebugOutput CWE-312 credential logging * * Models the HTTP header sanitization logic from curlDebugOutput (CurlSession.cxx). * Verifies that credential-bearing headers (Authorization, Proxy-Authorization, * Cookie, etc.) have their values redacted before logging, while non-sensitive * headers pass through unmodified. * * This is a unit test for the MOAD-0004 fix: lcl_IsCredentialHeader() + * lcl_RedactHeader() added to CurlSession.cxx. */ public class OpenOfficeWebDAVCredentialLogTest { // ---- Model of the fix ---- static boolean isCredentialHeader(String header) { int colon = header.indexOf(':'); if (colon <= 0) return false; String name = header.substring(0, colon).trim().toLowerCase(); return name.equals("authorization") || name.equals("proxy-authorization") || name.equals("x-auth-token") || name.equals("www-authenticate") || name.equals("proxy-authenticate") || name.equals("cookie") || name.equals("set-cookie"); } static String redactHeader(String header) { int colon = header.indexOf(':'); if (colon <= 0) return header; return header.substring(0, colon) + ": "; } /** Defective logger: logs header verbatim */ static String defectiveLogHeader(String header) { return "[CurlHDR ->] " + header; } /** Fixed logger: sanitizes credential headers */ static String fixedLogHeader(String header) { if (isCredentialHeader(header)) { return "[CurlHDR ->] " + redactHeader(header); } return "[CurlHDR ->] " + header; } public static void main(String[] args) { System.out.println("=== openoffice-0002: WebDAV credential header redaction ===\n"); // Test cases: (header, shouldBeRedacted) Object[][] cases = { // Credential headers — must be redacted { "Authorization: Basic dXNlcjpteXBhc3N3b3Jk", true }, { "Authorization: Digest username=\"user\", realm=\"realm\", response=\"abc123\"", true }, { "Proxy-Authorization: Basic cHJveHk6cGFzcw==", true }, { "Cookie: session=abc123; token=secretvalue", true }, { "Set-Cookie: auth_token=xyz789; HttpOnly", true }, { "X-Auth-Token: sk-live-abc123secret", true }, { "WWW-Authenticate: Basic realm=\"WebDAV\"", true }, { "Proxy-Authenticate: Digest realm=\"proxy\"", true }, // Non-credential headers — must pass through unmodified { "Content-Type: application/xml", false }, { "DAV: 1, 2, ordered-collections", false }, { "Host: dav.example.com", false }, { "User-Agent: OpenOffice/4.2", false }, { "Content-Length: 512", false }, { "Transfer-Encoding: chunked", false }, }; int pass = 0, fail = 0; for (Object[] tc : cases) { String header = (String) tc[0]; boolean shouldRedact = (boolean) tc[1]; String defOut = defectiveLogHeader(header); String fixOut = fixedLogHeader(header); boolean defContainsCred = !defOut.contains("") && shouldRedact; boolean fixCorrect; if (shouldRedact) { // Fixed output must contain and NOT the original value after colon fixCorrect = fixOut.contains("") && !fixOut.contains(header.substring(header.indexOf(':') + 1).trim()); } else { // Fixed output must be identical to defective (non-sensitive header) fixCorrect = fixOut.equals(defOut); } String status = fixCorrect ? "PASS" : "FAIL"; if (fixCorrect) pass++; else fail++; System.out.printf("[%s] %s%n", status, header.substring(0, Math.min(60, header.length()))); if (!fixCorrect) { System.out.printf(" defective: %s%n", defOut); System.out.printf(" fixed: %s%n", fixOut); } } System.out.printf("%n%d/%d tests passed%n", pass, pass + fail); // Key assertion: Authorization: Basic base64 credential must not appear in fixed log String basicAuthHeader = "Authorization: Basic dXNlcjpteXBhc3N3b3Jk"; String defLog = defectiveLogHeader(basicAuthHeader); String fixLog = fixedLogHeader(basicAuthHeader); assert defLog.contains("dXNlcjpteXBhc3N3b3Jk") : "Defective log should contain credential (demonstrates the bug)"; assert !fixLog.contains("dXNlcjpteXBhc3N3b3Jk") : "Fixed log must NOT contain base64 credential"; assert fixLog.contains("") : "Fixed log must contain placeholder"; // Cookie session token must be redacted String cookieHeader = "Cookie: session=abc123; token=secretvalue"; assert !fixedLogHeader(cookieHeader).contains("secretvalue") : "Fixed log must not expose cookie secret values"; assert fail == 0 : fail + " test(s) failed"; System.out.println("\nALL ASSERTIONS PASS"); } }