digikam-0003 CWE-407: MetaEngine/DMetadata addToXmpTagStringBag and removeFromXmpTagStringBag call QStringList::contains() inside a loop over existing entries — O(O*N) per image during batch metadata write. Fix: build QSet<QString> before the loop for O(1) lookup. 17x speedup at K=200 keywords (unit test PASS). digikam-0004 CWE-312: O2::onVerificationReceived logs the full OAuth2 token exchange POST body (including client_secret_) via qDebug() at GrantFlowAuthorizationCode completion — exposes cloud service credentials in debug logs/stderr for Google Photos, Flickr, OneDrive integrations. Fix: replace full-body dump with redacted log line. lmms: all 5 MOADs CLEAN — std::find uses are non-hot, contains() calls are on QHash/QMap/QSet, no credential logging, no leaked thread context, no unsynchronized cache access.
89 lines
4.4 KiB
Java
89 lines
4.4 KiB
Java
import java.util.*;
|
|
import java.util.logging.*;
|
|
import java.io.*;
|
|
|
|
/**
|
|
* DigiKamOAuthSecretLogTest — digikam-0004
|
|
*
|
|
* Reproduces the CWE-312 defect in O2::onVerificationReceived:
|
|
* when GrantFlowAuthorizationCode completes, the full token exchange
|
|
* POST body — including client_secret — is logged verbatim via qDebug().
|
|
*
|
|
* Models the defective (full body logged) vs fixed (body redacted) behavior,
|
|
* verifying that the fixed version does not emit the secret.
|
|
*/
|
|
public class DigiKamOAuthSecretLogTest {
|
|
|
|
// Simulates building the OAuth2 token exchange body (like buildRequestBody)
|
|
static String buildRequestBody(Map<String, String> params) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (Map.Entry<String, String> e : params.entrySet()) {
|
|
if (sb.length() > 0) sb.append("&");
|
|
sb.append(e.getKey()).append("=").append(e.getValue());
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
// Defective: logs full body including client_secret (mirrors o2.cpp line 284)
|
|
static String defectiveLog(String clientSecret, String code, String clientId, String redirectUri) {
|
|
Map<String, String> parameters = new LinkedHashMap<>();
|
|
parameters.put("code", code);
|
|
parameters.put("client_id", clientId);
|
|
parameters.put("client_secret", clientSecret);
|
|
parameters.put("redirect_uri", redirectUri);
|
|
parameters.put("grant_type", "authorization_code");
|
|
String data = buildRequestBody(parameters);
|
|
// Simulates: qDebug() << QString("O2::onVerificationReceived: Exchange access code data:\n%1").arg(data)
|
|
return "O2::onVerificationReceived: Exchange access code data:\n" + data;
|
|
}
|
|
|
|
// Fixed: logs redacted message (mirrors patch)
|
|
static String fixedLog(String clientSecret, String code, String clientId, String redirectUri) {
|
|
// Simulates: qDebug() << "O2::onVerificationReceived: Sending token exchange request (body redacted)"
|
|
return "O2::onVerificationReceived: Sending token exchange request (body redacted)";
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
System.out.println("DigiKamOAuthSecretLogTest — digikam-0004 CWE-312");
|
|
System.out.println("O2::onVerificationReceived logs client_secret in full POST body");
|
|
System.out.println();
|
|
|
|
String clientSecret = "super_secret_oauth2_client_credential_abc123xyz";
|
|
String code = "auth_code_from_callback";
|
|
String clientId = "digikam-app-client-id";
|
|
String redirectUri = "http://localhost:8080/callback";
|
|
|
|
// Defective: secret appears in log output
|
|
String defectiveOutput = defectiveLog(clientSecret, code, clientId, redirectUri);
|
|
boolean defectiveLeaks = defectiveOutput.contains(clientSecret);
|
|
System.out.println("Defective log output:");
|
|
System.out.println(" " + defectiveOutput.replace("\n", "\n "));
|
|
System.out.println(" Contains secret: " + defectiveLeaks);
|
|
assert defectiveLeaks : "Defective variant should contain secret in log (test setup error)";
|
|
System.out.println("PASS: defective variant confirmed to leak secret");
|
|
|
|
// Fixed: secret must NOT appear in log output
|
|
String fixedOutput = fixedLog(clientSecret, code, clientId, redirectUri);
|
|
boolean fixedLeaks = fixedOutput.contains(clientSecret);
|
|
System.out.println("\nFixed log output:");
|
|
System.out.println(" " + fixedOutput);
|
|
System.out.println(" Contains secret: " + fixedLeaks);
|
|
assert !fixedLeaks : "Fixed variant must not contain secret in log, but found: " + fixedOutput;
|
|
System.out.println("PASS: fixed variant does not leak secret");
|
|
|
|
// Verify fixed log still contains useful diagnostic info
|
|
assert fixedOutput.contains("O2::onVerificationReceived") :
|
|
"Fixed log should still identify the function";
|
|
assert fixedOutput.contains("redacted") :
|
|
"Fixed log should indicate body was redacted";
|
|
System.out.println("PASS: fixed log retains useful diagnostic context");
|
|
|
|
// Edge: verify no partial secret leak (first 3 chars truncation check)
|
|
String secretPrefix3 = clientSecret.substring(0, 3);
|
|
assert !fixedOutput.contains(secretPrefix3) :
|
|
"Fixed log should not contain even first 3 chars of secret";
|
|
System.out.println("PASS: fixed log contains no partial secret fragment");
|
|
|
|
System.out.println("\nALL PASS");
|
|
}
|
|
}
|