using System.Text.Json.Nodes; namespace Nexus.Api.Services; internal static class OpenClawPayloadSanitizer { private static readonly string[] SensitiveKeys = [ "accesstoken", "apikey", "authorization", "cookie", "credential", "devicetoken", "password", "privatekey", "refreshtoken", "secret", "token" ]; public static JsonNode? Redact(JsonNode? value) { if (value is null) return null; var clone = value.DeepClone(); RedactInPlace(clone); return clone; } private static void RedactInPlace(JsonNode node) { if (node is JsonObject obj) { foreach (var property in obj.ToList()) { var normalized = property.Key .Replace("-", string.Empty, StringComparison.Ordinal) .Replace("_", string.Empty, StringComparison.Ordinal) .ToLowerInvariant(); var tokenLikeSecret = !normalized.EndsWith("configured", StringComparison.Ordinal) && (normalized.Contains("accesstoken", StringComparison.Ordinal) || normalized.Contains("apitoken", StringComparison.Ordinal) || normalized.Contains("authtoken", StringComparison.Ordinal) || normalized.Contains("bearertoken", StringComparison.Ordinal) || normalized.Contains("devicetoken", StringComparison.Ordinal) || normalized.Contains("refreshtoken", StringComparison.Ordinal)); if (SensitiveKeys.Contains(normalized, StringComparer.Ordinal) || tokenLikeSecret || normalized.EndsWith("password", StringComparison.Ordinal) || normalized.EndsWith("privatekey", StringComparison.Ordinal) || normalized.EndsWith("secret", StringComparison.Ordinal) || normalized.EndsWith("credential", StringComparison.Ordinal) || (normalized.EndsWith("token", StringComparison.Ordinal) && !normalized.EndsWith("inputtoken", StringComparison.Ordinal) && !normalized.EndsWith("outputtoken", StringComparison.Ordinal) && !normalized.EndsWith("totaltoken", StringComparison.Ordinal) && !normalized.EndsWith("contexttoken", StringComparison.Ordinal) && !normalized.EndsWith("maxtoken", StringComparison.Ordinal))) { obj[property.Key] = "[redacted]"; continue; } if (property.Value is not null) RedactInPlace(property.Value); } return; } if (node is JsonArray array) { foreach (var item in array) { if (item is not null) RedactInPlace(item); } } } }