-
-
Notifications
You must be signed in to change notification settings - Fork 237
/
http_sanitizer.dart
105 lines (96 loc) · 2.76 KB
/
http_sanitizer.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import 'package:meta/meta.dart';
import '../protocol.dart';
import 'url_details.dart';
@internal
class HttpSanitizer {
static final RegExp _authRegExp = RegExp("(.+://)(.*@)(.*)");
static final List<String> _securityHeaders = [
"X-FORWARDED-FOR",
"AUTHORIZATION",
"COOKIE",
"SET-COOKIE",
"X-API-KEY",
"X-REAL-IP",
"REMOTE-ADDR",
"FORWARDED",
"PROXY-AUTHORIZATION",
"X-CSRF-TOKEN",
"X-CSRFTOKEN",
"X-XSRF-TOKEN"
];
/// Parse and sanitize url data for sentry.io
static UrlDetails? sanitizeUrl(String? url) {
if (url == null) {
return null;
}
final queryIndex = url.indexOf('?');
final fragmentIndex = url.indexOf('#');
if (queryIndex > -1 && fragmentIndex > -1 && fragmentIndex < queryIndex) {
// url considered malformed because of fragment position
return UrlDetails();
} else {
try {
final uri = Uri.parse(url);
final urlWithAuthRemoved = _urlWithAuthRemoved(uri._url());
return UrlDetails(
url: urlWithAuthRemoved.isEmpty ? null : urlWithAuthRemoved,
query: uri.query.isEmpty ? null : uri.query,
fragment: uri.fragment.isEmpty ? null : uri.fragment);
} catch (_) {
return null;
}
}
}
static Map<String, String>? sanitizedHeaders(Map<String, String>? headers) {
if (headers == null) {
return null;
}
final sanitizedHeaders = <String, String>{};
headers.forEach((key, value) {
if (!_securityHeaders.contains(key.toUpperCase())) {
sanitizedHeaders[key] = value;
}
});
return sanitizedHeaders;
}
static String _urlWithAuthRemoved(String url) {
final userInfoMatch = _authRegExp.firstMatch(url);
if (userInfoMatch != null && userInfoMatch.groupCount == 3) {
final userInfoString = userInfoMatch.group(2) ?? '';
final replacementString = userInfoString.contains(":")
? "[Filtered]:[Filtered]@"
: "[Filtered]@";
return '${userInfoMatch.group(1) ?? ''}$replacementString${userInfoMatch.group(3) ?? ''}';
} else {
return url;
}
}
}
extension UriPath on Uri {
String _url() {
var buffer = '';
if (scheme.isNotEmpty) {
buffer += '$scheme://';
}
if (userInfo.isNotEmpty) {
buffer += '$userInfo@';
}
buffer += host;
if (path.isNotEmpty) {
buffer += path;
}
return buffer;
}
}
extension SanitizedSentryRequest on SentryRequest {
SentryRequest sanitized() {
final urlDetails = HttpSanitizer.sanitizeUrl(url) ?? UrlDetails();
return copyWith(
url: urlDetails.urlOrFallback,
queryString: urlDetails.query,
fragment: urlDetails.fragment,
headers: HttpSanitizer.sanitizedHeaders(headers),
removeCookies: true,
);
}
}