observability-toolkit 1.8.0 → 1.8.4
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 +81 -3
- package/dist/backends/index.d.ts +119 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +57 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/index.test.d.ts +5 -0
- package/dist/backends/index.test.d.ts.map +1 -0
- package/dist/backends/index.test.js +156 -0
- package/dist/backends/index.test.js.map +1 -0
- package/dist/backends/local-jsonl-boolean-search.test.js +8 -27
- package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
- package/dist/backends/local-jsonl-logs.test.d.ts +2 -0
- package/dist/backends/local-jsonl-logs.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-logs.test.js +603 -0
- package/dist/backends/local-jsonl-logs.test.js.map +1 -0
- package/dist/backends/local-jsonl-traces.test.d.ts +2 -0
- package/dist/backends/local-jsonl-traces.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-traces.test.js +1723 -0
- package/dist/backends/local-jsonl-traces.test.js.map +1 -0
- package/dist/backends/local-jsonl.d.ts +4 -1
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +185 -1
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/local-jsonl.test.js +723 -46
- package/dist/backends/local-jsonl.test.js.map +1 -1
- package/dist/backends/signoz-api.d.ts +32 -0
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.js +231 -33
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.js +410 -63
- package/dist/backends/signoz-api.test.js.map +1 -1
- package/dist/lib/constants.d.ts +59 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +252 -6
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.js +357 -21
- package/dist/lib/constants.test.js.map +1 -1
- package/dist/lib/edge-cases.test.d.ts +11 -0
- package/dist/lib/edge-cases.test.d.ts.map +1 -0
- package/dist/lib/edge-cases.test.js +634 -0
- package/dist/lib/edge-cases.test.js.map +1 -0
- package/dist/lib/error-sanitizer.d.ts +57 -0
- package/dist/lib/error-sanitizer.d.ts.map +1 -0
- package/dist/lib/error-sanitizer.js +207 -0
- package/dist/lib/error-sanitizer.js.map +1 -0
- package/dist/lib/error-sanitizer.test.d.ts +8 -0
- package/dist/lib/error-sanitizer.test.d.ts.map +1 -0
- package/dist/lib/error-sanitizer.test.js +369 -0
- package/dist/lib/error-sanitizer.test.js.map +1 -0
- package/dist/lib/file-utils.d.ts +134 -0
- package/dist/lib/file-utils.d.ts.map +1 -1
- package/dist/lib/file-utils.js +395 -9
- package/dist/lib/file-utils.js.map +1 -1
- package/dist/lib/file-utils.test.js +444 -3
- package/dist/lib/file-utils.test.js.map +1 -1
- package/dist/lib/indexer.d.ts +9 -1
- package/dist/lib/indexer.d.ts.map +1 -1
- package/dist/lib/indexer.js +51 -2
- package/dist/lib/indexer.js.map +1 -1
- package/dist/lib/indexer.test.js +138 -20
- package/dist/lib/indexer.test.js.map +1 -1
- package/dist/lib/input-validator.d.ts +103 -0
- package/dist/lib/input-validator.d.ts.map +1 -0
- package/dist/lib/input-validator.js +250 -0
- package/dist/lib/input-validator.js.map +1 -0
- package/dist/lib/input-validator.test.d.ts +2 -0
- package/dist/lib/input-validator.test.d.ts.map +1 -0
- package/dist/lib/input-validator.test.js +287 -0
- package/dist/lib/input-validator.test.js.map +1 -0
- package/dist/lib/query-sanitizer.d.ts +143 -0
- package/dist/lib/query-sanitizer.d.ts.map +1 -0
- package/dist/lib/query-sanitizer.js +261 -0
- package/dist/lib/query-sanitizer.js.map +1 -0
- package/dist/lib/query-sanitizer.test.d.ts +5 -0
- package/dist/lib/query-sanitizer.test.d.ts.map +1 -0
- package/dist/lib/query-sanitizer.test.js +400 -0
- package/dist/lib/query-sanitizer.test.js.map +1 -0
- package/dist/lib/server-utils.d.ts +80 -0
- package/dist/lib/server-utils.d.ts.map +1 -0
- package/dist/lib/server-utils.js +141 -0
- package/dist/lib/server-utils.js.map +1 -0
- package/dist/lib/shared-schemas.d.ts +59 -0
- package/dist/lib/shared-schemas.d.ts.map +1 -0
- package/dist/lib/shared-schemas.js +58 -0
- package/dist/lib/shared-schemas.js.map +1 -0
- package/dist/lib/shared-schemas.test.d.ts +5 -0
- package/dist/lib/shared-schemas.test.d.ts.map +1 -0
- package/dist/lib/shared-schemas.test.js +106 -0
- package/dist/lib/shared-schemas.test.js.map +1 -0
- package/dist/lib/toon-encoder.d.ts +21 -0
- package/dist/lib/toon-encoder.d.ts.map +1 -0
- package/dist/lib/toon-encoder.js +46 -0
- package/dist/lib/toon-encoder.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +155 -81
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +363 -0
- package/dist/server.test.js.map +1 -1
- package/dist/test-helpers/env-utils.d.ts +65 -0
- package/dist/test-helpers/env-utils.d.ts.map +1 -0
- package/dist/test-helpers/env-utils.js +94 -0
- package/dist/test-helpers/env-utils.js.map +1 -0
- package/dist/test-helpers/file-utils.d.ts +93 -0
- package/dist/test-helpers/file-utils.d.ts.map +1 -0
- package/dist/test-helpers/file-utils.js +206 -0
- package/dist/test-helpers/file-utils.js.map +1 -0
- package/dist/test-helpers/index.d.ts +10 -0
- package/dist/test-helpers/index.d.ts.map +1 -0
- package/dist/test-helpers/index.js +28 -0
- package/dist/test-helpers/index.js.map +1 -0
- package/dist/test-helpers/mock-backends.d.ts +139 -0
- package/dist/test-helpers/mock-backends.d.ts.map +1 -0
- package/dist/test-helpers/mock-backends.js +227 -0
- package/dist/test-helpers/mock-backends.js.map +1 -0
- package/dist/test-helpers/mock-backends.test.d.ts +5 -0
- package/dist/test-helpers/mock-backends.test.d.ts.map +1 -0
- package/dist/test-helpers/mock-backends.test.js +368 -0
- package/dist/test-helpers/mock-backends.test.js.map +1 -0
- package/dist/test-helpers/schema-validators.d.ts +32 -0
- package/dist/test-helpers/schema-validators.d.ts.map +1 -0
- package/dist/test-helpers/schema-validators.js +125 -0
- package/dist/test-helpers/schema-validators.js.map +1 -0
- package/dist/test-helpers/test-data-builders.d.ts +223 -0
- package/dist/test-helpers/test-data-builders.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.js +288 -0
- package/dist/test-helpers/test-data-builders.js.map +1 -0
- package/dist/test-helpers/test-data-builders.test.d.ts +2 -0
- package/dist/test-helpers/test-data-builders.test.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.test.js +306 -0
- package/dist/test-helpers/test-data-builders.test.js.map +1 -0
- package/dist/test-helpers/tool-validators.d.ts +28 -0
- package/dist/test-helpers/tool-validators.d.ts.map +1 -0
- package/dist/test-helpers/tool-validators.js +56 -0
- package/dist/test-helpers/tool-validators.js.map +1 -0
- package/dist/tools/context-stats.d.ts +1 -0
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +9 -5
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.js +24 -10
- package/dist/tools/context-stats.test.js.map +1 -1
- package/dist/tools/get-trace-url.js +2 -2
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/health-check.js +2 -2
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/query-evaluations.d.ts +186 -0
- package/dist/tools/query-evaluations.d.ts.map +1 -0
- package/dist/tools/query-evaluations.js +351 -0
- package/dist/tools/query-evaluations.js.map +1 -0
- package/dist/tools/query-evaluations.test.d.ts +5 -0
- package/dist/tools/query-evaluations.test.d.ts.map +1 -0
- package/dist/tools/query-evaluations.test.js +733 -0
- package/dist/tools/query-evaluations.test.js.map +1 -0
- package/dist/tools/query-llm-events.d.ts +24 -18
- package/dist/tools/query-llm-events.d.ts.map +1 -1
- package/dist/tools/query-llm-events.js +103 -60
- package/dist/tools/query-llm-events.js.map +1 -1
- package/dist/tools/query-llm-events.test.js +271 -9
- package/dist/tools/query-llm-events.test.js.map +1 -1
- package/dist/tools/query-logs.d.ts +28 -20
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +85 -61
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.js +74 -145
- package/dist/tools/query-logs.test.js.map +1 -1
- package/dist/tools/query-metrics.d.ts +20 -20
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +109 -61
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.js +26 -61
- package/dist/tools/query-metrics.test.js.map +1 -1
- package/dist/tools/query-traces.d.ts +24 -22
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +95 -70
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.js +294 -90
- package/dist/tools/query-traces.test.js.map +1 -1
- package/dist/tools/setup-claudeignore.js +7 -7
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.js +4 -25
- package/dist/tools/setup-claudeignore.test.js.map +1 -1
- package/package.json +3 -4
|
@@ -0,0 +1,400 @@
|
|
|
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, sanitizeValue, 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('sanitizeValue (composable API)', () => {
|
|
270
|
+
it('should validate and escape by default', () => {
|
|
271
|
+
assert.strictEqual(sanitizeValue('hello'), 'hello');
|
|
272
|
+
assert.strictEqual(sanitizeValue("it's"), "it\\'s");
|
|
273
|
+
});
|
|
274
|
+
it('should throw for dangerous input with validation enabled', () => {
|
|
275
|
+
assert.throws(() => sanitizeValue('DROP TABLE', { fieldName: 'test' }), /Invalid test/);
|
|
276
|
+
});
|
|
277
|
+
it('should skip validation when validate: false', () => {
|
|
278
|
+
// Dangerous pattern but validation disabled - only escapes
|
|
279
|
+
assert.doesNotThrow(() => sanitizeValue('DROP TABLE', { validate: false }));
|
|
280
|
+
assert.strictEqual(sanitizeValue('DROP TABLE', { validate: false }), 'DROP TABLE');
|
|
281
|
+
});
|
|
282
|
+
it('should skip escaping when escapeString: false', () => {
|
|
283
|
+
// Only validates, no escaping
|
|
284
|
+
assert.strictEqual(sanitizeValue("it's", { escapeString: false }), "it's");
|
|
285
|
+
});
|
|
286
|
+
it('should escape LIKE wildcards when escapeLike: true', () => {
|
|
287
|
+
// 100% -> LIKE escape -> 100\% -> string escape -> 100\\%
|
|
288
|
+
assert.strictEqual(sanitizeValue('100%', { escapeLike: true }), '100\\\\%');
|
|
289
|
+
assert.strictEqual(sanitizeValue('test_value', { escapeLike: true }), 'test\\\\_value');
|
|
290
|
+
});
|
|
291
|
+
it('should allow validation-only mode', () => {
|
|
292
|
+
// validate: true (default), escapeString: false
|
|
293
|
+
assert.strictEqual(sanitizeValue('safe', { escapeString: false }), 'safe');
|
|
294
|
+
assert.throws(() => sanitizeValue('DROP TABLE', { escapeString: false, fieldName: 'input' }), /Invalid input/);
|
|
295
|
+
});
|
|
296
|
+
it('should allow escape-only mode', () => {
|
|
297
|
+
// validate: false, escapeString: true (default)
|
|
298
|
+
assert.strictEqual(sanitizeValue("it's", { validate: false }), "it\\'s");
|
|
299
|
+
assert.strictEqual(sanitizeValue('DROP TABLE', { validate: false }), 'DROP TABLE');
|
|
300
|
+
});
|
|
301
|
+
it('should match escapeFilterValueSafe behavior', () => {
|
|
302
|
+
const testCases = ['hello', "it's", 'test\\path', "test\nvalue"];
|
|
303
|
+
for (const input of testCases) {
|
|
304
|
+
assert.strictEqual(sanitizeValue(input, { fieldName: 'field' }), escapeFilterValueSafe(input, 'field'));
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
it('should match escapeLikeValueSafe behavior', () => {
|
|
308
|
+
const testCases = ['hello', '100%', 'test_value', "test%'value"];
|
|
309
|
+
for (const input of testCases) {
|
|
310
|
+
assert.strictEqual(sanitizeValue(input, { escapeLike: true, fieldName: 'field' }), escapeLikeValueSafe(input, 'field'));
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
it('should use default fieldName in error message', () => {
|
|
314
|
+
assert.throws(() => sanitizeValue('DROP TABLE'), /Invalid value/);
|
|
315
|
+
});
|
|
316
|
+
it('should handle all options disabled', () => {
|
|
317
|
+
// No validation, no escaping - passthrough
|
|
318
|
+
const result = sanitizeValue("it's DROP TABLE", {
|
|
319
|
+
validate: false,
|
|
320
|
+
escapeString: false,
|
|
321
|
+
});
|
|
322
|
+
assert.strictEqual(result, "it's DROP TABLE");
|
|
323
|
+
});
|
|
324
|
+
it('should allow empty strings by default', () => {
|
|
325
|
+
assert.strictEqual(sanitizeValue(''), '');
|
|
326
|
+
assert.strictEqual(sanitizeValue('', { allowEmpty: true }), '');
|
|
327
|
+
});
|
|
328
|
+
it('should throw for empty strings when allowEmpty: false', () => {
|
|
329
|
+
assert.throws(() => sanitizeValue('', { allowEmpty: false }), /Invalid value: empty string not allowed/);
|
|
330
|
+
});
|
|
331
|
+
it('should include fieldName in allowEmpty error message', () => {
|
|
332
|
+
assert.throws(() => sanitizeValue('', { allowEmpty: false, fieldName: 'searchTerm' }), /Invalid searchTerm: empty string not allowed/);
|
|
333
|
+
});
|
|
334
|
+
it('should allow non-empty strings when allowEmpty: false', () => {
|
|
335
|
+
assert.strictEqual(sanitizeValue('hello', { allowEmpty: false }), 'hello');
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
describe('security regression tests', () => {
|
|
339
|
+
it('should freeze input to prevent TOCTOU attacks (security regression)', () => {
|
|
340
|
+
// Regression test for TOCTOU vulnerability fix
|
|
341
|
+
// The function now uses String() to freeze the input as a primitive
|
|
342
|
+
let callCount = 0;
|
|
343
|
+
const maliciousProxy = {
|
|
344
|
+
toString() {
|
|
345
|
+
callCount++;
|
|
346
|
+
// Return safe string first time, dangerous second time
|
|
347
|
+
return callCount === 1 ? 'safe-value' : 'DROP TABLE';
|
|
348
|
+
},
|
|
349
|
+
get length() {
|
|
350
|
+
return 10;
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
// With frozen input, the result should be consistent
|
|
354
|
+
// The function should only access the value once via String()
|
|
355
|
+
const result = containsDangerousPattern(maliciousProxy);
|
|
356
|
+
// Should have called toString exactly once (via String())
|
|
357
|
+
assert.strictEqual(callCount, 1, 'Input should be frozen via String()');
|
|
358
|
+
assert.strictEqual(result, false, 'Frozen value should be "safe-value"');
|
|
359
|
+
});
|
|
360
|
+
it('should validate SQL_KEYWORDS do not contain unescaped alternation', () => {
|
|
361
|
+
// Regression test for regex injection vulnerability fix
|
|
362
|
+
// All SQL_KEYWORDS should be properly escaped
|
|
363
|
+
for (const keyword of DANGEROUS_PATTERNS) {
|
|
364
|
+
// After escaping, should not have bare pipe characters
|
|
365
|
+
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
366
|
+
assert.ok(!(/(?<!\\)\|/.test(escaped)), `Pattern "${keyword}" contains unescaped alternation after escaping`);
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
describe('edge cases', () => {
|
|
371
|
+
it('should handle strings at max length boundary', () => {
|
|
372
|
+
const longString = 'a'.repeat(10000);
|
|
373
|
+
assert.strictEqual(escapeClickHouseString(longString), longString);
|
|
374
|
+
// Exactly at MAX_QUERY_INPUT_LENGTH (10000) should pass
|
|
375
|
+
assert.strictEqual(containsDangerousPattern(longString), false);
|
|
376
|
+
});
|
|
377
|
+
it('should reject strings exceeding max length as dangerous (defense in depth)', () => {
|
|
378
|
+
const tooLongString = 'a'.repeat(10001);
|
|
379
|
+
// Exceeds MAX_QUERY_INPUT_LENGTH - treated as dangerous for defense in depth
|
|
380
|
+
assert.strictEqual(containsDangerousPattern(tooLongString), true);
|
|
381
|
+
});
|
|
382
|
+
it('should reject very long strings even if they contain safe content', () => {
|
|
383
|
+
// 15K of safe characters should still be rejected
|
|
384
|
+
const safeButLong = 'safe-string-'.repeat(1250);
|
|
385
|
+
assert.strictEqual(containsDangerousPattern(safeButLong), true);
|
|
386
|
+
});
|
|
387
|
+
it('should handle strings with only special characters', () => {
|
|
388
|
+
assert.strictEqual(escapeClickHouseString("'''"), "\\'\\'\\'");
|
|
389
|
+
assert.strictEqual(escapeClickHouseLike('%%%'), '\\%\\%\\%');
|
|
390
|
+
});
|
|
391
|
+
it('should handle null byte injection attempts', () => {
|
|
392
|
+
assert.strictEqual(escapeClickHouseString("test\0'DROP"), "test\\0\\'DROP");
|
|
393
|
+
});
|
|
394
|
+
it('should handle mixed case dangerous patterns', () => {
|
|
395
|
+
assert.strictEqual(containsDangerousPattern('DrOp TaBlE'), true);
|
|
396
|
+
assert.strictEqual(containsDangerousPattern('uNiOn SeLeCt'), true);
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
//# 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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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server utilities - Rate limiter and error classes
|
|
3
|
+
*
|
|
4
|
+
* Extracted from server.ts to allow importing in tests without starting the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
export declare const RATE_LIMIT: {
|
|
7
|
+
windowMs: number;
|
|
8
|
+
maxRequests: number;
|
|
9
|
+
bucketCount: number;
|
|
10
|
+
/** Maximum allowed bucketCount to prevent memory exhaustion */
|
|
11
|
+
maxBucketCount: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Sliding window counter rate limiter with O(1) operations and bounded memory.
|
|
15
|
+
* Uses fixed-size bucket array to track request counts per time slice.
|
|
16
|
+
*
|
|
17
|
+
* Memory usage: O(bucketCount) - fixed regardless of request volume
|
|
18
|
+
* Time complexity: O(bucketCount) for sum, but bucketCount is constant (60)
|
|
19
|
+
*/
|
|
20
|
+
export declare class RateLimiter {
|
|
21
|
+
private buckets;
|
|
22
|
+
private bucketTimestamps;
|
|
23
|
+
private readonly bucketMs;
|
|
24
|
+
private readonly windowMs;
|
|
25
|
+
private readonly maxRequests;
|
|
26
|
+
private readonly bucketCount;
|
|
27
|
+
constructor(windowMs?: number, maxRequests?: number, bucketCount?: number);
|
|
28
|
+
/**
|
|
29
|
+
* Get the bucket index for a given timestamp.
|
|
30
|
+
* Uses modulo to create circular buffer behavior.
|
|
31
|
+
*/
|
|
32
|
+
private getBucketIndex;
|
|
33
|
+
/**
|
|
34
|
+
* Clear expired buckets and return the current valid count.
|
|
35
|
+
* A bucket is expired if its timestamp is older than windowMs ago.
|
|
36
|
+
*/
|
|
37
|
+
private cleanAndCount;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a request is allowed and record it if so.
|
|
40
|
+
* O(bucketCount) time complexity where bucketCount is constant.
|
|
41
|
+
*/
|
|
42
|
+
isAllowed(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Get the number of remaining allowed requests in the current window.
|
|
45
|
+
*/
|
|
46
|
+
getRemainingRequests(): number;
|
|
47
|
+
/**
|
|
48
|
+
* Get the current memory footprint (for testing).
|
|
49
|
+
* Returns the fixed size of internal arrays.
|
|
50
|
+
*/
|
|
51
|
+
getMemoryFootprint(): {
|
|
52
|
+
bucketCount: number;
|
|
53
|
+
arraySize: number;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Reset the rate limiter state.
|
|
57
|
+
* @internal For testing ONLY - not thread-safe, do not use in production
|
|
58
|
+
*/
|
|
59
|
+
reset(): void;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Valid server initialization steps.
|
|
63
|
+
* Used for type-safe error reporting during server startup.
|
|
64
|
+
*/
|
|
65
|
+
export type ServerInitStep = 'tool-validation' | 'server-creation' | 'handler-registration' | 'transport-connection';
|
|
66
|
+
/**
|
|
67
|
+
* Server initialization error with explicit step information.
|
|
68
|
+
*
|
|
69
|
+
* Steps:
|
|
70
|
+
* - tool-validation: Validating tool configuration
|
|
71
|
+
* - server-creation: Creating MCP server instance
|
|
72
|
+
* - handler-registration: Registering request handlers
|
|
73
|
+
* - transport-connection: Connecting stdio transport
|
|
74
|
+
*/
|
|
75
|
+
export declare class ServerInitError extends Error {
|
|
76
|
+
readonly step: ServerInitStep;
|
|
77
|
+
readonly cause: unknown;
|
|
78
|
+
constructor(step: ServerInitStep, cause: unknown, message?: string);
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=server-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-utils.d.ts","sourceRoot":"","sources":["../../src/lib/server-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,eAAO,MAAM,UAAU;;;;IAIrB,+DAA+D;;CAEhE,CAAC;AAEF;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,gBAAgB,CAAW;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAGnC,QAAQ,GAAE,MAA4B,EACtC,WAAW,GAAE,MAA+B,EAC5C,WAAW,GAAE,MAA+B;IAwB9C;;;OAGG;IACH,OAAO,CAAC,cAAc;IAItB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAiBrB;;;OAGG;IACH,SAAS,IAAI,OAAO;IAuBpB;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAM9B;;;OAGG;IACH,kBAAkB,IAAI;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAOhE;;;OAGG;IACH,KAAK,IAAI,IAAI;CAId;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB,iBAAiB,GACjB,iBAAiB,GACjB,sBAAsB,GACtB,sBAAsB,CAAC;AAE3B;;;;;;;;GAQG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAEtB,IAAI,EAAE,cAAc;aACpB,KAAK,EAAE,OAAO;gBADd,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,OAAO,EAC9B,OAAO,CAAC,EAAE,MAAM;CAKnB"}
|