Add debug logging output redaction

* Add debug logging output redaction covering sensitive items and PII
This commit is contained in:
abraunegg 2025-11-24 17:12:58 +11:00
commit f6c1ff609d
3 changed files with 50 additions and 21 deletions

View file

@ -1243,9 +1243,13 @@ class OneDriveApi {
private void acquireToken(char[] postData) {
JSONValue response;
// Debug this input
if (debugLogging) {addLogEntry("Manual Auth postData = " ~ to!string(postData), ["debug"]);}
// Log what we are doing
if (debugLogging) {
addLogEntry("acquireToken: requesting new access token using refresh token (value redacted)", ["debug"]);
}
// Try and process the 'postData' content
try {
response = post(tokenUrl, postData, null, true, "application/x-www-form-urlencoded");
} catch (OneDriveException exception) {
@ -1268,9 +1272,14 @@ class OneDriveApi {
}
if (response.type() == JSONType.object) {
// Debug this response
if (debugLogging) {addLogEntry("Manual Auth Response JSON = " ~ to!string(response), ["debug"]);}
// Debug the provided response
if (debugLogging) {
string scopes = ("scope" in response) ? response["scope"].str() : "<none>";
string tokenType = ("token_type" in response) ? response["token_type"].str() : "<none>";
long expiresIn = ("expires_in" in response) ? response["expires_in"].integer() : -1;
addLogEntry("acquireToken post response: token_type=" ~ tokenType ~ ", expires_in=" ~ to!string(expiresIn) ~ ", scope=" ~ scopes, ["debug"]);
}
// Has the client been configured to use read_only_auth_scope
if (appConfig.getValueBool("read_only_auth_scope")) {
// read_only_auth_scope has been configured

View file

@ -594,7 +594,7 @@ class SyncEngine {
// If the JSON response is a correct JSON object, and has an 'id' we can set these details
if ((defaultOneDriveDriveDetails.type() == JSONType.object) && (hasId(defaultOneDriveDriveDetails))) {
if (debugLogging) {addLogEntry("OneDrive Account Default Drive Details: " ~ to!string(defaultOneDriveDriveDetails), ["debug"]);}
if (debugLogging) {addLogEntry("OneDrive Account Default Drive Details: " ~ sanitiseJSONItem(defaultOneDriveDriveDetails), ["debug"]);}
appConfig.accountType = defaultOneDriveDriveDetails["driveType"].str;
// Issue #3115 - Validate driveId length
@ -754,7 +754,7 @@ class SyncEngine {
// If the JSON response is a correct JSON object, and has an 'id' we can set these details
if ((defaultOneDriveRootDetails.type() == JSONType.object) && (hasId(defaultOneDriveRootDetails))) {
// Read the returned JSON data for the root drive details
if (debugLogging) {addLogEntry("OneDrive Account Default Root Details: " ~ to!string(defaultOneDriveRootDetails), ["debug"]);}
if (debugLogging) {addLogEntry("OneDrive Account Default Root Details: " ~ sanitiseJSONItem(defaultOneDriveRootDetails), ["debug"]);}
appConfig.defaultRootId = defaultOneDriveRootDetails["id"].str;
if (debugLogging) {addLogEntry("appConfig.defaultRootId = " ~ appConfig.defaultRootId, ["debug"]);}
@ -10321,15 +10321,13 @@ class SyncEngine {
// Log that we skipping adding item to the local DB and the reason why
if (debugLogging) {addLogEntry("Skipping adding to database as --upload-only & --remove-source-files configured", ["debug"]);}
} else {
// What is the JSON item we are trying to create a DB record with?
if (debugLogging) {addLogEntry("saveItem - creating DB item from this JSON: " ~ sanitiseJSONItem(jsonItem), ["debug"]);}
// Takes a JSON input and formats to an item which can be used by the database
Item item = makeItem(jsonItem);
// Is this JSON item a 'root' item?
if ((isItemRoot(jsonItem)) && (item.name == "root")) {
if (debugLogging) {
addLogEntry("Creating 'root' DB item from this JSON: " ~ sanitiseJSONItem(jsonItem), ["debug"]);
addLogEntry("Updating DB Item object with correct values as this is a 'root' object", ["debug"]);
addLogEntry(" item.parentId = null", ["debug"]);
addLogEntry(" item.type = ItemType.root", ["debug"]);
@ -14934,14 +14932,17 @@ class SyncEngine {
displayFunctionProcessingStart(thisFunctionName, logKey);
}
// Eventual output variable
string sanitisedJSONString;
// Validate UTF-8 before serialisation
if (!validateUTF8JSON(onedriveJSONItem)) {
return "JSON Validation Failed: JSON data from OneDrive API contains invalid UTF-8 characters";
}
// Redact PII in JSON before serialisation
redactPII(onedriveJSONItem);
// Eventual output variable
string sanitisedJSONString;
// Try and serialise the JSON into a string
try {
auto app = appender!string();
@ -14961,6 +14962,32 @@ class SyncEngine {
return sanitisedJSONString;
}
// Recursively redact PII and sensitive elements from JSONValue
void redactPII(ref JSONValue j) {
if (j.type == JSONType.object) {
foreach (key, ref value; j.object) {
// Match Graph's actual keys directly
if (key == "email") {
value = JSONValue("<redacted-email>");
continue;
}
if (key == "displayName") {
value = JSONValue("<redacted-displayName>");
continue;
}
// Recurse
redactPII(value);
}
} else if (j.type == JSONType.array) {
foreach (ref value; j.array) {
redactPII(value);
}
}
}
// Obtain the Websocket Notification URL
void obtainWebSocketNotificationURL() {
// Function Start Time

View file

@ -769,13 +769,6 @@ bool isValidUTF8(string input) {
return false;
}
// is the string empty?
if (input.empty) {
// input is empty
addLogEntry("UTF-8 validation failed: Input is empty.");
return false;
}
// return true
return true;
} catch (UTFException) {