From 68ab98ad614f6c1ec132b83ade3e47a6277884a5 Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Thu, 8 Sep 2022 08:40:49 -0700 Subject: [PATCH] Add mongo sanitization configuration (#6541) --- .../v3_1/MongoInstrumentationSingletons.java | 11 ++++++- .../mongo/v3_1/MongoDbAttributesGetter.java | 21 ++++++++---- .../mongo/v3_1/MongoInstrumenterFactory.java | 6 ++-- .../mongo/v3_1/MongoTelemetry.java | 8 +++-- .../mongo/v3_1/MongoTelemetryBuilder.java | 15 ++++++++- .../mongo/v3_1/StringBuilderWriter.java | 33 +++++++++++++++++++ .../v3_1/MongoDbAttributesGetterTest.groovy | 8 ++--- .../v3_1/MongoSpanNameExtractorTest.groovy | 2 +- .../v3_7/MongoInstrumentationSingletons.java | 11 ++++++- .../v4_0/MongoInstrumentationSingletons.java | 11 ++++++- .../v3_3/MongoInstrumentationSingletons.java | 11 ++++++- 11 files changed, 116 insertions(+), 21 deletions(-) create mode 100644 instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java diff --git a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java index 522efa3241c5..f9357d0fa3e6 100644 --- a/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_1/MongoInstrumentationSingletons.java @@ -8,11 +8,20 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); public static boolean isTracingListener(CommandListener listener) { return listener.getClass().getName().equals(LISTENER.getClass().getName()); diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java index 8bea66d51aac..6ffab51cd418 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetter.java @@ -10,7 +10,6 @@ import com.mongodb.event.CommandStartedEvent; import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientAttributesGetter; import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; -import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @@ -20,6 +19,8 @@ import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonValue; +import org.bson.codecs.BsonDocumentCodec; +import org.bson.codecs.EncoderContext; import org.bson.json.JsonWriter; import org.bson.json.JsonWriterSettings; @@ -36,10 +37,12 @@ class MongoDbAttributesGetter implements DbClientAttributesGetter= 3.7, the substring invocation will be a no-op due to use of // JsonWriterSettings.Builder.maxLength in the static initializer for JSON_WRITER_SETTINGS - StringBuffer buf = stringWriter.getBuffer(); + StringBuilder buf = stringWriter.getBuilder(); if (buf.length() <= maxNormalizedQueryLength) { return buf.toString(); } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java index 81857a72a4dd..ca45054a004f 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoInstrumenterFactory.java @@ -21,10 +21,12 @@ class MongoInstrumenterFactory { netAttributesExtractor = NetClientAttributesExtractor.create(new MongoNetAttributesGetter()); static Instrumenter createInstrumenter( - OpenTelemetry openTelemetry, int maxNormalizedQueryLength) { + OpenTelemetry openTelemetry, + boolean statementSanitizationEnabled, + int maxNormalizedQueryLength) { MongoDbAttributesGetter dbAttributesGetter = - new MongoDbAttributesGetter(maxNormalizedQueryLength); + new MongoDbAttributesGetter(statementSanitizationEnabled, maxNormalizedQueryLength); SpanNameExtractor spanNameExtractor = new MongoSpanNameExtractor(dbAttributesGetter, attributesExtractor); diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java index ad3085020968..d1c27ef1c6a4 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetry.java @@ -28,9 +28,13 @@ public static MongoTelemetryBuilder builder(OpenTelemetry openTelemetry) { private final Instrumenter instrumenter; - MongoTelemetry(OpenTelemetry openTelemetry, int maxNormalizedQueryLength) { + MongoTelemetry( + OpenTelemetry openTelemetry, + boolean statementSanitizationEnabled, + int maxNormalizedQueryLength) { this.instrumenter = - MongoInstrumenterFactory.createInstrumenter(openTelemetry, maxNormalizedQueryLength); + MongoInstrumenterFactory.createInstrumenter( + openTelemetry, statementSanitizationEnabled, maxNormalizedQueryLength); } /** diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java index c756af1a04a9..26b7fe3db016 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/MongoTelemetryBuilder.java @@ -15,12 +15,24 @@ public final class MongoTelemetryBuilder { private final OpenTelemetry openTelemetry; + private boolean statementSanitizationEnabled = true; private int maxNormalizedQueryLength = DEFAULT_MAX_NORMALIZED_QUERY_LENGTH; MongoTelemetryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; } + /** + * Sets whether the {@code db.statement} attribute on the spans emitted by the constructed {@link + * MongoTelemetry} should be sanitized. If set to {@code true}, all parameters that can + * potentially contain sensitive information will be masked. Enabled by default. + */ + public MongoTelemetryBuilder setStatementSanitizationEnabled( + boolean statementSanitizationEnabled) { + this.statementSanitizationEnabled = statementSanitizationEnabled; + return this; + } + /** * Sets the max length of recorded queries after normalization. Defaults to {@value * DEFAULT_MAX_NORMALIZED_QUERY_LENGTH}. @@ -34,6 +46,7 @@ public MongoTelemetryBuilder setMaxNormalizedQueryLength(int maxNormalizedQueryL * Returns a new {@link MongoTelemetry} with the settings of this {@link MongoTelemetryBuilder}. */ public MongoTelemetry build() { - return new MongoTelemetry(openTelemetry, maxNormalizedQueryLength); + return new MongoTelemetry( + openTelemetry, statementSanitizationEnabled, maxNormalizedQueryLength); } } diff --git a/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java new file mode 100644 index 000000000000..1178b76bd224 --- /dev/null +++ b/instrumentation/mongo/mongo-3.1/library/src/main/java/io/opentelemetry/instrumentation/mongo/v3_1/StringBuilderWriter.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.mongo.v3_1; + +import java.io.Writer; + +// because StringWriter uses the synchronized StringBuffer +class StringBuilderWriter extends Writer { + + private final StringBuilder sb; + + StringBuilderWriter(int initialSize) { + sb = new StringBuilder(initialSize); + } + + @Override + public void write(char[] cbuf, int off, int len) { + sb.append(cbuf, off, len); + } + + @Override + public void flush() {} + + @Override + public void close() {} + + public StringBuilder getBuilder() { + return sb; + } +} diff --git a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy index 1eb8695692d2..85a887b14c5a 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy +++ b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoDbAttributesGetterTest.groovy @@ -18,7 +18,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should sanitize statements to json'() { setup: - def extractor = new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) + def extractor = new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) expect: sanitizeStatementAcrossVersions(extractor, @@ -38,7 +38,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should only preserve string value if it is the value of the first top-level key'() { setup: - def extractor = new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) + def extractor = new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH) expect: sanitizeStatementAcrossVersions(extractor, @@ -50,7 +50,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should truncate simple command'() { setup: - def extractor = new MongoDbAttributesGetter(20) + def extractor = new MongoDbAttributesGetter(true, 20) def normalized = sanitizeStatementAcrossVersions(extractor, new BsonDocument("cmd", new BsonString("c")) @@ -63,7 +63,7 @@ class MongoDbAttributesGetterTest extends Specification { def 'should truncate array'() { setup: - def extractor = new MongoDbAttributesGetter(27) + def extractor = new MongoDbAttributesGetter(true, 27) def normalized = sanitizeStatementAcrossVersions(extractor, new BsonDocument("cmd", new BsonString("c")) diff --git a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy index 06ffbb133e57..368e41337e6e 100644 --- a/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy +++ b/instrumentation/mongo/mongo-3.1/library/src/test/groovy/io/opentelemetry/instrumentation/mongo/v3_1/MongoSpanNameExtractorTest.groovy @@ -16,7 +16,7 @@ class MongoSpanNameExtractorTest extends Specification { def 'test span name with no dbName'() { setup: - def nameExtractor = new MongoSpanNameExtractor(new MongoDbAttributesGetter(DEFAULT_MAX_NORMALIZED_QUERY_LENGTH), new MongoAttributesExtractor()) + def nameExtractor = new MongoSpanNameExtractor(new MongoDbAttributesGetter(true, DEFAULT_MAX_NORMALIZED_QUERY_LENGTH), new MongoAttributesExtractor()) def event = new CommandStartedEvent( 0, null, null, command, new BsonDocument(command, new BsonInt32(1))) diff --git a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java index 839998e4b4eb..044ce2f45a5f 100644 --- a/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-3.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v3_7/MongoInstrumentationSingletons.java @@ -8,11 +8,20 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); public static boolean isTracingListener(CommandListener listener) { return listener.getClass().getName().equals(LISTENER.getClass().getName()); diff --git a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java index 058bf53cb078..74f3bf6cb0b8 100644 --- a/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongo/v4_0/MongoInstrumentationSingletons.java @@ -8,11 +8,20 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); public static boolean isTracingListener(CommandListener listener) { return listener.getClass().getName().equals(LISTENER.getClass().getName()); diff --git a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java index 7ccd25635ca9..f502009eb409 100644 --- a/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java +++ b/instrumentation/mongo/mongo-async-3.3/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/mongoasync/v3_3/MongoInstrumentationSingletons.java @@ -8,11 +8,20 @@ import com.mongodb.event.CommandListener; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.mongo.v3_1.MongoTelemetry; +import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig; public final class MongoInstrumentationSingletons { public static final CommandListener LISTENER = - MongoTelemetry.create(GlobalOpenTelemetry.get()).newCommandListener(); + MongoTelemetry.builder(GlobalOpenTelemetry.get()) + .setStatementSanitizationEnabled( + InstrumentationConfig.get() + .getBoolean( + "otel.instrumentation.mongo.statement-sanitizer.enabled", + CommonConfig.get().isStatementSanitizationEnabled())) + .build() + .newCommandListener(); public static boolean isTracingListener(CommandListener listener) { return listener.getClass().getName().equals(LISTENER.getClass().getName());