Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Notes](../../RELEASENOTES.md).
cause the measurement to be recorded incorrectly.
([#7951](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7951))

* Fixed self-diagnostics preamble redaction of quoted or encoded PEM material
and prefixed JWTs.
([#7955](https://github.com/open-telemetry/opentelemetry-dotnet/pull/7955))

## 1.20.0

Released 2026-Oct-15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal static class SelfDiagnosticsEnvironmentVariablePolicy

private const string ResourceAttributesVarName = "OTEL_RESOURCE_ATTRIBUTES";
private const string PemArmourPrefix = "-----BEGIN";
private const string Base64PemArmourPrefix = "LS0tLS1CRUdJT";
private const string JwtPrefix = "eyJ";

private static readonly char[] NewLineChars = ['\r', '\n'];
Expand Down Expand Up @@ -215,7 +216,7 @@ internal static bool ShouldDisplayValue(string name)
internal static string GetDisplayValue(string name, string value)
{
// Applied ahead of everything else, including the safe list: no path, endpoint, or scalar
// setting legitimately spans lines or opens with PEM armour, so content that does is key
// setting legitimately spans lines or contains PEM armour, so content that does is key
// material pasted where a reference was expected.
if (ContainsInlineSecretMaterial(value))
{
Expand Down Expand Up @@ -263,8 +264,32 @@ private static string GetUriAuthority(string value)

private static bool ContainsInlineSecretMaterial(string value)
=> value.IndexOfAny(NewLineChars) >= 0
|| value.TrimStart().StartsWith(PemArmourPrefix, StringComparison.Ordinal)
|| value.TrimStart().StartsWith(JwtPrefix, StringComparison.Ordinal);
|| Contains(value, PemArmourPrefix)
|| Contains(value, Base64PemArmourPrefix)
|| ContainsJwt(value);

private static bool Contains(string value, string marker)
#if NET || NETSTANDARD2_1_OR_GREATER
=> value.Contains(marker, StringComparison.Ordinal);
#else
=> value.IndexOf(marker, StringComparison.Ordinal) >= 0;
#endif

private static bool ContainsJwt(string value)
{
for (var index = value.IndexOf(JwtPrefix, StringComparison.Ordinal);
index >= 0;
index = value.IndexOf(JwtPrefix, index + JwtPrefix.Length, StringComparison.Ordinal))
{
// Match token boundaries without treating a path such as /opt/keyJar/client.key as a JWT.
if (index == 0 || !char.IsLetterOrDigit(value[index - 1]))
{
return true;
}
}

return false;
}

/// <summary>
/// Redacts <c>OTEL_RESOURCE_ATTRIBUTES</c> per <c>key=value</c> pair rather than wholesale.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,43 @@ public void InlineJwtMaterial_IsRedacted(string name)
Assert.Equal(Redacted, SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue(name, Jwt));
}

[Theory]
[InlineData("OTEL_EXPORTER_OTLP_CERTIFICATE", "\"")]
[InlineData("OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", "\"")]
[InlineData("OTEL_EXPORTER_OTLP_CLIENT_KEY", "\"")]
[InlineData("OTEL_EXPORTER_OTLP_CERTIFICATE", "'")]
[InlineData("OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", "'")]
[InlineData("OTEL_EXPORTER_OTLP_CLIENT_KEY", "'")]
public void QuotedInlineKeyMaterial_IsRedacted(string name, string quote)
{
const string Pem = "-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN\\n-----END PRIVATE KEY-----";

Assert.Equal(Redacted, SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue(name, $"{quote}{Pem}{quote}"));
}

[Theory]
[InlineData("prefix:-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN\\n-----END PRIVATE KEY-----")]
[InlineData("LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTgo=")]
[InlineData("\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature\"")]
[InlineData("Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature")]
[InlineData("keyJar Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.signature")]
public void InlineSecretMaterial_IsRedactedWithPrefixesOrEncoding(string value) =>
Assert.Equal(Redacted, SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue("OTEL_EXPORTER_OTLP_CLIENT_KEY", value));

[Fact]
public void InlineKeyMaterial_InSafeResourceAttribute_IsRedacted() =>
Assert.Equal(
Redacted,
SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue(
"OTEL_RESOURCE_ATTRIBUTES",
"service.name=-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN\\n-----END PRIVATE KEY-----"));

[Theory]
[InlineData("/opt/keyJar/client.key")]
[InlineData("/opt/keyJar/keyJar/client.key")]
public void PathsContainingJwtPrefix_KeepTheirValue(string path) =>
Assert.Equal(path, SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue("OTEL_EXPORTER_OTLP_CLIENT_KEY", path));

[Fact]
public void MultiLineValue_IsRedacted() =>
Assert.Equal(Redacted, SelfDiagnosticsEnvironmentVariablePolicy.GetDisplayValue("OTEL_SERVICE_NAME", "first\r\nsecond"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,25 @@ public void KnownSafeValuesMode_RedactsCredentialCarryingVariables()
Assert.DoesNotContain("super-secret-token", preamble, StringComparison.Ordinal);
}

[Theory]
[InlineData(EnvironmentVariableLogMode.KnownSafeValues)]
[InlineData(EnvironmentVariableLogMode.Names)]
[InlineData(EnvironmentVariableLogMode.None)]
public void RestrictiveModes_DoNotDiscloseQuotedInlineKeyMaterial(EnvironmentVariableLogMode mode)
{
using var scope = EnvironmentVariableScope.Create(
"OTEL_EXPORTER_OTLP_CLIENT_KEY",
"\"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN\\n-----END PRIVATE KEY-----\"");

var preamble = Build(mode);

Assert.DoesNotContain("MIIEvQIBADAN", preamble, StringComparison.Ordinal);
if (mode == EnvironmentVariableLogMode.KnownSafeValues)
{
Assert.Contains("OTEL_EXPORTER_OTLP_CLIENT_KEY = [REDACTED]", preamble, StringComparison.Ordinal);
}
}

[Fact]
public void KnownSafeValuesMode_ReducesEndpointsToTheirAuthority()
{
Expand All @@ -103,14 +122,17 @@ public void KnownSafeValuesMode_ReducesEndpointsToTheirAuthority()
[Fact]
public void AllValuesMode_DisclosesEverythingVerbatim()
{
const string Pem = "\"-----BEGIN PRIVATE KEY-----\\nMIIEvQIBADAN\\n-----END PRIVATE KEY-----\"";
using var scope = EnvironmentVariableScope.Create(
(UnclassifiedVar, UnclassifiedValue),
("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer opt-in-token"));
("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer opt-in-token"),
("OTEL_EXPORTER_OTLP_CLIENT_KEY", Pem));

var preamble = Build(EnvironmentVariableLogMode.AllValues);

Assert.Contains($"{UnclassifiedVar} = {UnclassifiedValue}", preamble, StringComparison.Ordinal);
Assert.Contains("opt-in-token", preamble, StringComparison.Ordinal);
Assert.Contains($"OTEL_EXPORTER_OTLP_CLIENT_KEY = {Pem}", preamble, StringComparison.Ordinal);
}

[Fact]
Expand Down
Loading