@gblikas/querykit 0.0.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.
Files changed (118) hide show
  1. package/.cursor/BUGBOT.md +21 -0
  2. package/.cursor/rules/01-project-structure.mdc +77 -0
  3. package/.cursor/rules/02-typescript-standards.mdc +105 -0
  4. package/.cursor/rules/03-testing-standards.mdc +78 -0
  5. package/.cursor/rules/04-query-language.mdc +79 -0
  6. package/.cursor/rules/05-solid-principles.mdc +118 -0
  7. package/.cursor/rules/liqe-readme-docs.mdc +438 -0
  8. package/.devcontainer/devcontainer.json +25 -0
  9. package/.eslintignore +1 -0
  10. package/.eslintrc.js +39 -0
  11. package/.github/dependabot.yml +12 -0
  12. package/.github/workflows/ci.yml +114 -0
  13. package/.github/workflows/publish.yml +61 -0
  14. package/.husky/pre-commit +30 -0
  15. package/.prettierrc +10 -0
  16. package/CONTRIBUTING.md +187 -0
  17. package/LICENSE +674 -0
  18. package/README.md +237 -0
  19. package/dist/adapters/drizzle/index.d.ts +122 -0
  20. package/dist/adapters/drizzle/index.js +166 -0
  21. package/dist/adapters/index.d.ts +7 -0
  22. package/dist/adapters/index.js +25 -0
  23. package/dist/adapters/types.d.ts +60 -0
  24. package/dist/adapters/types.js +8 -0
  25. package/dist/index.d.ts +75 -0
  26. package/dist/index.js +118 -0
  27. package/dist/parser/index.d.ts +2 -0
  28. package/dist/parser/index.js +18 -0
  29. package/dist/parser/parser.d.ts +51 -0
  30. package/dist/parser/parser.js +201 -0
  31. package/dist/parser/types.d.ts +68 -0
  32. package/dist/parser/types.js +5 -0
  33. package/dist/query/builder.d.ts +61 -0
  34. package/dist/query/builder.js +188 -0
  35. package/dist/query/index.d.ts +2 -0
  36. package/dist/query/index.js +18 -0
  37. package/dist/query/types.d.ts +79 -0
  38. package/dist/query/types.js +2 -0
  39. package/dist/security/index.d.ts +2 -0
  40. package/dist/security/index.js +18 -0
  41. package/dist/security/types.d.ts +181 -0
  42. package/dist/security/types.js +43 -0
  43. package/dist/security/validator.d.ts +191 -0
  44. package/dist/security/validator.js +344 -0
  45. package/dist/translators/drizzle/index.d.ts +73 -0
  46. package/dist/translators/drizzle/index.js +260 -0
  47. package/dist/translators/index.d.ts +8 -0
  48. package/dist/translators/index.js +27 -0
  49. package/dist/translators/sql/index.d.ts +108 -0
  50. package/dist/translators/sql/index.js +252 -0
  51. package/dist/translators/types.d.ts +39 -0
  52. package/dist/translators/types.js +8 -0
  53. package/examples/qk-next/README.md +35 -0
  54. package/examples/qk-next/app/favicon.ico +0 -0
  55. package/examples/qk-next/app/globals.css +122 -0
  56. package/examples/qk-next/app/layout.tsx +121 -0
  57. package/examples/qk-next/app/page.tsx +813 -0
  58. package/examples/qk-next/app/providers.tsx +80 -0
  59. package/examples/qk-next/components/aurora-background.tsx +12 -0
  60. package/examples/qk-next/components/github-stars.tsx +51 -0
  61. package/examples/qk-next/components/mode-toggle.tsx +27 -0
  62. package/examples/qk-next/components/reactbits/blocks/Backgrounds/Aurora/Aurora.tsx +217 -0
  63. package/examples/qk-next/components/reactbits/blocks/Backgrounds/LightRays/LightRays.tsx +474 -0
  64. package/examples/qk-next/components/theme-provider.tsx +11 -0
  65. package/examples/qk-next/components/ui/card.tsx +92 -0
  66. package/examples/qk-next/components/ui/command.tsx +184 -0
  67. package/examples/qk-next/components/ui/dialog.tsx +143 -0
  68. package/examples/qk-next/components/ui/drawer.tsx +135 -0
  69. package/examples/qk-next/components/ui/hover-card.tsx +44 -0
  70. package/examples/qk-next/components/ui/icons.tsx +148 -0
  71. package/examples/qk-next/components/ui/sonner.tsx +26 -0
  72. package/examples/qk-next/components/ui/table.tsx +117 -0
  73. package/examples/qk-next/components.json +21 -0
  74. package/examples/qk-next/eslint.config.mjs +21 -0
  75. package/examples/qk-next/jsrepo.json +13 -0
  76. package/examples/qk-next/lib/utils.ts +6 -0
  77. package/examples/qk-next/next.config.ts +8 -0
  78. package/examples/qk-next/package.json +48 -0
  79. package/examples/qk-next/pnpm-lock.yaml +5558 -0
  80. package/examples/qk-next/postcss.config.mjs +5 -0
  81. package/examples/qk-next/public/file.svg +1 -0
  82. package/examples/qk-next/public/globe.svg +1 -0
  83. package/examples/qk-next/public/next.svg +1 -0
  84. package/examples/qk-next/public/vercel.svg +1 -0
  85. package/examples/qk-next/public/window.svg +1 -0
  86. package/examples/qk-next/tsconfig.json +42 -0
  87. package/examples/qk-next/types/sonner.d.ts +3 -0
  88. package/jest.config.js +26 -0
  89. package/package.json +51 -0
  90. package/src/adapters/drizzle/drizzle-adapter.test.ts +115 -0
  91. package/src/adapters/drizzle/index.ts +299 -0
  92. package/src/adapters/index.ts +11 -0
  93. package/src/adapters/types.ts +72 -0
  94. package/src/index.ts +194 -0
  95. package/src/integration.test.ts +202 -0
  96. package/src/parser/index.ts +2 -0
  97. package/src/parser/parser.test.ts +1056 -0
  98. package/src/parser/parser.ts +268 -0
  99. package/src/parser/types.ts +97 -0
  100. package/src/query/builder.test.ts +272 -0
  101. package/src/query/builder.ts +274 -0
  102. package/src/query/index.ts +2 -0
  103. package/src/query/types.ts +107 -0
  104. package/src/security/index.ts +2 -0
  105. package/src/security/types.ts +210 -0
  106. package/src/security/validator.test.ts +459 -0
  107. package/src/security/validator.ts +395 -0
  108. package/src/security.test.ts +366 -0
  109. package/src/translators/drizzle/drizzle-translator.test.ts +128 -0
  110. package/src/translators/drizzle/index.test.ts +45 -0
  111. package/src/translators/drizzle/index.ts +346 -0
  112. package/src/translators/index.ts +14 -0
  113. package/src/translators/sql/index.test.ts +45 -0
  114. package/src/translators/sql/index.ts +331 -0
  115. package/src/translators/sql/sql-translator.test.ts +419 -0
  116. package/src/translators/types.ts +44 -0
  117. package/src/types/sonner.d.ts +3 -0
  118. package/tsconfig.json +34 -0
@@ -0,0 +1,191 @@
1
+ import { QueryExpression } from '../parser/types';
2
+ import { ISecurityOptions } from './types';
3
+ /**
4
+ * Error thrown when a query violates security constraints
5
+ *
6
+ * This error is thrown when a query attempts to bypass security settings
7
+ * such as accessing unauthorized fields, exceeding complexity limits,
8
+ * or using potentially dangerous patterns.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * try {
13
+ * queryValidator.validate(parsedQuery);
14
+ * } catch (error) {
15
+ * if (error instanceof QuerySecurityError) {
16
+ * console.error('Security violation:', error.message);
17
+ * // Return appropriate error response to client
18
+ * }
19
+ * }
20
+ * ```
21
+ */
22
+ export declare class QuerySecurityError extends Error {
23
+ constructor(message: string);
24
+ }
25
+ /**
26
+ * Validates query expressions against security constraints
27
+ *
28
+ * The QuerySecurityValidator ensures that queries comply with the security
29
+ * rules defined in the ISecurityOptions. It should be used before executing
30
+ * any query to prevent potential security issues or resource exhaustion.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { QuerySecurityValidator, ISecurityOptions } from 'querykit';
35
+ * import { parseQuery } from 'querykit/parser';
36
+ *
37
+ * // Define security options
38
+ * const securityOptions: ISecurityOptions = {
39
+ * allowedFields: ['id', 'name', 'createdAt'],
40
+ * denyFields: ['password'],
41
+ * maxQueryDepth: 5
42
+ * };
43
+ *
44
+ * // Create validator
45
+ * const validator = new QuerySecurityValidator(securityOptions);
46
+ *
47
+ * // Use in API endpoint
48
+ * app.get('/users', (req, res) => {
49
+ * try {
50
+ * // Parse the query from request
51
+ * const queryStr = req.query.filter || '';
52
+ * const parsedQuery = parseQuery(queryStr);
53
+ *
54
+ * // Validate against security rules
55
+ * validator.validate(parsedQuery, userSchema);
56
+ *
57
+ * // If validation passes, execute the query
58
+ * const results = executeQuery(parsedQuery);
59
+ * res.json(results);
60
+ * } catch (error) {
61
+ * if (error instanceof QuerySecurityError) {
62
+ * res.status(400).json({ error: error.message });
63
+ * } else {
64
+ * res.status(500).json({ error: 'Server error' });
65
+ * }
66
+ * }
67
+ * });
68
+ * ```
69
+ */
70
+ export declare class QuerySecurityValidator {
71
+ private options;
72
+ /**
73
+ * Creates a new QuerySecurityValidator instance
74
+ *
75
+ * @param options - Security options to apply. If not provided, default options will be used.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * // Create with default security settings
80
+ * const defaultValidator = new QuerySecurityValidator();
81
+ *
82
+ * // Create with custom security settings
83
+ * const strictValidator = new QuerySecurityValidator({
84
+ * maxQueryDepth: 3,
85
+ * maxClauseCount: 10,
86
+ * maxValueLength: 500
87
+ * });
88
+ * ```
89
+ */
90
+ constructor(options?: ISecurityOptions);
91
+ /**
92
+ * Validate a query expression against security constraints
93
+ *
94
+ * This is the main method that should be called before executing any query.
95
+ * It performs all security checks defined in the options.
96
+ *
97
+ * @param expression - The parsed query expression to validate
98
+ * @param schema - Optional schema definition to validate fields against
99
+ * @throws {QuerySecurityError} If the query violates any security constraints
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { parseQuery } from 'querykit/parser';
104
+ *
105
+ * const validator = new QuerySecurityValidator();
106
+ *
107
+ * // Simple validation
108
+ * try {
109
+ * const query = parseQuery('user.name == "John" && user.priority > 2');
110
+ * validator.validate(query);
111
+ * // Query is safe to execute
112
+ * } catch (error) {
113
+ * // Handle security violation
114
+ * }
115
+ *
116
+ * // Validation with schema
117
+ * const userSchema = {
118
+ * user: {
119
+ * id: 'number',
120
+ * name: 'string',
121
+ * priority: 'number',
122
+ * email: 'string'
123
+ * }
124
+ * };
125
+ *
126
+ * try {
127
+ * const query = parseQuery('user.email == "john@example.com"');
128
+ * validator.validate(query, userSchema);
129
+ * // Query is safe to execute
130
+ * } catch (error) {
131
+ * // Handle security violation
132
+ * }
133
+ * ```
134
+ */
135
+ validate(expression: QueryExpression, schema?: Record<string, Record<string, unknown>>): void;
136
+ /**
137
+ * Validate that query fields are allowed and not denied
138
+ *
139
+ * @private
140
+ * @param expression - The query expression to validate
141
+ * @param schema - Optional schema definition to validate fields against
142
+ */
143
+ private validateFields;
144
+ /**
145
+ * Validate that query depth does not exceed the maximum
146
+ *
147
+ * @private
148
+ * @param expression - The query expression to validate
149
+ * @param currentDepth - The current depth level in the recursion
150
+ */
151
+ private validateQueryDepth;
152
+ /**
153
+ * Validate that the number of clauses does not exceed the maximum
154
+ *
155
+ * @private
156
+ * @param expression - The query expression to validate
157
+ */
158
+ private validateClauseCount;
159
+ /**
160
+ * Count the number of clauses in a query expression
161
+ *
162
+ * @private
163
+ * @param expression - The query expression to count clauses in
164
+ * @returns The total number of comparison clauses
165
+ */
166
+ private countClauses;
167
+ /**
168
+ * Validate that string values do not exceed maximum length
169
+ * Security: Enhanced to prevent type confusion attacks via arrays/objects
170
+ *
171
+ * @private
172
+ * @param expression - The query expression to validate
173
+ */
174
+ private validateValueLengths;
175
+ /**
176
+ * Sanitize wildcard patterns in LIKE queries to prevent regex DoS
177
+ * Security: Enhanced to prevent ReDoS attacks via catastrophic backtracking
178
+ *
179
+ * @private
180
+ * @param expression - The query expression to sanitize
181
+ */
182
+ private sanitizeWildcards;
183
+ /**
184
+ * Collect all field names used in the query
185
+ *
186
+ * @private
187
+ * @param expression - The query expression to collect fields from
188
+ * @param fieldSet - Set to store the collected field names
189
+ */
190
+ private collectFields;
191
+ }
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QuerySecurityValidator = exports.QuerySecurityError = void 0;
4
+ const types_1 = require("./types");
5
+ /**
6
+ * Error thrown when a query violates security constraints
7
+ *
8
+ * This error is thrown when a query attempts to bypass security settings
9
+ * such as accessing unauthorized fields, exceeding complexity limits,
10
+ * or using potentially dangerous patterns.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * try {
15
+ * queryValidator.validate(parsedQuery);
16
+ * } catch (error) {
17
+ * if (error instanceof QuerySecurityError) {
18
+ * console.error('Security violation:', error.message);
19
+ * // Return appropriate error response to client
20
+ * }
21
+ * }
22
+ * ```
23
+ */
24
+ class QuerySecurityError extends Error {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = 'QuerySecurityError';
28
+ }
29
+ }
30
+ exports.QuerySecurityError = QuerySecurityError;
31
+ /**
32
+ * Validates query expressions against security constraints
33
+ *
34
+ * The QuerySecurityValidator ensures that queries comply with the security
35
+ * rules defined in the ISecurityOptions. It should be used before executing
36
+ * any query to prevent potential security issues or resource exhaustion.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * import { QuerySecurityValidator, ISecurityOptions } from 'querykit';
41
+ * import { parseQuery } from 'querykit/parser';
42
+ *
43
+ * // Define security options
44
+ * const securityOptions: ISecurityOptions = {
45
+ * allowedFields: ['id', 'name', 'createdAt'],
46
+ * denyFields: ['password'],
47
+ * maxQueryDepth: 5
48
+ * };
49
+ *
50
+ * // Create validator
51
+ * const validator = new QuerySecurityValidator(securityOptions);
52
+ *
53
+ * // Use in API endpoint
54
+ * app.get('/users', (req, res) => {
55
+ * try {
56
+ * // Parse the query from request
57
+ * const queryStr = req.query.filter || '';
58
+ * const parsedQuery = parseQuery(queryStr);
59
+ *
60
+ * // Validate against security rules
61
+ * validator.validate(parsedQuery, userSchema);
62
+ *
63
+ * // If validation passes, execute the query
64
+ * const results = executeQuery(parsedQuery);
65
+ * res.json(results);
66
+ * } catch (error) {
67
+ * if (error instanceof QuerySecurityError) {
68
+ * res.status(400).json({ error: error.message });
69
+ * } else {
70
+ * res.status(500).json({ error: 'Server error' });
71
+ * }
72
+ * }
73
+ * });
74
+ * ```
75
+ */
76
+ class QuerySecurityValidator {
77
+ /**
78
+ * Creates a new QuerySecurityValidator instance
79
+ *
80
+ * @param options - Security options to apply. If not provided, default options will be used.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Create with default security settings
85
+ * const defaultValidator = new QuerySecurityValidator();
86
+ *
87
+ * // Create with custom security settings
88
+ * const strictValidator = new QuerySecurityValidator({
89
+ * maxQueryDepth: 3,
90
+ * maxClauseCount: 10,
91
+ * maxValueLength: 500
92
+ * });
93
+ * ```
94
+ */
95
+ constructor(options = {}) {
96
+ this.options = {
97
+ ...types_1.DEFAULT_SECURITY_OPTIONS,
98
+ ...options
99
+ };
100
+ }
101
+ /**
102
+ * Validate a query expression against security constraints
103
+ *
104
+ * This is the main method that should be called before executing any query.
105
+ * It performs all security checks defined in the options.
106
+ *
107
+ * @param expression - The parsed query expression to validate
108
+ * @param schema - Optional schema definition to validate fields against
109
+ * @throws {QuerySecurityError} If the query violates any security constraints
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import { parseQuery } from 'querykit/parser';
114
+ *
115
+ * const validator = new QuerySecurityValidator();
116
+ *
117
+ * // Simple validation
118
+ * try {
119
+ * const query = parseQuery('user.name == "John" && user.priority > 2');
120
+ * validator.validate(query);
121
+ * // Query is safe to execute
122
+ * } catch (error) {
123
+ * // Handle security violation
124
+ * }
125
+ *
126
+ * // Validation with schema
127
+ * const userSchema = {
128
+ * user: {
129
+ * id: 'number',
130
+ * name: 'string',
131
+ * priority: 'number',
132
+ * email: 'string'
133
+ * }
134
+ * };
135
+ *
136
+ * try {
137
+ * const query = parseQuery('user.email == "john@example.com"');
138
+ * validator.validate(query, userSchema);
139
+ * // Query is safe to execute
140
+ * } catch (error) {
141
+ * // Handle security violation
142
+ * }
143
+ * ```
144
+ */
145
+ validate(expression, schema) {
146
+ // Check for field restrictions if specified
147
+ this.validateFields(expression, schema);
148
+ // Check query complexity
149
+ this.validateQueryDepth(expression, 0);
150
+ this.validateClauseCount(expression);
151
+ // Check value lengths
152
+ this.validateValueLengths(expression);
153
+ // Sanitize wildcard patterns if enabled
154
+ if (this.options.sanitizeWildcards) {
155
+ this.sanitizeWildcards(expression);
156
+ }
157
+ }
158
+ /**
159
+ * Validate that query fields are allowed and not denied
160
+ *
161
+ * @private
162
+ * @param expression - The query expression to validate
163
+ * @param schema - Optional schema definition to validate fields against
164
+ */
165
+ validateFields(expression, schema) {
166
+ const fieldSet = new Set();
167
+ this.collectFields(expression, fieldSet);
168
+ // Create a set of allowed fields
169
+ const allowedFields = new Set();
170
+ // If allowedFields is empty and schema is provided, use schema fields
171
+ if (this.options.allowedFields.length === 0 && schema) {
172
+ for (const table in schema) {
173
+ if (typeof schema[table] === 'object') {
174
+ for (const field in schema[table]) {
175
+ allowedFields.add(`${table}.${field}`);
176
+ allowedFields.add(field);
177
+ }
178
+ }
179
+ }
180
+ }
181
+ else {
182
+ this.options.allowedFields.forEach(field => allowedFields.add(field));
183
+ }
184
+ // Create a set of denied fields
185
+ const deniedFields = new Set(this.options.denyFields);
186
+ // Check each field in the query
187
+ for (const field of fieldSet) {
188
+ // Security fix: Generic error to prevent field enumeration attacks
189
+ if (deniedFields.has(field) ||
190
+ (allowedFields.size > 0 && !allowedFields.has(field))) {
191
+ throw new QuerySecurityError('Invalid query parameters');
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Validate that query depth does not exceed the maximum
197
+ *
198
+ * @private
199
+ * @param expression - The query expression to validate
200
+ * @param currentDepth - The current depth level in the recursion
201
+ */
202
+ validateQueryDepth(expression, currentDepth) {
203
+ if (currentDepth > this.options.maxQueryDepth) {
204
+ throw new QuerySecurityError(`Query exceeds maximum depth of ${this.options.maxQueryDepth}`);
205
+ }
206
+ if (expression.type === 'logical') {
207
+ this.validateQueryDepth(expression.left, currentDepth + 1);
208
+ if (expression.right) {
209
+ this.validateQueryDepth(expression.right, currentDepth + 1);
210
+ }
211
+ }
212
+ }
213
+ /**
214
+ * Validate that the number of clauses does not exceed the maximum
215
+ *
216
+ * @private
217
+ * @param expression - The query expression to validate
218
+ */
219
+ validateClauseCount(expression) {
220
+ const count = this.countClauses(expression);
221
+ if (count > this.options.maxClauseCount) {
222
+ throw new QuerySecurityError(`Query exceeds maximum clause count of ${this.options.maxClauseCount} (found ${count})`);
223
+ }
224
+ }
225
+ /**
226
+ * Count the number of clauses in a query expression
227
+ *
228
+ * @private
229
+ * @param expression - The query expression to count clauses in
230
+ * @returns The total number of comparison clauses
231
+ */
232
+ countClauses(expression) {
233
+ if (expression.type === 'comparison') {
234
+ return 1;
235
+ }
236
+ let count = 0;
237
+ count += this.countClauses(expression.left);
238
+ if (expression.right) {
239
+ count += this.countClauses(expression.right);
240
+ }
241
+ return count;
242
+ }
243
+ /**
244
+ * Validate that string values do not exceed maximum length
245
+ * Security: Enhanced to prevent type confusion attacks via arrays/objects
246
+ *
247
+ * @private
248
+ * @param expression - The query expression to validate
249
+ */
250
+ validateValueLengths(expression) {
251
+ if (expression.type === 'comparison') {
252
+ const { value } = expression;
253
+ // Check string values
254
+ if (typeof value === 'string' &&
255
+ value.length > this.options.maxValueLength) {
256
+ throw new QuerySecurityError(`Query contains a string value that exceeds maximum length of ${this.options.maxValueLength} characters`);
257
+ }
258
+ // Security fix: Enhanced array validation to prevent bypass
259
+ if (Array.isArray(value)) {
260
+ if (value.length > 100) {
261
+ // Limit array size
262
+ throw new QuerySecurityError('Array values cannot exceed 100 items');
263
+ }
264
+ for (const item of value) {
265
+ if (typeof item === 'string' &&
266
+ item.length > this.options.maxValueLength) {
267
+ throw new QuerySecurityError(`Query contains a string value in array that exceeds maximum length of ${this.options.maxValueLength} characters`);
268
+ }
269
+ // Security fix: Prevent object injection in arrays
270
+ if (typeof item === 'object' && item !== null) {
271
+ throw new QuerySecurityError('Object values are not allowed in arrays');
272
+ }
273
+ }
274
+ }
275
+ // Security fix: Prevent object values entirely
276
+ if (typeof value === 'object' &&
277
+ value !== null &&
278
+ !Array.isArray(value)) {
279
+ throw new QuerySecurityError('Object values are not allowed');
280
+ }
281
+ }
282
+ else {
283
+ this.validateValueLengths(expression.left);
284
+ if (expression.right) {
285
+ this.validateValueLengths(expression.right);
286
+ }
287
+ }
288
+ }
289
+ /**
290
+ * Sanitize wildcard patterns in LIKE queries to prevent regex DoS
291
+ * Security: Enhanced to prevent ReDoS attacks via catastrophic backtracking
292
+ *
293
+ * @private
294
+ * @param expression - The query expression to sanitize
295
+ */
296
+ sanitizeWildcards(expression) {
297
+ if (expression.type === 'comparison') {
298
+ const { operator, value } = expression;
299
+ // Only sanitize LIKE operators with string values
300
+ if (operator === 'LIKE' && typeof value === 'string') {
301
+ // Security fix: Count wildcards to prevent ReDoS
302
+ const wildcardCount = (value.match(/[*?]/g) || []).length;
303
+ if (wildcardCount > 10) {
304
+ throw new QuerySecurityError('Excessive wildcard usage');
305
+ }
306
+ // Security fix: Prevent alternating patterns that cause catastrophic backtracking
307
+ // Pattern like "*a*b*c*d*e*f" (alternating * and non-* chars)
308
+ if (/(\*[^*]+){5,}/.test(value)) {
309
+ throw new QuerySecurityError('Complex wildcard patterns not allowed');
310
+ }
311
+ // Enhanced sanitization: limit consecutive wildcards
312
+ const sanitized = value
313
+ .replace(/\*{2,}/g, '*') // Limit consecutive asterisks
314
+ .replace(/\?{2,}/g, '?'); // Limit consecutive question marks
315
+ expression.value = sanitized;
316
+ }
317
+ }
318
+ else {
319
+ this.sanitizeWildcards(expression.left);
320
+ if (expression.right) {
321
+ this.sanitizeWildcards(expression.right);
322
+ }
323
+ }
324
+ }
325
+ /**
326
+ * Collect all field names used in the query
327
+ *
328
+ * @private
329
+ * @param expression - The query expression to collect fields from
330
+ * @param fieldSet - Set to store the collected field names
331
+ */
332
+ collectFields(expression, fieldSet) {
333
+ if (expression.type === 'comparison') {
334
+ fieldSet.add(expression.field);
335
+ }
336
+ else {
337
+ this.collectFields(expression.left, fieldSet);
338
+ if (expression.right) {
339
+ this.collectFields(expression.right, fieldSet);
340
+ }
341
+ }
342
+ }
343
+ }
344
+ exports.QuerySecurityValidator = QuerySecurityValidator;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Drizzle ORM Translator for QueryKit
3
+ *
4
+ * This translator converts QueryKit AST expressions into Drizzle ORM
5
+ * query conditions that can be used in Drizzle's SQL query builder.
6
+ */
7
+ import { SQL } from 'drizzle-orm';
8
+ import { QueryExpression } from '../../parser/types';
9
+ import { ITranslator, ITranslatorOptions } from '../types';
10
+ /**
11
+ * Options specific to the Drizzle translator
12
+ */
13
+ export interface IDrizzleTranslatorOptions extends ITranslatorOptions {
14
+ /**
15
+ * Schema information for type safety
16
+ */
17
+ schema?: Record<string, Record<string, unknown>>;
18
+ }
19
+ /**
20
+ * Error thrown when translation fails
21
+ */
22
+ export declare class DrizzleTranslationError extends Error {
23
+ constructor(message: string);
24
+ }
25
+ /**
26
+ * Translates QueryKit AST expressions to Drizzle ORM conditions
27
+ */
28
+ export declare class DrizzleTranslator implements ITranslator<SQL> {
29
+ private options;
30
+ constructor(options?: IDrizzleTranslatorOptions);
31
+ /**
32
+ * Translate a QueryKit expression to a Drizzle ORM condition
33
+ */
34
+ translate(expression: QueryExpression): SQL;
35
+ /**
36
+ * Check if an expression can be translated to Drizzle ORM
37
+ */
38
+ canTranslate(expression: QueryExpression): boolean;
39
+ /**
40
+ * Translate any QueryKit expression to a Drizzle ORM condition
41
+ */
42
+ private translateExpression;
43
+ /**
44
+ * Translate a comparison expression to a Drizzle ORM condition
45
+ */
46
+ private translateComparisonExpression;
47
+ /**
48
+ * Build SQL for a specific operator with raw field name
49
+ * Security: Validates field names to prevent SQL injection
50
+ */
51
+ private buildSqlForOperator;
52
+ /**
53
+ * Convert wildcard pattern to SQL LIKE pattern
54
+ */
55
+ private wildcardToSqlPattern;
56
+ /**
57
+ * Translate a logical expression to a Drizzle ORM condition
58
+ */
59
+ private translateLogicalExpression;
60
+ /**
61
+ * Normalize a field name based on translator options
62
+ */
63
+ private normalizeField;
64
+ /**
65
+ * Get a field from the schema if it exists
66
+ */
67
+ private getSchemaField;
68
+ /**
69
+ * Security: Validates field names to prevent SQL injection
70
+ * Only allows alphanumeric chars, dots, underscores. Max 64 chars per part.
71
+ */
72
+ private isValidFieldName;
73
+ }