observability-toolkit 1.6.0 → 1.8.2

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.
Files changed (114) hide show
  1. package/README.md +221 -91
  2. package/dist/backends/index.d.ts +146 -0
  3. package/dist/backends/index.d.ts.map +1 -1
  4. package/dist/backends/index.js +65 -1
  5. package/dist/backends/index.js.map +1 -1
  6. package/dist/backends/local-jsonl-boolean-search.test.js +1 -23
  7. package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
  8. package/dist/backends/local-jsonl.d.ts +4 -1
  9. package/dist/backends/local-jsonl.d.ts.map +1 -1
  10. package/dist/backends/local-jsonl.js +216 -6
  11. package/dist/backends/local-jsonl.js.map +1 -1
  12. package/dist/backends/local-jsonl.test.js +715 -26
  13. package/dist/backends/local-jsonl.test.js.map +1 -1
  14. package/dist/backends/signoz-api.d.ts +32 -0
  15. package/dist/backends/signoz-api.d.ts.map +1 -1
  16. package/dist/backends/signoz-api.js +237 -33
  17. package/dist/backends/signoz-api.js.map +1 -1
  18. package/dist/backends/signoz-api.test.js +410 -63
  19. package/dist/backends/signoz-api.test.js.map +1 -1
  20. package/dist/lib/constants.d.ts +16 -0
  21. package/dist/lib/constants.d.ts.map +1 -1
  22. package/dist/lib/constants.js +121 -5
  23. package/dist/lib/constants.js.map +1 -1
  24. package/dist/lib/constants.test.js +202 -15
  25. package/dist/lib/constants.test.js.map +1 -1
  26. package/dist/lib/error-sanitizer.d.ts +57 -0
  27. package/dist/lib/error-sanitizer.d.ts.map +1 -0
  28. package/dist/lib/error-sanitizer.js +197 -0
  29. package/dist/lib/error-sanitizer.js.map +1 -0
  30. package/dist/lib/error-sanitizer.test.d.ts +8 -0
  31. package/dist/lib/error-sanitizer.test.d.ts.map +1 -0
  32. package/dist/lib/error-sanitizer.test.js +342 -0
  33. package/dist/lib/error-sanitizer.test.js.map +1 -0
  34. package/dist/lib/file-utils.d.ts +210 -0
  35. package/dist/lib/file-utils.d.ts.map +1 -1
  36. package/dist/lib/file-utils.js +529 -14
  37. package/dist/lib/file-utils.js.map +1 -1
  38. package/dist/lib/file-utils.test.js +657 -3
  39. package/dist/lib/file-utils.test.js.map +1 -1
  40. package/dist/lib/indexer.d.ts +19 -1
  41. package/dist/lib/indexer.d.ts.map +1 -1
  42. package/dist/lib/indexer.js +84 -8
  43. package/dist/lib/indexer.js.map +1 -1
  44. package/dist/lib/indexer.test.js +187 -16
  45. package/dist/lib/indexer.test.js.map +1 -1
  46. package/dist/lib/input-validator.d.ts +98 -0
  47. package/dist/lib/input-validator.d.ts.map +1 -0
  48. package/dist/lib/input-validator.js +245 -0
  49. package/dist/lib/input-validator.js.map +1 -0
  50. package/dist/lib/input-validator.test.d.ts +2 -0
  51. package/dist/lib/input-validator.test.d.ts.map +1 -0
  52. package/dist/lib/input-validator.test.js +287 -0
  53. package/dist/lib/input-validator.test.js.map +1 -0
  54. package/dist/lib/query-sanitizer.d.ts +95 -0
  55. package/dist/lib/query-sanitizer.d.ts.map +1 -0
  56. package/dist/lib/query-sanitizer.js +187 -0
  57. package/dist/lib/query-sanitizer.js.map +1 -0
  58. package/dist/lib/query-sanitizer.test.d.ts +5 -0
  59. package/dist/lib/query-sanitizer.test.d.ts.map +1 -0
  60. package/dist/lib/query-sanitizer.test.js +299 -0
  61. package/dist/lib/query-sanitizer.test.js.map +1 -0
  62. package/dist/server.d.ts +49 -1
  63. package/dist/server.d.ts.map +1 -1
  64. package/dist/server.js +97 -13
  65. package/dist/server.js.map +1 -1
  66. package/dist/server.test.js +202 -0
  67. package/dist/server.test.js.map +1 -1
  68. package/dist/test-helpers/file-utils.d.ts +26 -0
  69. package/dist/test-helpers/file-utils.d.ts.map +1 -0
  70. package/dist/test-helpers/file-utils.js +43 -0
  71. package/dist/test-helpers/file-utils.js.map +1 -0
  72. package/dist/test-helpers/mock-backends.d.ts +28 -0
  73. package/dist/test-helpers/mock-backends.d.ts.map +1 -0
  74. package/dist/test-helpers/mock-backends.js +31 -0
  75. package/dist/test-helpers/mock-backends.js.map +1 -0
  76. package/dist/tools/health-check.js +1 -1
  77. package/dist/tools/health-check.js.map +1 -1
  78. package/dist/tools/index.d.ts +1 -0
  79. package/dist/tools/index.d.ts.map +1 -1
  80. package/dist/tools/index.js +1 -0
  81. package/dist/tools/index.js.map +1 -1
  82. package/dist/tools/query-evaluations.d.ts +183 -0
  83. package/dist/tools/query-evaluations.d.ts.map +1 -0
  84. package/dist/tools/query-evaluations.js +351 -0
  85. package/dist/tools/query-evaluations.js.map +1 -0
  86. package/dist/tools/query-evaluations.test.d.ts +5 -0
  87. package/dist/tools/query-evaluations.test.d.ts.map +1 -0
  88. package/dist/tools/query-evaluations.test.js +743 -0
  89. package/dist/tools/query-evaluations.test.js.map +1 -0
  90. package/dist/tools/query-llm-events.d.ts +62 -11
  91. package/dist/tools/query-llm-events.d.ts.map +1 -1
  92. package/dist/tools/query-llm-events.js +97 -37
  93. package/dist/tools/query-llm-events.js.map +1 -1
  94. package/dist/tools/query-llm-events.test.js +253 -0
  95. package/dist/tools/query-llm-events.test.js.map +1 -1
  96. package/dist/tools/query-logs.d.ts +32 -18
  97. package/dist/tools/query-logs.d.ts.map +1 -1
  98. package/dist/tools/query-logs.js +77 -44
  99. package/dist/tools/query-logs.js.map +1 -1
  100. package/dist/tools/query-logs.test.js +226 -64
  101. package/dist/tools/query-logs.test.js.map +1 -1
  102. package/dist/tools/query-metrics.d.ts +24 -24
  103. package/dist/tools/query-metrics.d.ts.map +1 -1
  104. package/dist/tools/query-metrics.js +102 -54
  105. package/dist/tools/query-metrics.js.map +1 -1
  106. package/dist/tools/query-metrics.test.js +35 -36
  107. package/dist/tools/query-metrics.test.js.map +1 -1
  108. package/dist/tools/query-traces.d.ts +66 -22
  109. package/dist/tools/query-traces.d.ts.map +1 -1
  110. package/dist/tools/query-traces.js +86 -42
  111. package/dist/tools/query-traces.js.map +1 -1
  112. package/dist/tools/query-traces.test.js +458 -36
  113. package/dist/tools/query-traces.test.js.map +1 -1
  114. package/package.json +1 -3
@@ -0,0 +1,95 @@
1
+ /**
2
+ * ClickHouse-specific query escaping and input validation
3
+ *
4
+ * Provides security utilities for sanitizing user inputs before
5
+ * including them in ClickHouse queries via SigNoz API.
6
+ */
7
+ /**
8
+ * Dangerous SQL/query patterns that should never appear in user input
9
+ * Case-insensitive matching is used
10
+ */
11
+ export declare const DANGEROUS_PATTERNS: readonly string[];
12
+ /**
13
+ * Escape a string value for use in ClickHouse single-quoted strings
14
+ *
15
+ * ClickHouse uses backslash escaping for special characters.
16
+ * @see https://clickhouse.com/docs/en/sql-reference/syntax#string
17
+ *
18
+ * @param value - The string value to escape
19
+ * @returns Escaped string safe for use in single-quoted ClickHouse strings
20
+ */
21
+ export declare function escapeClickHouseString(value: string): string;
22
+ /**
23
+ * Escape LIKE wildcard characters for use in ClickHouse LIKE patterns
24
+ *
25
+ * This escapes % and _ which have special meaning in LIKE patterns.
26
+ * Use this when the user's input should be treated as a literal string
27
+ * in a LIKE clause, not as a pattern.
28
+ *
29
+ * @param value - The string value to escape for LIKE
30
+ * @returns Escaped string with LIKE wildcards escaped
31
+ */
32
+ export declare function escapeClickHouseLike(value: string): string;
33
+ /**
34
+ * Sanitize an identifier (column name, table name, etc.)
35
+ *
36
+ * Only allows alphanumeric characters, underscores, and dots.
37
+ * Removes all other characters.
38
+ *
39
+ * @param name - The identifier to sanitize
40
+ * @returns Sanitized identifier containing only safe characters
41
+ */
42
+ export declare function sanitizeIdentifier(name: string): string;
43
+ /**
44
+ * Maximum length for query input validation
45
+ * Prevents performance issues with very long inputs
46
+ */
47
+ export declare const MAX_QUERY_INPUT_LENGTH = 10000;
48
+ /**
49
+ * Check if a string contains any dangerous SQL patterns
50
+ *
51
+ * Uses case-insensitive matching and word boundary detection
52
+ * for most patterns to reduce false positives.
53
+ *
54
+ * Defense in depth: Rejects inputs exceeding MAX_QUERY_INPUT_LENGTH
55
+ * to prevent performance issues with very long strings.
56
+ *
57
+ * @param input - The input string to check
58
+ * @returns true if dangerous pattern found, false otherwise
59
+ */
60
+ export declare function containsDangerousPattern(input: string): boolean;
61
+ /**
62
+ * Validate that user input does not contain dangerous patterns
63
+ *
64
+ * Throws an error if dangerous patterns are detected, including
65
+ * the field name for better error messages.
66
+ *
67
+ * @param input - The input string to validate
68
+ * @param fieldName - Name of the field being validated (for error messages)
69
+ * @throws Error if dangerous pattern is found
70
+ */
71
+ export declare function validateQueryInput(input: string, fieldName: string): void;
72
+ /**
73
+ * Escape and validate a string value for use in WHERE clause conditions
74
+ *
75
+ * Combines validation and escaping for convenience.
76
+ *
77
+ * @param value - The value to escape
78
+ * @param fieldName - Name of the field (for error messages)
79
+ * @returns Escaped string safe for ClickHouse queries
80
+ * @throws Error if dangerous pattern is found
81
+ */
82
+ export declare function escapeFilterValueSafe(value: string, fieldName: string): string;
83
+ /**
84
+ * Escape and validate a value for use in LIKE clauses
85
+ *
86
+ * Escapes both ClickHouse string escapes and LIKE wildcards,
87
+ * and validates for dangerous patterns.
88
+ *
89
+ * @param value - The value to escape
90
+ * @param fieldName - Name of the field (for error messages)
91
+ * @returns Escaped string safe for ClickHouse LIKE clauses
92
+ * @throws Error if dangerous pattern is found
93
+ */
94
+ export declare function escapeLikeValueSafe(value: string, fieldName: string): string;
95
+ //# sourceMappingURL=query-sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-sanitizer.d.ts","sourceRoot":"","sources":["../../src/lib/query-sanitizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EAuBtC,CAAC;AAEX;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAc5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAQ,CAAC;AAU5C;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CA6B/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAIzE;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAG9E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAK5E"}
@@ -0,0 +1,187 @@
1
+ /**
2
+ * ClickHouse-specific query escaping and input validation
3
+ *
4
+ * Provides security utilities for sanitizing user inputs before
5
+ * including them in ClickHouse queries via SigNoz API.
6
+ */
7
+ /**
8
+ * Dangerous SQL/query patterns that should never appear in user input
9
+ * Case-insensitive matching is used
10
+ */
11
+ export const DANGEROUS_PATTERNS = [
12
+ 'DROP',
13
+ 'DELETE',
14
+ 'INSERT',
15
+ 'UPDATE',
16
+ 'ALTER',
17
+ 'CREATE',
18
+ 'TRUNCATE',
19
+ '--',
20
+ '/*',
21
+ '*/',
22
+ ';',
23
+ 'UNION',
24
+ 'INTO OUTFILE',
25
+ 'INTO DUMPFILE',
26
+ 'LOAD_FILE',
27
+ 'SYSTEM',
28
+ 'ATTACH',
29
+ 'DETACH',
30
+ 'RENAME',
31
+ 'OPTIMIZE',
32
+ 'GRANT',
33
+ 'REVOKE',
34
+ ];
35
+ /**
36
+ * Escape a string value for use in ClickHouse single-quoted strings
37
+ *
38
+ * ClickHouse uses backslash escaping for special characters.
39
+ * @see https://clickhouse.com/docs/en/sql-reference/syntax#string
40
+ *
41
+ * @param value - The string value to escape
42
+ * @returns Escaped string safe for use in single-quoted ClickHouse strings
43
+ */
44
+ export function escapeClickHouseString(value) {
45
+ return value
46
+ // Escape backslashes first (must be first to avoid double-escaping)
47
+ .replace(/\\/g, '\\\\')
48
+ // Escape single quotes
49
+ .replace(/'/g, "\\'")
50
+ // Escape null bytes
51
+ .replace(/\0/g, '\\0')
52
+ // Escape newlines
53
+ .replace(/\n/g, '\\n')
54
+ // Escape carriage returns
55
+ .replace(/\r/g, '\\r')
56
+ // Escape tabs
57
+ .replace(/\t/g, '\\t');
58
+ }
59
+ /**
60
+ * Escape LIKE wildcard characters for use in ClickHouse LIKE patterns
61
+ *
62
+ * This escapes % and _ which have special meaning in LIKE patterns.
63
+ * Use this when the user's input should be treated as a literal string
64
+ * in a LIKE clause, not as a pattern.
65
+ *
66
+ * @param value - The string value to escape for LIKE
67
+ * @returns Escaped string with LIKE wildcards escaped
68
+ */
69
+ export function escapeClickHouseLike(value) {
70
+ // First escape backslashes (used as escape character in LIKE)
71
+ // Then escape the LIKE wildcards
72
+ return value
73
+ .replace(/\\/g, '\\\\')
74
+ .replace(/%/g, '\\%')
75
+ .replace(/_/g, '\\_');
76
+ }
77
+ /**
78
+ * Sanitize an identifier (column name, table name, etc.)
79
+ *
80
+ * Only allows alphanumeric characters, underscores, and dots.
81
+ * Removes all other characters.
82
+ *
83
+ * @param name - The identifier to sanitize
84
+ * @returns Sanitized identifier containing only safe characters
85
+ */
86
+ export function sanitizeIdentifier(name) {
87
+ return name.replace(/[^a-zA-Z0-9_.]/g, '');
88
+ }
89
+ /**
90
+ * Maximum length for query input validation
91
+ * Prevents performance issues with very long inputs
92
+ */
93
+ export const MAX_QUERY_INPUT_LENGTH = 10000;
94
+ /**
95
+ * Escape regex metacharacters in a string for safe use in RegExp constructor
96
+ * Prevents ReDoS attacks when using user-influenced patterns
97
+ */
98
+ function escapeRegexPattern(pattern) {
99
+ return pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
100
+ }
101
+ /**
102
+ * Check if a string contains any dangerous SQL patterns
103
+ *
104
+ * Uses case-insensitive matching and word boundary detection
105
+ * for most patterns to reduce false positives.
106
+ *
107
+ * Defense in depth: Rejects inputs exceeding MAX_QUERY_INPUT_LENGTH
108
+ * to prevent performance issues with very long strings.
109
+ *
110
+ * @param input - The input string to check
111
+ * @returns true if dangerous pattern found, false otherwise
112
+ */
113
+ export function containsDangerousPattern(input) {
114
+ // Defense in depth: reject excessively long inputs
115
+ // This prevents O(n) regex operations on very large strings
116
+ if (input.length > MAX_QUERY_INPUT_LENGTH) {
117
+ console.warn(`[SECURITY] Input length ${input.length} exceeds maximum ${MAX_QUERY_INPUT_LENGTH}`);
118
+ return true; // Treat as dangerous
119
+ }
120
+ const upperInput = input.toUpperCase();
121
+ for (const pattern of DANGEROUS_PATTERNS) {
122
+ // For comment markers and semicolons, do exact substring match
123
+ if (pattern === '--' || pattern === '/*' || pattern === '*/' || pattern === ';') {
124
+ if (input.includes(pattern)) {
125
+ return true;
126
+ }
127
+ }
128
+ else {
129
+ // For SQL keywords, check with word boundaries to reduce false positives
130
+ // e.g., "DROPPED" should not match "DROP", but "DROP TABLE" should
131
+ // Escape pattern to prevent ReDoS if pattern contains regex metacharacters
132
+ const escapedPattern = escapeRegexPattern(pattern);
133
+ const regex = new RegExp(`\\b${escapedPattern}\\b`, 'i');
134
+ if (regex.test(input)) {
135
+ return true;
136
+ }
137
+ }
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Validate that user input does not contain dangerous patterns
143
+ *
144
+ * Throws an error if dangerous patterns are detected, including
145
+ * the field name for better error messages.
146
+ *
147
+ * @param input - The input string to validate
148
+ * @param fieldName - Name of the field being validated (for error messages)
149
+ * @throws Error if dangerous pattern is found
150
+ */
151
+ export function validateQueryInput(input, fieldName) {
152
+ if (containsDangerousPattern(input)) {
153
+ throw new Error(`Invalid ${fieldName}: contains potentially dangerous SQL pattern`);
154
+ }
155
+ }
156
+ /**
157
+ * Escape and validate a string value for use in WHERE clause conditions
158
+ *
159
+ * Combines validation and escaping for convenience.
160
+ *
161
+ * @param value - The value to escape
162
+ * @param fieldName - Name of the field (for error messages)
163
+ * @returns Escaped string safe for ClickHouse queries
164
+ * @throws Error if dangerous pattern is found
165
+ */
166
+ export function escapeFilterValueSafe(value, fieldName) {
167
+ validateQueryInput(value, fieldName);
168
+ return escapeClickHouseString(value);
169
+ }
170
+ /**
171
+ * Escape and validate a value for use in LIKE clauses
172
+ *
173
+ * Escapes both ClickHouse string escapes and LIKE wildcards,
174
+ * and validates for dangerous patterns.
175
+ *
176
+ * @param value - The value to escape
177
+ * @param fieldName - Name of the field (for error messages)
178
+ * @returns Escaped string safe for ClickHouse LIKE clauses
179
+ * @throws Error if dangerous pattern is found
180
+ */
181
+ export function escapeLikeValueSafe(value, fieldName) {
182
+ validateQueryInput(value, fieldName);
183
+ // First escape LIKE wildcards, then escape for string context
184
+ const likeEscaped = escapeClickHouseLike(value);
185
+ return escapeClickHouseString(likeEscaped);
186
+ }
187
+ //# sourceMappingURL=query-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-sanitizer.js","sourceRoot":"","sources":["../../src/lib/query-sanitizer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Unit tests for query-sanitizer.ts
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=query-sanitizer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-sanitizer.test.d.ts","sourceRoot":"","sources":["../../src/lib/query-sanitizer.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Unit tests for query-sanitizer.ts
3
+ */
4
+ import { describe, it } from 'node:test';
5
+ import assert from 'node:assert';
6
+ import { escapeClickHouseString, escapeClickHouseLike, sanitizeIdentifier, containsDangerousPattern, validateQueryInput, escapeFilterValueSafe, escapeLikeValueSafe, DANGEROUS_PATTERNS, } from './query-sanitizer.js';
7
+ describe('query-sanitizer', () => {
8
+ describe('DANGEROUS_PATTERNS', () => {
9
+ it('should be a non-empty array', () => {
10
+ assert.ok(Array.isArray(DANGEROUS_PATTERNS));
11
+ assert.ok(DANGEROUS_PATTERNS.length > 0);
12
+ });
13
+ it('should include common SQL injection keywords', () => {
14
+ assert.ok(DANGEROUS_PATTERNS.includes('DROP'));
15
+ assert.ok(DANGEROUS_PATTERNS.includes('DELETE'));
16
+ assert.ok(DANGEROUS_PATTERNS.includes('INSERT'));
17
+ assert.ok(DANGEROUS_PATTERNS.includes('UPDATE'));
18
+ assert.ok(DANGEROUS_PATTERNS.includes('UNION'));
19
+ });
20
+ it('should include DDL keywords', () => {
21
+ assert.ok(DANGEROUS_PATTERNS.includes('ALTER'));
22
+ assert.ok(DANGEROUS_PATTERNS.includes('CREATE'));
23
+ assert.ok(DANGEROUS_PATTERNS.includes('TRUNCATE'));
24
+ });
25
+ it('should include comment markers', () => {
26
+ assert.ok(DANGEROUS_PATTERNS.includes('--'));
27
+ assert.ok(DANGEROUS_PATTERNS.includes('/*'));
28
+ assert.ok(DANGEROUS_PATTERNS.includes('*/'));
29
+ });
30
+ it('should include statement terminator', () => {
31
+ assert.ok(DANGEROUS_PATTERNS.includes(';'));
32
+ });
33
+ it('should include file operation keywords', () => {
34
+ assert.ok(DANGEROUS_PATTERNS.includes('INTO OUTFILE'));
35
+ assert.ok(DANGEROUS_PATTERNS.includes('INTO DUMPFILE'));
36
+ assert.ok(DANGEROUS_PATTERNS.includes('LOAD_FILE'));
37
+ });
38
+ it('should include ClickHouse-specific dangerous keywords', () => {
39
+ assert.ok(DANGEROUS_PATTERNS.includes('SYSTEM'));
40
+ assert.ok(DANGEROUS_PATTERNS.includes('ATTACH'));
41
+ assert.ok(DANGEROUS_PATTERNS.includes('DETACH'));
42
+ });
43
+ });
44
+ describe('escapeClickHouseString', () => {
45
+ it('should return empty string for empty input', () => {
46
+ assert.strictEqual(escapeClickHouseString(''), '');
47
+ });
48
+ it('should return unchanged string with no special characters', () => {
49
+ assert.strictEqual(escapeClickHouseString('hello'), 'hello');
50
+ assert.strictEqual(escapeClickHouseString('test123'), 'test123');
51
+ });
52
+ it('should escape single quotes', () => {
53
+ assert.strictEqual(escapeClickHouseString("it's"), "it\\'s");
54
+ assert.strictEqual(escapeClickHouseString("test'value"), "test\\'value");
55
+ assert.strictEqual(escapeClickHouseString("''"), "\\'\\'");
56
+ });
57
+ it('should escape backslashes', () => {
58
+ assert.strictEqual(escapeClickHouseString('path\\to\\file'), 'path\\\\to\\\\file');
59
+ assert.strictEqual(escapeClickHouseString('\\'), '\\\\');
60
+ });
61
+ it('should escape backslashes before single quotes', () => {
62
+ // Important: backslash followed by quote should become \\'
63
+ assert.strictEqual(escapeClickHouseString("\\'"), "\\\\\\'");
64
+ });
65
+ it('should escape null bytes', () => {
66
+ assert.strictEqual(escapeClickHouseString('test\0value'), 'test\\0value');
67
+ assert.strictEqual(escapeClickHouseString('\0'), '\\0');
68
+ });
69
+ it('should escape newlines', () => {
70
+ assert.strictEqual(escapeClickHouseString('line1\nline2'), 'line1\\nline2');
71
+ assert.strictEqual(escapeClickHouseString('\n'), '\\n');
72
+ });
73
+ it('should escape carriage returns', () => {
74
+ assert.strictEqual(escapeClickHouseString('line1\rline2'), 'line1\\rline2');
75
+ assert.strictEqual(escapeClickHouseString('\r\n'), '\\r\\n');
76
+ });
77
+ it('should escape tabs', () => {
78
+ assert.strictEqual(escapeClickHouseString('col1\tcol2'), 'col1\\tcol2');
79
+ assert.strictEqual(escapeClickHouseString('\t'), '\\t');
80
+ });
81
+ it('should handle multiple special characters', () => {
82
+ const input = "test'\n\\\t\0";
83
+ const expected = "test\\'\\n\\\\\\t\\0";
84
+ assert.strictEqual(escapeClickHouseString(input), expected);
85
+ });
86
+ it('should handle unicode characters unchanged', () => {
87
+ assert.strictEqual(escapeClickHouseString('unicode: \u00E9\u00F1'), 'unicode: \u00E9\u00F1');
88
+ assert.strictEqual(escapeClickHouseString('emoji: \uD83D\uDE00'), 'emoji: \uD83D\uDE00');
89
+ });
90
+ });
91
+ describe('escapeClickHouseLike', () => {
92
+ it('should return empty string for empty input', () => {
93
+ assert.strictEqual(escapeClickHouseLike(''), '');
94
+ });
95
+ it('should return unchanged string with no special characters', () => {
96
+ assert.strictEqual(escapeClickHouseLike('hello'), 'hello');
97
+ assert.strictEqual(escapeClickHouseLike('test123'), 'test123');
98
+ });
99
+ it('should escape percent sign', () => {
100
+ assert.strictEqual(escapeClickHouseLike('100%'), '100\\%');
101
+ assert.strictEqual(escapeClickHouseLike('%value%'), '\\%value\\%');
102
+ });
103
+ it('should escape underscore', () => {
104
+ assert.strictEqual(escapeClickHouseLike('test_value'), 'test\\_value');
105
+ assert.strictEqual(escapeClickHouseLike('_'), '\\_');
106
+ });
107
+ it('should escape backslashes', () => {
108
+ assert.strictEqual(escapeClickHouseLike('path\\file'), 'path\\\\file');
109
+ });
110
+ it('should escape backslash before wildcards', () => {
111
+ assert.strictEqual(escapeClickHouseLike('\\%'), '\\\\\\%');
112
+ assert.strictEqual(escapeClickHouseLike('\\_'), '\\\\\\_');
113
+ });
114
+ it('should handle multiple wildcards', () => {
115
+ assert.strictEqual(escapeClickHouseLike('%_%'), '\\%\\_\\%');
116
+ });
117
+ it('should not escape other special characters', () => {
118
+ // Single quotes, etc. are handled by escapeClickHouseString
119
+ assert.strictEqual(escapeClickHouseLike("it's"), "it's");
120
+ });
121
+ });
122
+ describe('sanitizeIdentifier', () => {
123
+ it('should return empty string for empty input', () => {
124
+ assert.strictEqual(sanitizeIdentifier(''), '');
125
+ });
126
+ it('should allow alphanumeric characters', () => {
127
+ assert.strictEqual(sanitizeIdentifier('abc123'), 'abc123');
128
+ assert.strictEqual(sanitizeIdentifier('ABC123'), 'ABC123');
129
+ });
130
+ it('should allow underscores', () => {
131
+ assert.strictEqual(sanitizeIdentifier('my_column'), 'my_column');
132
+ assert.strictEqual(sanitizeIdentifier('_private'), '_private');
133
+ });
134
+ it('should allow dots for qualified names', () => {
135
+ assert.strictEqual(sanitizeIdentifier('table.column'), 'table.column');
136
+ assert.strictEqual(sanitizeIdentifier('db.table.column'), 'db.table.column');
137
+ });
138
+ it('should remove spaces', () => {
139
+ assert.strictEqual(sanitizeIdentifier('my column'), 'mycolumn');
140
+ assert.strictEqual(sanitizeIdentifier(' column '), 'column');
141
+ });
142
+ it('should remove special characters', () => {
143
+ assert.strictEqual(sanitizeIdentifier('column;DROP'), 'columnDROP');
144
+ assert.strictEqual(sanitizeIdentifier('col--comment'), 'colcomment');
145
+ assert.strictEqual(sanitizeIdentifier("col'name"), 'colname');
146
+ });
147
+ it('should remove SQL injection attempts', () => {
148
+ assert.strictEqual(sanitizeIdentifier('column; DROP TABLE users'), 'columnDROPTABLEusers');
149
+ assert.strictEqual(sanitizeIdentifier('column/*comment*/'), 'columncomment');
150
+ });
151
+ it('should handle unicode by removing it', () => {
152
+ assert.strictEqual(sanitizeIdentifier('column\u00E9'), 'column');
153
+ assert.strictEqual(sanitizeIdentifier('\u4E2D\u6587'), '');
154
+ });
155
+ });
156
+ describe('containsDangerousPattern', () => {
157
+ it('should return false for empty string', () => {
158
+ assert.strictEqual(containsDangerousPattern(''), false);
159
+ });
160
+ it('should return false for normal strings', () => {
161
+ assert.strictEqual(containsDangerousPattern('hello'), false);
162
+ assert.strictEqual(containsDangerousPattern('test123'), false);
163
+ assert.strictEqual(containsDangerousPattern('my-service-name'), false);
164
+ });
165
+ it('should detect DROP keyword', () => {
166
+ assert.strictEqual(containsDangerousPattern('DROP TABLE'), true);
167
+ assert.strictEqual(containsDangerousPattern('drop table'), true);
168
+ assert.strictEqual(containsDangerousPattern('DrOp TaBlE'), true);
169
+ });
170
+ it('should detect DELETE keyword', () => {
171
+ assert.strictEqual(containsDangerousPattern('DELETE FROM'), true);
172
+ assert.strictEqual(containsDangerousPattern('delete from users'), true);
173
+ });
174
+ it('should detect INSERT keyword', () => {
175
+ assert.strictEqual(containsDangerousPattern('INSERT INTO'), true);
176
+ assert.strictEqual(containsDangerousPattern('insert values'), true);
177
+ });
178
+ it('should detect UPDATE keyword', () => {
179
+ assert.strictEqual(containsDangerousPattern('UPDATE users SET'), true);
180
+ assert.strictEqual(containsDangerousPattern('update table'), true);
181
+ });
182
+ it('should detect UNION keyword', () => {
183
+ assert.strictEqual(containsDangerousPattern('UNION SELECT'), true);
184
+ assert.strictEqual(containsDangerousPattern('union all'), true);
185
+ });
186
+ it('should detect comment markers', () => {
187
+ assert.strictEqual(containsDangerousPattern('value -- comment'), true);
188
+ assert.strictEqual(containsDangerousPattern('value /* comment */'), true);
189
+ assert.strictEqual(containsDangerousPattern('*/'), true);
190
+ });
191
+ it('should detect semicolon', () => {
192
+ assert.strictEqual(containsDangerousPattern('value; DROP'), true);
193
+ assert.strictEqual(containsDangerousPattern(';'), true);
194
+ });
195
+ it('should detect file operations', () => {
196
+ assert.strictEqual(containsDangerousPattern("INTO OUTFILE '/tmp/file'"), true);
197
+ assert.strictEqual(containsDangerousPattern('INTO DUMPFILE'), true);
198
+ assert.strictEqual(containsDangerousPattern('LOAD_FILE()'), true);
199
+ });
200
+ it('should detect ClickHouse system commands', () => {
201
+ assert.strictEqual(containsDangerousPattern('SYSTEM RELOAD'), true);
202
+ assert.strictEqual(containsDangerousPattern('ATTACH TABLE'), true);
203
+ assert.strictEqual(containsDangerousPattern('DETACH TABLE'), true);
204
+ });
205
+ it('should not flag words containing keywords', () => {
206
+ // "DROPPED" contains "DROP" but is not the DROP command
207
+ assert.strictEqual(containsDangerousPattern('DROPPED'), false);
208
+ assert.strictEqual(containsDangerousPattern('DELETED'), false);
209
+ assert.strictEqual(containsDangerousPattern('UPDATED'), false);
210
+ assert.strictEqual(containsDangerousPattern('INSERTED'), false);
211
+ });
212
+ it('should detect keywords at word boundaries', () => {
213
+ assert.strictEqual(containsDangerousPattern('DROP'), true);
214
+ assert.strictEqual(containsDangerousPattern('(DROP)'), true);
215
+ assert.strictEqual(containsDangerousPattern('x DROP x'), true);
216
+ });
217
+ });
218
+ describe('validateQueryInput', () => {
219
+ it('should not throw for safe input', () => {
220
+ assert.doesNotThrow(() => validateQueryInput('hello', 'serviceName'));
221
+ assert.doesNotThrow(() => validateQueryInput('test-123', 'traceId'));
222
+ assert.doesNotThrow(() => validateQueryInput('my.service.name', 'serviceName'));
223
+ });
224
+ it('should throw for dangerous input', () => {
225
+ assert.throws(() => validateQueryInput('DROP TABLE', 'serviceName'), /Invalid serviceName: contains potentially dangerous SQL pattern/);
226
+ });
227
+ it('should include field name in error message', () => {
228
+ assert.throws(() => validateQueryInput('DROP TABLE', 'traceId'), /Invalid traceId/);
229
+ assert.throws(() => validateQueryInput('DELETE FROM', 'spanName'), /Invalid spanName/);
230
+ });
231
+ it('should throw for comment injection', () => {
232
+ assert.throws(() => validateQueryInput('value -- comment', 'search'), /Invalid search/);
233
+ });
234
+ it('should throw for semicolon injection', () => {
235
+ assert.throws(() => validateQueryInput('value; DROP', 'filter'), /Invalid filter/);
236
+ });
237
+ });
238
+ describe('escapeFilterValueSafe', () => {
239
+ it('should escape and validate safe input', () => {
240
+ assert.strictEqual(escapeFilterValueSafe('hello', 'field'), 'hello');
241
+ assert.strictEqual(escapeFilterValueSafe("it's", 'field'), "it\\'s");
242
+ });
243
+ it('should throw for dangerous input', () => {
244
+ assert.throws(() => escapeFilterValueSafe('DROP TABLE', 'serviceName'), /Invalid serviceName/);
245
+ });
246
+ it('should escape special characters in safe input', () => {
247
+ assert.strictEqual(escapeFilterValueSafe("test\nvalue", 'field'), 'test\\nvalue');
248
+ assert.strictEqual(escapeFilterValueSafe('test\\path', 'field'), 'test\\\\path');
249
+ });
250
+ });
251
+ describe('escapeLikeValueSafe', () => {
252
+ it('should escape LIKE wildcards and validate', () => {
253
+ // 100% -> escapeClickHouseLike -> 100\% -> escapeClickHouseString -> 100\\%
254
+ assert.strictEqual(escapeLikeValueSafe('100%', 'field'), '100\\\\%');
255
+ // test_value -> escapeClickHouseLike -> test\_value -> escapeClickHouseString -> test\\_value
256
+ assert.strictEqual(escapeLikeValueSafe('test_value', 'field'), 'test\\\\_value');
257
+ });
258
+ it('should throw for dangerous input', () => {
259
+ assert.throws(() => escapeLikeValueSafe('UNION SELECT', 'search'), /Invalid search/);
260
+ });
261
+ it('should handle combined escaping', () => {
262
+ // Input: test%'value
263
+ // After LIKE escape: test\%'value (% escaped for LIKE)
264
+ // After string escape: test\\%\'value (\ and ' escaped for string)
265
+ const result = escapeLikeValueSafe("test%'value", 'field');
266
+ assert.strictEqual(result, "test\\\\%\\'value");
267
+ });
268
+ });
269
+ describe('edge cases', () => {
270
+ it('should handle strings at max length boundary', () => {
271
+ const longString = 'a'.repeat(10000);
272
+ assert.strictEqual(escapeClickHouseString(longString), longString);
273
+ // Exactly at MAX_QUERY_INPUT_LENGTH (10000) should pass
274
+ assert.strictEqual(containsDangerousPattern(longString), false);
275
+ });
276
+ it('should reject strings exceeding max length as dangerous (defense in depth)', () => {
277
+ const tooLongString = 'a'.repeat(10001);
278
+ // Exceeds MAX_QUERY_INPUT_LENGTH - treated as dangerous for defense in depth
279
+ assert.strictEqual(containsDangerousPattern(tooLongString), true);
280
+ });
281
+ it('should reject very long strings even if they contain safe content', () => {
282
+ // 15K of safe characters should still be rejected
283
+ const safeButLong = 'safe-string-'.repeat(1250);
284
+ assert.strictEqual(containsDangerousPattern(safeButLong), true);
285
+ });
286
+ it('should handle strings with only special characters', () => {
287
+ assert.strictEqual(escapeClickHouseString("'''"), "\\'\\'\\'");
288
+ assert.strictEqual(escapeClickHouseLike('%%%'), '\\%\\%\\%');
289
+ });
290
+ it('should handle null byte injection attempts', () => {
291
+ assert.strictEqual(escapeClickHouseString("test\0'DROP"), "test\\0\\'DROP");
292
+ });
293
+ it('should handle mixed case dangerous patterns', () => {
294
+ assert.strictEqual(containsDangerousPattern('DrOp TaBlE'), true);
295
+ assert.strictEqual(containsDangerousPattern('uNiOn SeLeCt'), true);
296
+ });
297
+ });
298
+ });
299
+ //# sourceMappingURL=query-sanitizer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-sanitizer.test.js","sourceRoot":"","sources":["../../src/lib/query-sanitizer.test.ts"],"names":[],"mappings":"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"}