Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : /// Redaction of header values written to the debug log.
5 : ///
6 : /// Which OTLP header holds a credential depends on the backend
7 : /// (`authorization`, `x-api-key`, `x-honeycomb-team`, `dd-api-key`), so a list
8 : /// of names to hide leaks anything not on it. This is the other way round: an
9 : /// allowlist of names whose values are safe to print, everything else redacted.
10 : ///
11 : /// Only values are opted in. Header names and the header count are always
12 : /// logged, so the default empty allowlist still shows which headers are
13 : /// configured.
14 : library;
15 :
16 : /// Written in place of a header value that is not allowed to be logged.
17 : ///
18 : /// Without the value length, which would narrow the search space for the token.
19 : const String redactedHeaderPlaceholder = '[REDACTED]';
20 :
21 : /// Header names whose values are never logged, whatever the allowlist says.
22 : const Set<String> alwaysRedactedHeaderNames = <String>{
23 : 'authorization',
24 : 'proxy-authorization',
25 : };
26 :
27 42 : Set<String> _allowedHeaderNames = const <String>{};
28 :
29 : /// The header names whose values are currently allowed to be logged.
30 : ///
31 : /// Lowercased, and never contains a name in [alwaysRedactedHeaderNames].
32 2 : Set<String> get allowedHeaderLogNames => _allowedHeaderNames;
33 :
34 : /// Sets the header names whose values may be logged, replacing any previous
35 : /// allowlist rather than adding to it.
36 : ///
37 : /// Names are lowercased here so [redactHeaderValue] only has to lowercase its
38 : /// argument. Null or empty restores the default of redacting every value.
39 154 : void configureHeaderLogAllowlist(Iterable<String>? headerNames) {
40 : if (headerNames == null) {
41 : _allowedHeaderNames = const <String>{};
42 : return;
43 : }
44 : _allowedHeaderNames = <String>{
45 154 : for (final name in headerNames)
46 4 : if (name.trim().isNotEmpty) name.trim().toLowerCase(),
47 154 : }..removeAll(alwaysRedactedHeaderNames);
48 : }
49 :
50 : /// Parses the comma separated form used by the environment variable.
51 : ///
52 : /// Entries are trimmed, empty ones are dropped, and the result is a set, so
53 : /// `" x-trace-id , ,X-Trace-Id "` gives `{'x-trace-id'}`.
54 154 : Set<String> parseHeaderLogAllowlist(String? value) {
55 : if (value == null) {
56 : return const <String>{};
57 : }
58 : return <String>{
59 1 : for (final name in value.split(','))
60 4 : if (name.trim().isNotEmpty) name.trim().toLowerCase(),
61 1 : }..removeAll(alwaysRedactedHeaderNames);
62 : }
63 :
64 : /// Returns [value] when [name] is allowed to be logged, and
65 : /// [redactedHeaderPlaceholder] otherwise.
66 40 : String redactHeaderValue(String name, String value) {
67 120 : return _allowedHeaderNames.contains(name.toLowerCase())
68 : ? value
69 : : redactedHeaderPlaceholder;
70 : }
71 :
72 : /// Formats one header for the debug log as `name: value-or-placeholder`.
73 40 : String formatHeaderForLog(String name, String value) {
74 80 : return '$name: ${redactHeaderValue(name, value)}';
75 : }
|