Skip to content

Commit

Permalink
Merge pull request #6059 from DataDog/jpbempel/redaction-user-types
Browse files Browse the repository at this point in the history
Add config for custom redacted types
  • Loading branch information
jpbempel committed Oct 19, 2023
2 parents 0fecc32 + 4527ad8 commit c5953b6
Show file tree
Hide file tree
Showing 15 changed files with 412 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package datadog.trace.bootstrap.debugger.util;

import datadog.trace.api.Config;
import datadog.trace.util.ClassNameTrie;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
Expand All @@ -11,42 +15,45 @@ public class Redaction {
// avoid internalization (intern) by the JVM because it's a string constant
public static final String REDACTED_VALUE = new String("REDACTED".toCharArray());

/*
* based on sentry list: https://github.com/getsentry/sentry-python/blob/fefb454287b771ac31db4e30fa459d9be2f977b8/sentry_sdk/scrubber.py#L17-L58
*/
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
private static final List<String> PREDEFINED_KEYWORDS =
Arrays.asList(
"password",
"passwd",
"secret",
"apikey",
"auth",
"credentials",
"mysqlpwd",
"privatekey",
"token",
"ipaddress",
"session",
// django
"csrftoken",
"sessionid",
// wsgi
"remoteaddr",
"xcsrftoken",
"xforwardedfor",
"setcookie",
"cookie",
"authorization",
"xapikey",
"xforwardedfor",
"xrealip");
private static final Set<String> KEYWORDS = ConcurrentHashMap.newKeySet();
private static ClassNameTrie typeTrie = ClassNameTrie.Builder.EMPTY_TRIE;
private static List<String> redactedClasses;
private static List<String> redactedPackages;

static {
KEYWORDS.addAll(
Arrays.asList(
"password",
"passwd",
"secret",
"apikey",
"auth",
"credentials",
"mysqlpwd",
"privatekey",
"token",
"ipaddress",
"session",
// django
"csrftoken",
"sessionid",
// wsgi
"remoteaddr",
"xcsrftoken",
"xforwardedfor",
"setcookie",
"cookie",
"authorization",
"xapikey",
"xforwardedfor",
"xrealip"));
/*
* based on sentry list: https://github.com/getsentry/sentry-python/blob/fefb454287b771ac31db4e30fa459d9be2f977b8/sentry_sdk/scrubber.py#L17-L58
*/
KEYWORDS.addAll(PREDEFINED_KEYWORDS);
}

private static final Pattern COMMA_PATTERN = Pattern.compile(",");

public static void addUserDefinedKeywords(Config config) {
String redactedIdentifiers = config.getDebuggerRedactedIdentifiers();
if (redactedIdentifiers == null) {
Expand All @@ -58,6 +65,38 @@ public static void addUserDefinedKeywords(Config config) {
}
}

public static void addUserDefinedTypes(Config config) {
String redactedTypes = config.getDebuggerRedactedTypes();
if (redactedTypes == null) {
return;
}
List<String> packages = null;
List<String> classes = null;
ClassNameTrie.Builder builder = new ClassNameTrie.Builder();
String[] types = COMMA_PATTERN.split(redactedTypes);
for (String type : types) {
builder.put(type, 1);
if (type.endsWith("*")) {
if (packages == null) {
packages = new ArrayList<>();
}
type =
type.endsWith(".*")
? type.substring(0, type.length() - 2)
: type.substring(0, type.length() - 1);
packages.add(type);
} else {
if (classes == null) {
classes = new ArrayList<>();
}
classes.add(type);
}
}
typeTrie = builder.buildTrie();
redactedPackages = packages;
redactedClasses = classes;
}

public static boolean isRedactedKeyword(String name) {
if (name == null) {
return false;
Expand All @@ -66,6 +105,30 @@ public static boolean isRedactedKeyword(String name) {
return KEYWORDS.contains(name);
}

public static boolean isRedactedType(String className) {
if (className == null) {
return false;
}
return typeTrie.apply(className) > 0;
}

public static List<String> getRedactedPackages() {
return redactedPackages != null ? redactedPackages : Collections.emptyList();
}

public static List<String> getRedactedClasses() {
return redactedClasses != null ? redactedClasses : Collections.emptyList();
}

public static void clearUserDefinedTypes() {
typeTrie = ClassNameTrie.Builder.EMPTY_TRIE;
}

public static void resetUserDefinedKeywords() {
KEYWORDS.clear();
KEYWORDS.addAll(PREDEFINED_KEYWORDS);
}

private static String normalize(String name) {
StringBuilder sb = null;
for (int i = 0; i < name.length(); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.*;

import datadog.trace.api.Config;
import java.lang.reflect.Field;
import org.junit.jupiter.api.Test;

class RedactionTest {
Expand All @@ -23,14 +24,37 @@ public void basic() {

@Test
public void userDefinedKeywords() {
final String REDACTED_IDENTIFIERS = "dd.dynamic.instrumentation.redacted.identifiers";
System.setProperty(REDACTED_IDENTIFIERS, "_MotDePasse,$Passwort");
Config config = Config.get();
setFieldInConfig(config, "debuggerRedactedIdentifiers", "_MotDePasse,$Passwort");
try {
Redaction.addUserDefinedKeywords(Config.get());
Redaction.addUserDefinedKeywords(config);
assertTrue(Redaction.isRedactedKeyword("mot-de-passe"));
assertTrue(Redaction.isRedactedKeyword("Passwort"));
} finally {
System.clearProperty(REDACTED_IDENTIFIERS);
Redaction.resetUserDefinedKeywords();
}
}

@Test
public void userDefinedTypes() {
Config config = Config.get();
setFieldInConfig(config, "debuggerRedactedTypes", "java.security.Security,javax.security.*");
try {
Redaction.addUserDefinedTypes(Config.get());
assertTrue(Redaction.isRedactedType("java.security.Security"));
assertTrue(Redaction.isRedactedType("javax.security.SecurityContext"));
} finally {
Redaction.clearUserDefinedTypes();
}
}

private static void setFieldInConfig(Config config, String fieldName, Object value) {
try {
Field field = config.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(config, value);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.datadog.debugger.el.expressions;

import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.Expression;
import com.datadog.debugger.el.PrettyPrintVisitor;

public class ExpressionHelper {
public static void throwRedactedException(Expression<?> expr) {
String strExpr = PrettyPrintVisitor.print(expr);
throw new EvaluationException(
"Could not evaluate the expression because '" + strExpr + "' was redacted", strExpr);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
} catch (RuntimeException ex) {
throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this), ex);
}
if (member == Redaction.REDACTED_VALUE) {
String expr = PrettyPrintVisitor.print(this);
throw new EvaluationException(
"Could not evaluate the expression because '" + expr + "' was redacted", expr);
if (member == Redaction.REDACTED_VALUE
|| (member != null && Redaction.isRedactedType(member.getClass().getTypeName()))) {
ExpressionHelper.throwRedactedException(this);
}
return Value.of(member);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
if (targetValue instanceof MapValue) {
Object objKey = keyValue.getValue();
if (objKey instanceof String && Redaction.isRedactedKeyword((String) objKey)) {
String expr = PrettyPrintVisitor.print(this);
throw new EvaluationException(
"Could not evaluate the expression because '" + expr + "' was redacted", expr);
ExpressionHelper.throwRedactedException(this);
} else {
result = ((MapValue) targetValue).get(objKey);
}
Expand All @@ -47,6 +45,10 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
} catch (IllegalArgumentException ex) {
throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this), ex);
}
Object obj = result.getValue();
if (obj != null && Redaction.isRedactedType(obj.getClass().getTypeName())) {
ExpressionHelper.throwRedactedException(this);
}
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
} catch (RuntimeException ex) {
throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this));
}
if (symbol == Redaction.REDACTED_VALUE) {
String expr = PrettyPrintVisitor.print(this);
throw new EvaluationException(
"Could not evaluate the expression because '" + expr + "' was redacted", expr);
if (symbol == Redaction.REDACTED_VALUE
|| (symbol != null && Redaction.isRedactedType(symbol.getClass().getTypeName()))) {
ExpressionHelper.throwRedactedException(this);
}
return Value.of(symbol);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public static synchronized void run(
ClassesToRetransformFinder classesToRetransformFinder = new ClassesToRetransformFinder();
setupSourceFileTracking(instrumentation, classesToRetransformFinder);
Redaction.addUserDefinedKeywords(config);
Redaction.addUserDefinedTypes(config);
DDAgentFeaturesDiscovery ddAgentFeaturesDiscovery = sco.featuresDiscovery(config);
ddAgentFeaturesDiscovery.discoverIfOutdated();
agentVersion = ddAgentFeaturesDiscovery.getVersion();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.datadog.debugger.agent;

import datadog.trace.bootstrap.debugger.DebuggerContext;
import datadog.trace.bootstrap.debugger.util.Redaction;
import datadog.trace.util.ClassNameTrie;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -21,6 +22,8 @@ public class DenyListHelper implements DebuggerContext.ClassFilter {
public DenyListHelper(Configuration.FilterList denyList) {
Collection<String> packages = new ArrayList<>(DENIED_PACKAGES);
Collection<String> classes = new ArrayList<>(DENIED_CLASSES);
packages.addAll(Redaction.getRedactedPackages());
classes.addAll(Redaction.getRedactedClasses());
if (denyList != null) {
packages.addAll(denyList.getPackagePrefixes());
classes.addAll(denyList.getClasses());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public void serialize(Object value, String type, Limits limits) throws Exception
throw new IllegalArgumentException("Type is required for serialization");
}
tokenWriter.prologue(value, type);
if (value == REDACTED_VALUE) {
if (value == REDACTED_VALUE || Redaction.isRedactedType(type)) {
tokenWriter.notCaptured(NotCapturedReason.REDACTED);
tokenWriter.epilogue(value);
return;
Expand Down
Loading

0 comments on commit c5953b6

Please sign in to comment.