@tracelog/lib 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -180
- package/dist/browser/tracelog.esm.js +1007 -1357
- package/dist/browser/tracelog.js +2 -2
- package/dist/cjs/api.d.ts +12 -2
- package/dist/cjs/api.js +63 -27
- package/dist/cjs/app.d.ts +2 -2
- package/dist/cjs/app.js +26 -32
- package/dist/cjs/constants/config.constants.d.ts +4 -2
- package/dist/cjs/constants/config.constants.js +6 -18
- package/dist/cjs/constants/index.d.ts +0 -1
- package/dist/cjs/constants/index.js +0 -1
- package/dist/cjs/constants/storage.constants.d.ts +3 -2
- package/dist/cjs/constants/storage.constants.js +4 -4
- package/dist/cjs/handlers/click.handler.js +3 -6
- package/dist/cjs/handlers/error.handler.js +1 -11
- package/dist/cjs/handlers/page-view.handler.js +0 -4
- package/dist/cjs/handlers/performance.handler.js +14 -29
- package/dist/cjs/handlers/scroll.handler.js +7 -6
- package/dist/cjs/handlers/session.handler.js +7 -6
- package/dist/cjs/integrations/google-analytics.integration.js +2 -6
- package/dist/cjs/listeners/activity-listener-manager.js +3 -3
- package/dist/cjs/listeners/input-listener-managers.js +3 -3
- package/dist/cjs/listeners/touch-listener-manager.js +3 -3
- package/dist/cjs/listeners/unload-listener-manager.js +3 -3
- package/dist/cjs/listeners/visibility-listener-manager.js +3 -3
- package/dist/cjs/managers/event.manager.d.ts +2 -1
- package/dist/cjs/managers/event.manager.js +60 -38
- package/dist/cjs/managers/sender.manager.js +29 -36
- package/dist/cjs/managers/session.manager.js +5 -13
- package/dist/cjs/managers/state.manager.d.ts +0 -3
- package/dist/cjs/managers/state.manager.js +1 -43
- package/dist/cjs/managers/storage.manager.d.ts +16 -2
- package/dist/cjs/managers/storage.manager.js +73 -19
- package/dist/cjs/managers/user.manager.d.ts +1 -1
- package/dist/cjs/managers/user.manager.js +2 -2
- package/dist/cjs/public-api.d.ts +3 -3
- package/dist/cjs/public-api.js +1 -1
- package/dist/cjs/test-bridge.d.ts +1 -0
- package/dist/cjs/test-bridge.js +37 -2
- package/dist/cjs/types/config.types.d.ts +15 -18
- package/dist/cjs/types/config.types.js +6 -0
- package/dist/cjs/types/event.types.d.ts +1 -13
- package/dist/cjs/types/index.d.ts +0 -2
- package/dist/cjs/types/index.js +0 -2
- package/dist/cjs/types/mode.types.d.ts +1 -2
- package/dist/cjs/types/mode.types.js +0 -1
- package/dist/cjs/types/queue.types.d.ts +0 -6
- package/dist/cjs/types/state.types.d.ts +2 -0
- package/dist/cjs/types/test-bridge.types.d.ts +2 -2
- package/dist/cjs/types/validation-error.types.d.ts +0 -6
- package/dist/cjs/types/validation-error.types.js +1 -10
- package/dist/cjs/utils/browser/device-detector.utils.js +2 -24
- package/dist/cjs/utils/browser/index.d.ts +1 -0
- package/dist/cjs/utils/browser/index.js +1 -0
- package/dist/cjs/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/cjs/utils/browser/qa-mode.utils.js +43 -0
- package/dist/cjs/utils/browser/utm-params.utils.js +0 -15
- package/dist/cjs/utils/data/uuid.utils.d.ts +13 -0
- package/dist/cjs/utils/data/uuid.utils.js +37 -1
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.js +1 -1
- package/dist/cjs/utils/logging.utils.d.ts +6 -0
- package/dist/cjs/utils/logging.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +0 -1
- package/dist/cjs/utils/network/index.js +0 -1
- package/dist/cjs/utils/network/url.utils.d.ts +2 -8
- package/dist/cjs/utils/network/url.utils.js +46 -90
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/cjs/utils/security/sanitize.utils.js +15 -178
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/cjs/utils/validations/config-validations.utils.js +48 -94
- package/dist/cjs/utils/validations/event-validations.utils.js +11 -5
- package/dist/cjs/utils/validations/index.d.ts +0 -1
- package/dist/cjs/utils/validations/index.js +0 -1
- package/dist/cjs/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/cjs/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/cjs/utils/validations/type-guards.utils.js +50 -4
- package/dist/esm/api.d.ts +12 -2
- package/dist/esm/api.js +62 -27
- package/dist/esm/app.d.ts +2 -2
- package/dist/esm/app.js +28 -34
- package/dist/esm/constants/config.constants.d.ts +4 -2
- package/dist/esm/constants/config.constants.js +4 -16
- package/dist/esm/constants/index.d.ts +0 -1
- package/dist/esm/constants/index.js +0 -1
- package/dist/esm/constants/storage.constants.d.ts +3 -2
- package/dist/esm/constants/storage.constants.js +3 -2
- package/dist/esm/handlers/click.handler.js +3 -6
- package/dist/esm/handlers/error.handler.js +1 -11
- package/dist/esm/handlers/page-view.handler.js +0 -4
- package/dist/esm/handlers/performance.handler.js +14 -29
- package/dist/esm/handlers/scroll.handler.js +7 -6
- package/dist/esm/handlers/session.handler.js +7 -6
- package/dist/esm/integrations/google-analytics.integration.js +3 -7
- package/dist/esm/listeners/activity-listener-manager.js +3 -3
- package/dist/esm/listeners/input-listener-managers.js +3 -3
- package/dist/esm/listeners/touch-listener-manager.js +3 -3
- package/dist/esm/listeners/unload-listener-manager.js +3 -3
- package/dist/esm/listeners/visibility-listener-manager.js +3 -3
- package/dist/esm/managers/event.manager.d.ts +2 -1
- package/dist/esm/managers/event.manager.js +62 -40
- package/dist/esm/managers/sender.manager.js +31 -38
- package/dist/esm/managers/session.manager.js +5 -13
- package/dist/esm/managers/state.manager.d.ts +0 -3
- package/dist/esm/managers/state.manager.js +1 -43
- package/dist/esm/managers/storage.manager.d.ts +16 -2
- package/dist/esm/managers/storage.manager.js +73 -19
- package/dist/esm/managers/user.manager.d.ts +1 -1
- package/dist/esm/managers/user.manager.js +2 -2
- package/dist/esm/public-api.d.ts +3 -3
- package/dist/esm/public-api.js +1 -1
- package/dist/esm/test-bridge.d.ts +1 -0
- package/dist/esm/test-bridge.js +37 -2
- package/dist/esm/types/config.types.d.ts +15 -18
- package/dist/esm/types/config.types.js +5 -1
- package/dist/esm/types/event.types.d.ts +1 -13
- package/dist/esm/types/index.d.ts +0 -2
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/types/mode.types.d.ts +1 -2
- package/dist/esm/types/mode.types.js +0 -1
- package/dist/esm/types/queue.types.d.ts +0 -6
- package/dist/esm/types/state.types.d.ts +2 -0
- package/dist/esm/types/test-bridge.types.d.ts +2 -2
- package/dist/esm/types/validation-error.types.d.ts +0 -6
- package/dist/esm/types/validation-error.types.js +0 -8
- package/dist/esm/utils/browser/device-detector.utils.js +2 -24
- package/dist/esm/utils/browser/index.d.ts +1 -0
- package/dist/esm/utils/browser/index.js +1 -0
- package/dist/esm/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/esm/utils/browser/qa-mode.utils.js +39 -0
- package/dist/esm/utils/browser/utm-params.utils.js +0 -15
- package/dist/esm/utils/data/uuid.utils.d.ts +13 -0
- package/dist/esm/utils/data/uuid.utils.js +35 -0
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/logging.utils.d.ts +6 -0
- package/dist/esm/utils/logging.utils.js +20 -0
- package/dist/esm/utils/network/index.d.ts +0 -1
- package/dist/esm/utils/network/index.js +0 -1
- package/dist/esm/utils/network/url.utils.d.ts +2 -8
- package/dist/esm/utils/network/url.utils.js +45 -88
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/esm/utils/security/sanitize.utils.js +15 -176
- package/dist/esm/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/esm/utils/validations/config-validations.utils.js +49 -94
- package/dist/esm/utils/validations/event-validations.utils.js +11 -5
- package/dist/esm/utils/validations/index.d.ts +0 -1
- package/dist/esm/utils/validations/index.js +0 -1
- package/dist/esm/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/esm/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/esm/utils/validations/type-guards.utils.js +50 -4
- package/package.json +1 -1
- package/dist/cjs/app.types.d.ts +0 -2
- package/dist/cjs/app.types.js +0 -12
- package/dist/cjs/constants/api.constants.d.ts +0 -6
- package/dist/cjs/constants/api.constants.js +0 -14
- package/dist/cjs/managers/api.manager.d.ts +0 -13
- package/dist/cjs/managers/api.manager.js +0 -44
- package/dist/cjs/managers/config.builder.d.ts +0 -33
- package/dist/cjs/managers/config.builder.js +0 -116
- package/dist/cjs/managers/config.manager.d.ts +0 -56
- package/dist/cjs/managers/config.manager.js +0 -157
- package/dist/cjs/managers/tags.manager.d.ts +0 -36
- package/dist/cjs/managers/tags.manager.js +0 -171
- package/dist/cjs/types/api.types.d.ts +0 -52
- package/dist/cjs/types/api.types.js +0 -56
- package/dist/cjs/types/tag.types.d.ts +0 -43
- package/dist/cjs/types/tag.types.js +0 -31
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/cjs/utils/logging/debug-logger.utils.js +0 -47
- package/dist/cjs/utils/logging/index.d.ts +0 -1
- package/dist/cjs/utils/logging/index.js +0 -5
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +0 -25
- package/dist/cjs/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/cjs/utils/validations/url-validations.utils.js +0 -47
- package/dist/esm/app.types.d.ts +0 -2
- package/dist/esm/app.types.js +0 -1
- package/dist/esm/constants/api.constants.d.ts +0 -6
- package/dist/esm/constants/api.constants.js +0 -11
- package/dist/esm/managers/api.manager.d.ts +0 -13
- package/dist/esm/managers/api.manager.js +0 -41
- package/dist/esm/managers/config.builder.d.ts +0 -33
- package/dist/esm/managers/config.builder.js +0 -112
- package/dist/esm/managers/config.manager.d.ts +0 -56
- package/dist/esm/managers/config.manager.js +0 -153
- package/dist/esm/managers/tags.manager.d.ts +0 -36
- package/dist/esm/managers/tags.manager.js +0 -167
- package/dist/esm/types/api.types.d.ts +0 -52
- package/dist/esm/types/api.types.js +0 -53
- package/dist/esm/types/tag.types.d.ts +0 -43
- package/dist/esm/types/tag.types.js +0 -28
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/esm/utils/logging/debug-logger.utils.js +0 -44
- package/dist/esm/utils/logging/index.d.ts +0 -1
- package/dist/esm/utils/logging/index.js +0 -1
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +0 -22
- package/dist/esm/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/esm/utils/validations/url-validations.utils.js +0 -42
|
@@ -1,30 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { log } from '../logging.utils';
|
|
2
|
+
/**
|
|
3
|
+
* Validates if a URL is valid and optionally allows HTTP URLs
|
|
4
|
+
* @param url - The URL to validate
|
|
5
|
+
* @param allowHttp - Whether to allow HTTP URLs (default: false)
|
|
6
|
+
* @returns True if the URL is valid, false otherwise
|
|
7
|
+
*/
|
|
8
|
+
const isValidUrl = (url, allowHttp = false) => {
|
|
9
|
+
try {
|
|
10
|
+
const parsed = new URL(url);
|
|
11
|
+
const isHttps = parsed.protocol === 'https:';
|
|
12
|
+
const isHttp = parsed.protocol === 'http:';
|
|
13
|
+
return isHttps || (allowHttp && isHttp);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
3
19
|
/**
|
|
4
20
|
* Generates an API URL based on project ID and current domain
|
|
5
21
|
* @param id - The project ID
|
|
6
22
|
* @returns The generated API URL
|
|
7
23
|
*/
|
|
8
|
-
export const getApiUrl = (
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
export const getApiUrl = (config) => {
|
|
25
|
+
const allowHttp = config.allowHttp ?? false;
|
|
26
|
+
if (config.integrations?.tracelog?.projectId) {
|
|
27
|
+
const url = new URL(window.location.href);
|
|
28
|
+
const host = url.hostname;
|
|
29
|
+
const parts = host.split('.');
|
|
30
|
+
if (parts.length === 0) {
|
|
31
|
+
throw new Error('Invalid URL');
|
|
32
|
+
}
|
|
33
|
+
const projectId = config.integrations.tracelog.projectId;
|
|
34
|
+
const cleanDomain = parts.slice(-2).join('.');
|
|
35
|
+
const protocol = allowHttp && url.protocol === 'http:' ? 'http' : 'https';
|
|
36
|
+
const apiUrl = `${protocol}://${projectId}.${cleanDomain}`;
|
|
37
|
+
const isValid = isValidUrl(apiUrl, allowHttp);
|
|
38
|
+
if (!isValid) {
|
|
39
|
+
throw new Error('Invalid URL');
|
|
40
|
+
}
|
|
41
|
+
return apiUrl;
|
|
15
42
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
allowHttp,
|
|
24
|
-
});
|
|
25
|
-
throw new Error('Invalid URL');
|
|
43
|
+
if (config.integrations?.custom?.apiUrl) {
|
|
44
|
+
const apiUrl = config.integrations.custom.apiUrl;
|
|
45
|
+
const isValid = isValidUrl(apiUrl, allowHttp);
|
|
46
|
+
if (!isValid) {
|
|
47
|
+
throw new Error('Invalid URL');
|
|
48
|
+
}
|
|
49
|
+
return apiUrl;
|
|
26
50
|
}
|
|
27
|
-
return
|
|
51
|
+
return '';
|
|
28
52
|
};
|
|
29
53
|
/**
|
|
30
54
|
* Normalizes a URL by removing sensitive query parameters
|
|
@@ -36,7 +60,6 @@ export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
|
36
60
|
try {
|
|
37
61
|
const urlObject = new URL(url);
|
|
38
62
|
const searchParams = urlObject.searchParams;
|
|
39
|
-
const originalParamCount = Array.from(searchParams.keys()).length;
|
|
40
63
|
let hasChanged = false;
|
|
41
64
|
const removedParams = [];
|
|
42
65
|
sensitiveQueryParams.forEach((param) => {
|
|
@@ -46,13 +69,6 @@ export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
|
46
69
|
removedParams.push(param);
|
|
47
70
|
}
|
|
48
71
|
});
|
|
49
|
-
if (hasChanged) {
|
|
50
|
-
debugLog.debug('URLUtils', 'Sensitive parameters removed from URL', {
|
|
51
|
-
removedParams,
|
|
52
|
-
originalParamCount,
|
|
53
|
-
finalParamCount: Array.from(searchParams.keys()).length,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
72
|
if (!hasChanged && url.includes('?')) {
|
|
57
73
|
return url;
|
|
58
74
|
}
|
|
@@ -61,66 +77,7 @@ export const normalizeUrl = (url, sensitiveQueryParams = []) => {
|
|
|
61
77
|
return result;
|
|
62
78
|
}
|
|
63
79
|
catch (error) {
|
|
64
|
-
|
|
65
|
-
url: url.slice(0, 100),
|
|
66
|
-
error: error instanceof Error ? error.message : error,
|
|
67
|
-
});
|
|
80
|
+
log('warn', 'URL normalization failed, returning original', { error, data: { url: url.slice(0, 100) } });
|
|
68
81
|
return url;
|
|
69
82
|
}
|
|
70
83
|
};
|
|
71
|
-
/**
|
|
72
|
-
* Checks if a URL path should be excluded from tracking
|
|
73
|
-
* @param url - The URL to check
|
|
74
|
-
* @param excludedPaths - Array of patterns to match against
|
|
75
|
-
* @returns True if the URL should be excluded
|
|
76
|
-
*/
|
|
77
|
-
export const isUrlPathExcluded = (url, excludedPaths = []) => {
|
|
78
|
-
if (excludedPaths.length === 0) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
let path;
|
|
82
|
-
try {
|
|
83
|
-
const parsedUrl = new URL(url, window.location.origin);
|
|
84
|
-
path = parsedUrl.pathname + (parsedUrl.hash ?? '');
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
debugLog.warn('URLUtils', 'Failed to parse URL for path exclusion check', {
|
|
88
|
-
url: url.slice(0, 100),
|
|
89
|
-
error: error instanceof Error ? error.message : error,
|
|
90
|
-
});
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
const isRegularExpression = (value) => typeof value === 'object' && value !== undefined && typeof value.test === 'function';
|
|
94
|
-
const escapeRegexString = (string_) => string_.replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&');
|
|
95
|
-
const wildcardToRegex = (string_) => new RegExp('^' +
|
|
96
|
-
string_
|
|
97
|
-
.split('*')
|
|
98
|
-
.map((element) => escapeRegexString(element))
|
|
99
|
-
.join('.+') +
|
|
100
|
-
'$');
|
|
101
|
-
const matchedPattern = excludedPaths.find((pattern) => {
|
|
102
|
-
try {
|
|
103
|
-
if (isRegularExpression(pattern)) {
|
|
104
|
-
const matches = pattern.test(path);
|
|
105
|
-
return matches;
|
|
106
|
-
}
|
|
107
|
-
if (pattern.includes('*')) {
|
|
108
|
-
const regex = wildcardToRegex(pattern);
|
|
109
|
-
const matches = regex.test(path);
|
|
110
|
-
return matches;
|
|
111
|
-
}
|
|
112
|
-
const matches = pattern === path;
|
|
113
|
-
return matches;
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
debugLog.warn('URLUtils', 'Error testing exclusion pattern', {
|
|
117
|
-
pattern,
|
|
118
|
-
path,
|
|
119
|
-
error: error instanceof Error ? error.message : error,
|
|
120
|
-
});
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
const isExcluded = !!matchedPattern;
|
|
125
|
-
return isExcluded;
|
|
126
|
-
};
|
|
@@ -1,22 +1,10 @@
|
|
|
1
|
-
import { MetadataType
|
|
1
|
+
import { MetadataType } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* Sanitizes a string value to prevent XSS attacks
|
|
4
4
|
* @param value - The string to sanitize
|
|
5
5
|
* @returns The sanitized string
|
|
6
6
|
*/
|
|
7
7
|
export declare const sanitizeString: (value: string) => string;
|
|
8
|
-
/**
|
|
9
|
-
* Sanitizes a path string for route exclusion checks
|
|
10
|
-
* @param value - The path string to sanitize
|
|
11
|
-
* @returns The sanitized path string
|
|
12
|
-
*/
|
|
13
|
-
export declare const sanitizePathString: (value: string) => string;
|
|
14
|
-
/**
|
|
15
|
-
* Sanitizes API configuration data with strict validation
|
|
16
|
-
* @param data - The API config data to sanitize
|
|
17
|
-
* @returns The sanitized API config
|
|
18
|
-
*/
|
|
19
|
-
export declare const sanitizeApiConfig: (data: unknown) => ApiConfig;
|
|
20
8
|
/**
|
|
21
9
|
* Sanitizes user metadata for custom events
|
|
22
10
|
* @param metadata - The metadata to sanitize
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { MAX_ARRAY_LENGTH, MAX_OBJECT_DEPTH, MAX_STRING_LENGTH, XSS_PATTERNS } from '../../constants';
|
|
2
|
+
import { log } from '../logging.utils';
|
|
3
3
|
/**
|
|
4
4
|
* Sanitizes a string value to prevent XSS attacks
|
|
5
5
|
* @param value - The string to sanitize
|
|
@@ -7,19 +7,13 @@ import { debugLog } from '../logging';
|
|
|
7
7
|
*/
|
|
8
8
|
export const sanitizeString = (value) => {
|
|
9
9
|
if (!value || typeof value !== 'string' || value.trim().length === 0) {
|
|
10
|
-
debugLog.debug('Sanitize', 'String sanitization skipped - empty or invalid input', { value, type: typeof value });
|
|
11
10
|
return '';
|
|
12
11
|
}
|
|
13
|
-
const originalLength = value.length;
|
|
14
12
|
let sanitized = value;
|
|
15
13
|
// Limit string length
|
|
16
14
|
if (value.length > MAX_STRING_LENGTH) {
|
|
17
15
|
sanitized = value.slice(0, Math.max(0, MAX_STRING_LENGTH));
|
|
18
|
-
|
|
19
|
-
originalLength,
|
|
20
|
-
maxLength: MAX_STRING_LENGTH,
|
|
21
|
-
truncatedLength: sanitized.length,
|
|
22
|
-
});
|
|
16
|
+
// Silent truncation - this is expected behavior for long strings
|
|
23
17
|
}
|
|
24
18
|
// Remove potential XSS patterns
|
|
25
19
|
let xssPatternMatches = 0;
|
|
@@ -31,9 +25,11 @@ export const sanitizeString = (value) => {
|
|
|
31
25
|
}
|
|
32
26
|
}
|
|
33
27
|
if (xssPatternMatches > 0) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
log('warn', 'XSS patterns detected and removed', {
|
|
29
|
+
data: {
|
|
30
|
+
patternMatches: xssPatternMatches,
|
|
31
|
+
originalValue: value.slice(0, 100),
|
|
32
|
+
},
|
|
37
33
|
});
|
|
38
34
|
}
|
|
39
35
|
// Basic HTML entity encoding for critical characters
|
|
@@ -45,40 +41,8 @@ export const sanitizeString = (value) => {
|
|
|
45
41
|
.replaceAll("'", ''')
|
|
46
42
|
.replaceAll('/', '/');
|
|
47
43
|
const result = sanitized.trim();
|
|
48
|
-
if (originalLength > 50 || xssPatternMatches > 0) {
|
|
49
|
-
debugLog.debug('Sanitize', 'String sanitization completed', {
|
|
50
|
-
originalLength,
|
|
51
|
-
sanitizedLength: result.length,
|
|
52
|
-
xssPatternMatches,
|
|
53
|
-
wasTruncated: originalLength > MAX_STRING_LENGTH,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
44
|
return result;
|
|
57
45
|
};
|
|
58
|
-
/**
|
|
59
|
-
* Sanitizes a path string for route exclusion checks
|
|
60
|
-
* @param value - The path string to sanitize
|
|
61
|
-
* @returns The sanitized path string
|
|
62
|
-
*/
|
|
63
|
-
export const sanitizePathString = (value) => {
|
|
64
|
-
if (typeof value !== 'string') {
|
|
65
|
-
return '';
|
|
66
|
-
}
|
|
67
|
-
if (value.length > MAX_STRING_LENGTH) {
|
|
68
|
-
value = value.slice(0, Math.max(0, MAX_STRING_LENGTH));
|
|
69
|
-
}
|
|
70
|
-
let sanitized = value;
|
|
71
|
-
for (const pattern of XSS_PATTERNS) {
|
|
72
|
-
sanitized = sanitized.replace(pattern, '');
|
|
73
|
-
}
|
|
74
|
-
sanitized = sanitized
|
|
75
|
-
.replaceAll('&', '&')
|
|
76
|
-
.replaceAll('<', '<')
|
|
77
|
-
.replaceAll('>', '>')
|
|
78
|
-
.replaceAll('"', '"')
|
|
79
|
-
.replaceAll("'", ''');
|
|
80
|
-
return sanitized.trim();
|
|
81
|
-
};
|
|
82
46
|
/**
|
|
83
47
|
* Sanitizes any value recursively with depth protection
|
|
84
48
|
* @param value - The value to sanitize
|
|
@@ -88,10 +52,7 @@ export const sanitizePathString = (value) => {
|
|
|
88
52
|
const sanitizeValue = (value, depth = 0) => {
|
|
89
53
|
// Prevent infinite recursion
|
|
90
54
|
if (depth > MAX_OBJECT_DEPTH) {
|
|
91
|
-
|
|
92
|
-
depth,
|
|
93
|
-
maxDepth: MAX_OBJECT_DEPTH,
|
|
94
|
-
});
|
|
55
|
+
// Silent depth limit - prevents stack overflow
|
|
95
56
|
return null;
|
|
96
57
|
}
|
|
97
58
|
if (value === null || value === undefined) {
|
|
@@ -102,7 +63,7 @@ const sanitizeValue = (value, depth = 0) => {
|
|
|
102
63
|
}
|
|
103
64
|
if (typeof value === 'number') {
|
|
104
65
|
if (!Number.isFinite(value) || value < -Number.MAX_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) {
|
|
105
|
-
|
|
66
|
+
// Silent normalization - invalid numbers become 0
|
|
106
67
|
return 0;
|
|
107
68
|
}
|
|
108
69
|
return value;
|
|
@@ -111,34 +72,17 @@ const sanitizeValue = (value, depth = 0) => {
|
|
|
111
72
|
return value;
|
|
112
73
|
}
|
|
113
74
|
if (Array.isArray(value)) {
|
|
114
|
-
const originalLength = value.length;
|
|
115
75
|
const limitedArray = value.slice(0, MAX_ARRAY_LENGTH);
|
|
116
|
-
|
|
117
|
-
debugLog.warn('Sanitize', 'Array truncated due to length limit', {
|
|
118
|
-
originalLength,
|
|
119
|
-
maxLength: MAX_ARRAY_LENGTH,
|
|
120
|
-
depth,
|
|
121
|
-
});
|
|
122
|
-
}
|
|
76
|
+
// Silent array length limit
|
|
123
77
|
const sanitizedArray = limitedArray.map((item) => sanitizeValue(item, depth + 1)).filter((item) => item !== null);
|
|
124
|
-
|
|
125
|
-
debugLog.warn('Sanitize', 'All array items were filtered out during sanitization', { originalLength, depth });
|
|
126
|
-
}
|
|
78
|
+
// Silent filter - empty arrays are valid results
|
|
127
79
|
return sanitizedArray;
|
|
128
80
|
}
|
|
129
81
|
if (typeof value === 'object') {
|
|
130
82
|
const sanitizedObject = {};
|
|
131
83
|
const entries = Object.entries(value);
|
|
132
|
-
const originalKeysCount = entries.length;
|
|
133
84
|
const limitedEntries = entries.slice(0, 20);
|
|
134
|
-
|
|
135
|
-
debugLog.warn('Sanitize', 'Object keys truncated due to limit', {
|
|
136
|
-
originalKeys: originalKeysCount,
|
|
137
|
-
maxKeys: 20,
|
|
138
|
-
depth,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
let filteredKeysCount = 0;
|
|
85
|
+
// Silent object keys limit
|
|
142
86
|
for (const [key, value_] of limitedEntries) {
|
|
143
87
|
const sanitizedKey = sanitizeString(key);
|
|
144
88
|
if (sanitizedKey) {
|
|
@@ -146,133 +90,28 @@ const sanitizeValue = (value, depth = 0) => {
|
|
|
146
90
|
if (sanitizedValue !== null) {
|
|
147
91
|
sanitizedObject[sanitizedKey] = sanitizedValue;
|
|
148
92
|
}
|
|
149
|
-
else {
|
|
150
|
-
filteredKeysCount++;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
filteredKeysCount++;
|
|
155
93
|
}
|
|
156
94
|
}
|
|
157
|
-
if (filteredKeysCount > 0) {
|
|
158
|
-
debugLog.debug('Sanitize', 'Object properties filtered during sanitization', {
|
|
159
|
-
filteredKeysCount,
|
|
160
|
-
remainingKeys: Object.keys(sanitizedObject).length,
|
|
161
|
-
depth,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
95
|
return sanitizedObject;
|
|
165
96
|
}
|
|
166
|
-
debugLog.debug('Sanitize', 'Unknown value type sanitized to null', { type: typeof value, depth });
|
|
167
97
|
return null;
|
|
168
98
|
};
|
|
169
|
-
/**
|
|
170
|
-
* Sanitizes API configuration data with strict validation
|
|
171
|
-
* @param data - The API config data to sanitize
|
|
172
|
-
* @returns The sanitized API config
|
|
173
|
-
*/
|
|
174
|
-
export const sanitizeApiConfig = (data) => {
|
|
175
|
-
debugLog.debug('Sanitize', 'Starting API config sanitization');
|
|
176
|
-
const safeData = {};
|
|
177
|
-
if (typeof data !== 'object' || data === null) {
|
|
178
|
-
debugLog.warn('Sanitize', 'API config data is not an object', { data, type: typeof data });
|
|
179
|
-
return safeData;
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
const originalKeys = Object.keys(data);
|
|
183
|
-
let processedKeys = 0;
|
|
184
|
-
let filteredKeys = 0;
|
|
185
|
-
for (const key of originalKeys) {
|
|
186
|
-
if (ALLOWED_API_CONFIG_KEYS.has(key)) {
|
|
187
|
-
const value = data[key];
|
|
188
|
-
if (key === 'excludedUrlPaths') {
|
|
189
|
-
const paths = Array.isArray(value) ? value : typeof value === 'string' ? [value] : [];
|
|
190
|
-
const originalPathsCount = paths.length;
|
|
191
|
-
safeData.excludedUrlPaths = paths.map((path) => sanitizePathString(String(path))).filter(Boolean);
|
|
192
|
-
const filteredPathsCount = originalPathsCount - safeData.excludedUrlPaths.length;
|
|
193
|
-
if (filteredPathsCount > 0) {
|
|
194
|
-
debugLog.warn('Sanitize', 'Some excluded URL paths were filtered during sanitization', {
|
|
195
|
-
originalCount: originalPathsCount,
|
|
196
|
-
filteredCount: filteredPathsCount,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
else if (key === 'tags') {
|
|
201
|
-
if (Array.isArray(value)) {
|
|
202
|
-
safeData.tags = value;
|
|
203
|
-
debugLog.debug('Sanitize', 'Tags processed', { count: value.length });
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
debugLog.warn('Sanitize', 'Tags value is not an array', { value, type: typeof value });
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else if (key === 'samplingRate') {
|
|
210
|
-
const sanitizedValue = sanitizeValue(value);
|
|
211
|
-
if (typeof sanitizedValue === 'number') {
|
|
212
|
-
safeData.samplingRate = sanitizedValue;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
const sanitizedValue = sanitizeValue(value);
|
|
217
|
-
if (sanitizedValue !== null) {
|
|
218
|
-
safeData[key] = sanitizedValue;
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
debugLog.warn('Sanitize', 'API config value sanitized to null', { key, originalValue: value });
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
processedKeys++;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
filteredKeys++;
|
|
228
|
-
debugLog.debug('Sanitize', 'API config key not allowed', { key });
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
debugLog.info('Sanitize', 'API config sanitization completed', {
|
|
232
|
-
originalKeys: originalKeys.length,
|
|
233
|
-
processedKeys,
|
|
234
|
-
filteredKeys,
|
|
235
|
-
finalKeys: Object.keys(safeData).length,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
catch (error) {
|
|
239
|
-
debugLog.error('Sanitize', 'API config sanitization failed', {
|
|
240
|
-
error: error instanceof Error ? error.message : error,
|
|
241
|
-
});
|
|
242
|
-
throw new Error(`API config sanitization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
243
|
-
}
|
|
244
|
-
return safeData;
|
|
245
|
-
};
|
|
246
99
|
/**
|
|
247
100
|
* Sanitizes user metadata for custom events
|
|
248
101
|
* @param metadata - The metadata to sanitize
|
|
249
102
|
* @returns The sanitized metadata
|
|
250
103
|
*/
|
|
251
104
|
export const sanitizeMetadata = (metadata) => {
|
|
252
|
-
debugLog.debug('Sanitize', 'Starting metadata sanitization', { hasMetadata: metadata != null });
|
|
253
105
|
if (typeof metadata !== 'object' || metadata === null) {
|
|
254
|
-
debugLog.debug('Sanitize', 'Metadata is not an object, returning empty object', {
|
|
255
|
-
metadata,
|
|
256
|
-
type: typeof metadata,
|
|
257
|
-
});
|
|
258
106
|
return {};
|
|
259
107
|
}
|
|
260
108
|
try {
|
|
261
|
-
const originalKeys = Object.keys(metadata).length;
|
|
262
109
|
const sanitized = sanitizeValue(metadata);
|
|
263
110
|
const result = typeof sanitized === 'object' && sanitized !== null ? sanitized : {};
|
|
264
|
-
const finalKeys = Object.keys(result).length;
|
|
265
|
-
debugLog.debug('Sanitize', 'Metadata sanitization completed', {
|
|
266
|
-
originalKeys,
|
|
267
|
-
finalKeys,
|
|
268
|
-
keysFiltered: originalKeys - finalKeys,
|
|
269
|
-
});
|
|
270
111
|
return result;
|
|
271
112
|
}
|
|
272
113
|
catch (error) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
});
|
|
276
|
-
throw new Error(`Metadata sanitization failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
114
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
115
|
+
throw new Error(`[TraceLog] Metadata sanitization failed: ${errorMessage}`);
|
|
277
116
|
}
|
|
278
117
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Config } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* Validates the app configuration object (before normalization)
|
|
4
4
|
* This validates the structure and basic types but allows for normalization afterward
|
|
@@ -6,7 +6,7 @@ import { AppConfig, ApiConfig } from '../../types';
|
|
|
6
6
|
* @throws {ProjectIdValidationError} If project ID validation fails
|
|
7
7
|
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
8
8
|
*/
|
|
9
|
-
export declare const validateAppConfig: (config:
|
|
9
|
+
export declare const validateAppConfig: (config: Config) => void;
|
|
10
10
|
/**
|
|
11
11
|
* Validates and normalizes the app configuration
|
|
12
12
|
* This is the primary validation entry point that ensures consistent behavior
|
|
@@ -15,10 +15,4 @@ export declare const validateAppConfig: (config: AppConfig) => void;
|
|
|
15
15
|
* @throws {ProjectIdValidationError} If project ID validation fails after normalization
|
|
16
16
|
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
17
17
|
*/
|
|
18
|
-
export declare const validateAndNormalizeConfig: (config:
|
|
19
|
-
/**
|
|
20
|
-
* Type guard to check if a JSON response is a valid API config
|
|
21
|
-
* @param json - The JSON to validate
|
|
22
|
-
* @returns True if the JSON is a valid API config
|
|
23
|
-
*/
|
|
24
|
-
export declare const isValidConfigApiResponse: (json: unknown) => json is ApiConfig;
|
|
18
|
+
export declare const validateAndNormalizeConfig: (config: Config) => Config;
|