journal/Journal.Core/Services/LogRedactor.cs

74 lines
2.2 KiB
C#

using System.Text.Json;
namespace Journal.Core.Services;
public static class LogRedactor
{
private static readonly HashSet<string> SensitiveKeys = new(StringComparer.OrdinalIgnoreCase)
{
"password",
"passphrase",
"secret",
"token",
"apiKey",
"api_key",
"cloudAiApiKey",
"content",
"rawContent",
"prompt",
"audioBase64",
"audio_base64",
"text"
};
public static object? RedactPayload(JsonElement? payload)
{
if (payload is null)
return null;
return RedactElement(payload.Value, parentKey: null);
}
private static object? RedactElement(JsonElement element, string? parentKey)
{
if (parentKey is not null && SensitiveKeys.Contains(parentKey))
return "[REDACTED]";
return element.ValueKind switch
{
JsonValueKind.Object => RedactObject(element),
JsonValueKind.Array => RedactArray(element),
JsonValueKind.String => RedactString(element.GetString() ?? "", parentKey),
JsonValueKind.Number => element.GetRawText(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
_ => element.GetRawText()
};
}
private static Dictionary<string, object?> RedactObject(JsonElement element)
{
var output = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
foreach (var property in element.EnumerateObject())
output[property.Name] = RedactElement(property.Value, property.Name);
return output;
}
private static List<object?> RedactArray(JsonElement element)
{
var output = new List<object?>();
foreach (var item in element.EnumerateArray())
output.Add(RedactElement(item, parentKey: null));
return output;
}
private static object RedactString(string value, string? key)
{
if (key is not null && SensitiveKeys.Contains(key))
return "[REDACTED]";
if (value.Length <= 128)
return value;
return $"{value[..128]}...(truncated)";
}
}