@freshguard/freshguard-core 0.11.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 (180) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +644 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +350 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/connectors/base-connector.d.ts +62 -0
  8. package/dist/connectors/base-connector.d.ts.map +1 -0
  9. package/dist/connectors/base-connector.js +549 -0
  10. package/dist/connectors/base-connector.js.map +1 -0
  11. package/dist/connectors/bigquery.d.ts +38 -0
  12. package/dist/connectors/bigquery.d.ts.map +1 -0
  13. package/dist/connectors/bigquery.js +406 -0
  14. package/dist/connectors/bigquery.js.map +1 -0
  15. package/dist/connectors/duckdb.d.ts +36 -0
  16. package/dist/connectors/duckdb.d.ts.map +1 -0
  17. package/dist/connectors/duckdb.js +364 -0
  18. package/dist/connectors/duckdb.js.map +1 -0
  19. package/dist/connectors/index.d.ts +7 -0
  20. package/dist/connectors/index.d.ts.map +1 -0
  21. package/dist/connectors/index.js +7 -0
  22. package/dist/connectors/index.js.map +1 -0
  23. package/dist/connectors/mysql.d.ts +32 -0
  24. package/dist/connectors/mysql.d.ts.map +1 -0
  25. package/dist/connectors/mysql.js +348 -0
  26. package/dist/connectors/mysql.js.map +1 -0
  27. package/dist/connectors/postgres.d.ts +31 -0
  28. package/dist/connectors/postgres.d.ts.map +1 -0
  29. package/dist/connectors/postgres.js +326 -0
  30. package/dist/connectors/postgres.js.map +1 -0
  31. package/dist/connectors/redshift.d.ts +32 -0
  32. package/dist/connectors/redshift.d.ts.map +1 -0
  33. package/dist/connectors/redshift.js +366 -0
  34. package/dist/connectors/redshift.js.map +1 -0
  35. package/dist/connectors/snowflake.d.ts +43 -0
  36. package/dist/connectors/snowflake.d.ts.map +1 -0
  37. package/dist/connectors/snowflake.js +442 -0
  38. package/dist/connectors/snowflake.js.map +1 -0
  39. package/dist/db/index.d.ts +9 -0
  40. package/dist/db/index.d.ts.map +1 -0
  41. package/dist/db/index.js +10 -0
  42. package/dist/db/index.js.map +1 -0
  43. package/dist/db/migrate.d.ts +12 -0
  44. package/dist/db/migrate.d.ts.map +1 -0
  45. package/dist/db/migrate.js +114 -0
  46. package/dist/db/migrate.js.map +1 -0
  47. package/dist/db/schema.d.ts +2053 -0
  48. package/dist/db/schema.d.ts.map +1 -0
  49. package/dist/db/schema.js +164 -0
  50. package/dist/db/schema.js.map +1 -0
  51. package/dist/errors/debug-factory.d.ts +23 -0
  52. package/dist/errors/debug-factory.d.ts.map +1 -0
  53. package/dist/errors/debug-factory.js +149 -0
  54. package/dist/errors/debug-factory.js.map +1 -0
  55. package/dist/errors/index.d.ts +119 -0
  56. package/dist/errors/index.d.ts.map +1 -0
  57. package/dist/errors/index.js +341 -0
  58. package/dist/errors/index.js.map +1 -0
  59. package/dist/index.d.ts +9 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +6 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/metadata/duckdb-storage.d.ts +31 -0
  64. package/dist/metadata/duckdb-storage.d.ts.map +1 -0
  65. package/dist/metadata/duckdb-storage.js +230 -0
  66. package/dist/metadata/duckdb-storage.js.map +1 -0
  67. package/dist/metadata/factory.d.ts +4 -0
  68. package/dist/metadata/factory.d.ts.map +1 -0
  69. package/dist/metadata/factory.js +23 -0
  70. package/dist/metadata/factory.js.map +1 -0
  71. package/dist/metadata/index.d.ts +6 -0
  72. package/dist/metadata/index.d.ts.map +1 -0
  73. package/dist/metadata/index.js +4 -0
  74. package/dist/metadata/index.js.map +1 -0
  75. package/dist/metadata/interface.d.ts +26 -0
  76. package/dist/metadata/interface.d.ts.map +1 -0
  77. package/dist/metadata/interface.js +2 -0
  78. package/dist/metadata/interface.js.map +1 -0
  79. package/dist/metadata/postgresql-storage.d.ts +32 -0
  80. package/dist/metadata/postgresql-storage.d.ts.map +1 -0
  81. package/dist/metadata/postgresql-storage.js +242 -0
  82. package/dist/metadata/postgresql-storage.js.map +1 -0
  83. package/dist/metadata/schema-config.d.ts +30 -0
  84. package/dist/metadata/schema-config.d.ts.map +1 -0
  85. package/dist/metadata/schema-config.js +94 -0
  86. package/dist/metadata/schema-config.js.map +1 -0
  87. package/dist/metadata/types.d.ts +35 -0
  88. package/dist/metadata/types.d.ts.map +1 -0
  89. package/dist/metadata/types.js +2 -0
  90. package/dist/metadata/types.js.map +1 -0
  91. package/dist/monitor/baseline-calculator.d.ts +30 -0
  92. package/dist/monitor/baseline-calculator.d.ts.map +1 -0
  93. package/dist/monitor/baseline-calculator.js +192 -0
  94. package/dist/monitor/baseline-calculator.js.map +1 -0
  95. package/dist/monitor/baseline-config.d.ts +37 -0
  96. package/dist/monitor/baseline-config.d.ts.map +1 -0
  97. package/dist/monitor/baseline-config.js +156 -0
  98. package/dist/monitor/baseline-config.js.map +1 -0
  99. package/dist/monitor/freshness.d.ts +5 -0
  100. package/dist/monitor/freshness.d.ts.map +1 -0
  101. package/dist/monitor/freshness.js +239 -0
  102. package/dist/monitor/freshness.js.map +1 -0
  103. package/dist/monitor/index.d.ts +5 -0
  104. package/dist/monitor/index.d.ts.map +1 -0
  105. package/dist/monitor/index.js +5 -0
  106. package/dist/monitor/index.js.map +1 -0
  107. package/dist/monitor/schema-baseline.d.ts +22 -0
  108. package/dist/monitor/schema-baseline.d.ts.map +1 -0
  109. package/dist/monitor/schema-baseline.js +211 -0
  110. package/dist/monitor/schema-baseline.js.map +1 -0
  111. package/dist/monitor/schema-changes.d.ts +5 -0
  112. package/dist/monitor/schema-changes.d.ts.map +1 -0
  113. package/dist/monitor/schema-changes.js +289 -0
  114. package/dist/monitor/schema-changes.js.map +1 -0
  115. package/dist/monitor/volume.d.ts +5 -0
  116. package/dist/monitor/volume.d.ts.map +1 -0
  117. package/dist/monitor/volume.js +262 -0
  118. package/dist/monitor/volume.js.map +1 -0
  119. package/dist/observability/logger.d.ts +63 -0
  120. package/dist/observability/logger.d.ts.map +1 -0
  121. package/dist/observability/logger.js +282 -0
  122. package/dist/observability/logger.js.map +1 -0
  123. package/dist/observability/metrics.d.ts +106 -0
  124. package/dist/observability/metrics.d.ts.map +1 -0
  125. package/dist/observability/metrics.js +441 -0
  126. package/dist/observability/metrics.js.map +1 -0
  127. package/dist/query-analyzer.js +526 -0
  128. package/dist/resilience/circuit-breaker.d.ts +94 -0
  129. package/dist/resilience/circuit-breaker.d.ts.map +1 -0
  130. package/dist/resilience/circuit-breaker.js +379 -0
  131. package/dist/resilience/circuit-breaker.js.map +1 -0
  132. package/dist/resilience/index.d.ts +7 -0
  133. package/dist/resilience/index.d.ts.map +1 -0
  134. package/dist/resilience/index.js +7 -0
  135. package/dist/resilience/index.js.map +1 -0
  136. package/dist/resilience/retry-policy.d.ts +87 -0
  137. package/dist/resilience/retry-policy.d.ts.map +1 -0
  138. package/dist/resilience/retry-policy.js +423 -0
  139. package/dist/resilience/retry-policy.js.map +1 -0
  140. package/dist/resilience/timeout-manager.d.ts +97 -0
  141. package/dist/resilience/timeout-manager.d.ts.map +1 -0
  142. package/dist/resilience/timeout-manager.js +339 -0
  143. package/dist/resilience/timeout-manager.js.map +1 -0
  144. package/dist/security/query-analyzer.d.ts +82 -0
  145. package/dist/security/query-analyzer.d.ts.map +1 -0
  146. package/dist/security/query-analyzer.js +381 -0
  147. package/dist/security/query-analyzer.js.map +1 -0
  148. package/dist/security/schema-cache.d.ts +95 -0
  149. package/dist/security/schema-cache.d.ts.map +1 -0
  150. package/dist/security/schema-cache.js +344 -0
  151. package/dist/security/schema-cache.js.map +1 -0
  152. package/dist/types/connector.d.ts +68 -0
  153. package/dist/types/connector.d.ts.map +1 -0
  154. package/dist/types/connector.js +26 -0
  155. package/dist/types/connector.js.map +1 -0
  156. package/dist/types.d.ts +244 -0
  157. package/dist/types.d.ts.map +1 -0
  158. package/dist/types.js +2 -0
  159. package/dist/types.js.map +1 -0
  160. package/dist/validation/index.d.ts +7 -0
  161. package/dist/validation/index.d.ts.map +1 -0
  162. package/dist/validation/index.js +5 -0
  163. package/dist/validation/index.js.map +1 -0
  164. package/dist/validation/runtime-validator.d.ts +70 -0
  165. package/dist/validation/runtime-validator.d.ts.map +1 -0
  166. package/dist/validation/runtime-validator.js +206 -0
  167. package/dist/validation/runtime-validator.js.map +1 -0
  168. package/dist/validation/sanitizers.d.ts +56 -0
  169. package/dist/validation/sanitizers.d.ts.map +1 -0
  170. package/dist/validation/sanitizers.js +264 -0
  171. package/dist/validation/sanitizers.js.map +1 -0
  172. package/dist/validation/schemas.d.ts +224 -0
  173. package/dist/validation/schemas.d.ts.map +1 -0
  174. package/dist/validation/schemas.js +263 -0
  175. package/dist/validation/schemas.js.map +1 -0
  176. package/dist/validators/index.d.ts +18 -0
  177. package/dist/validators/index.d.ts.map +1 -0
  178. package/dist/validators/index.js +209 -0
  179. package/dist/validators/index.js.map +1 -0
  180. package/package.json +91 -0
@@ -0,0 +1,526 @@
1
+ "use strict";
2
+ /**
3
+ * Query Complexity Analyzer for FreshGuard Core Phase 2
4
+ *
5
+ * Analyzes SQL queries for complexity and security risks, providing
6
+ * risk scoring and recommendations for safe query execution.
7
+ *
8
+ * @license MIT
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.defaultQueryAnalyzer = exports.QueryComplexityAnalyzer = void 0;
12
+ exports.createQueryAnalyzer = createQueryAnalyzer;
13
+ exports.createSecurityAnalyzer = createSecurityAnalyzer;
14
+ exports.createPerformanceAnalyzer = createPerformanceAnalyzer;
15
+ // ==============================================
16
+ // Default Configuration
17
+ // ==============================================
18
+ /**
19
+ * Default query analyzer configuration
20
+ */
21
+ const DEFAULT_CONFIG = {
22
+ maxRiskScore: 70,
23
+ maxComplexityScore: 80,
24
+ maxEstimatedCost: 1000000, // 1 million cost units
25
+ maxResultSetSize: 10000,
26
+ enablePerformanceAnalysis: true,
27
+ enableSecurityAnalysis: true,
28
+ customRiskFactors: []
29
+ };
30
+ /**
31
+ * Default risk factors for security analysis
32
+ */
33
+ const DEFAULT_RISK_FACTORS = [
34
+ {
35
+ pattern: /union\s+all|union\s+select/i,
36
+ riskScore: 30,
37
+ description: 'UNION operations can be expensive and may indicate injection attempts',
38
+ blocking: false
39
+ },
40
+ {
41
+ pattern: /\bor\s+1\s*=\s*1\b/i,
42
+ riskScore: 90,
43
+ description: 'Classic SQL injection pattern detected',
44
+ blocking: true
45
+ },
46
+ {
47
+ pattern: /\bor\s+\'.*?\'\s*=\s*\'.*?\'/i,
48
+ riskScore: 85,
49
+ description: 'Potential SQL injection with string comparison',
50
+ blocking: true
51
+ },
52
+ {
53
+ pattern: /;\s*drop\s+table/i,
54
+ riskScore: 100,
55
+ description: 'SQL injection attempt to drop table',
56
+ blocking: true
57
+ },
58
+ {
59
+ pattern: /;\s*delete\s+from/i,
60
+ riskScore: 100,
61
+ description: 'SQL injection attempt to delete data',
62
+ blocking: true
63
+ },
64
+ {
65
+ pattern: /select\s+\*\s+from\s+information_schema/i,
66
+ riskScore: 40,
67
+ description: 'Information schema access - potentially sensitive',
68
+ blocking: false
69
+ },
70
+ {
71
+ pattern: /select\s+.*\s+from\s+.*\s+where\s+1\s*=\s*1/i,
72
+ riskScore: 70,
73
+ description: 'Suspicious WHERE clause that always evaluates to true',
74
+ blocking: false
75
+ },
76
+ {
77
+ pattern: /\/\*.*?\*\//,
78
+ riskScore: 20,
79
+ description: 'SQL comments detected - review for injection attempts',
80
+ blocking: false
81
+ },
82
+ {
83
+ pattern: /--.*$/m,
84
+ riskScore: 25,
85
+ description: 'SQL line comments detected - review for injection attempts',
86
+ blocking: false
87
+ }
88
+ ];
89
+ // ==============================================
90
+ // SQL Parser Utilities
91
+ // ==============================================
92
+ /**
93
+ * Simple SQL parser for query analysis
94
+ * Note: This is a simplified parser for basic analysis
95
+ */
96
+ class SimpleSQLParser {
97
+ /**
98
+ * Parse basic query structure
99
+ */
100
+ static parseQuery(sql) {
101
+ const normalizedSql = sql.trim().toLowerCase();
102
+ // Determine query type
103
+ const queryType = this.getQueryType(normalizedSql);
104
+ // Count tables (simplified - counts FROM and JOIN clauses)
105
+ const tableCount = this.countTables(normalizedSql);
106
+ // Count joins
107
+ const joinCount = this.countJoins(normalizedSql);
108
+ // Check for various SQL constructs
109
+ const hasSubqueries = /\(\s*select\b/.test(normalizedSql);
110
+ const hasAggregations = /\b(count|sum|avg|max|min|group_concat)\s*\(/.test(normalizedSql);
111
+ const hasWildcards = /select\s+\*\s+from\b/.test(normalizedSql);
112
+ const hasWhere = /\bwhere\b/.test(normalizedSql);
113
+ const hasOrderBy = /\border\s+by\b/.test(normalizedSql);
114
+ const hasGroupBy = /\bgroup\s+by\b/.test(normalizedSql);
115
+ const hasHaving = /\bhaving\b/.test(normalizedSql);
116
+ // Check for LIMIT
117
+ const limitMatch = normalizedSql.match(/\blimit\s+(\d+)/);
118
+ const hasLimit = !!limitMatch;
119
+ const limitValue = limitMatch ? parseInt(limitMatch[1], 10) : undefined;
120
+ // Estimate result set size (simplified)
121
+ const estimatedResultSize = this.estimateResultSize(tableCount, hasWhere, hasLimit, limitValue);
122
+ return {
123
+ queryType,
124
+ tableCount,
125
+ joinCount,
126
+ hasSubqueries,
127
+ hasAggregations,
128
+ hasWildcards,
129
+ hasLimit,
130
+ limitValue,
131
+ hasWhere,
132
+ hasOrderBy,
133
+ hasGroupBy,
134
+ hasHaving,
135
+ estimatedResultSize
136
+ };
137
+ }
138
+ /**
139
+ * Get query type from SQL
140
+ */
141
+ static getQueryType(sql) {
142
+ if (sql.startsWith('select'))
143
+ return 'SELECT';
144
+ if (sql.startsWith('insert'))
145
+ return 'INSERT';
146
+ if (sql.startsWith('update'))
147
+ return 'UPDATE';
148
+ if (sql.startsWith('delete'))
149
+ return 'DELETE';
150
+ if (sql.startsWith('create'))
151
+ return 'CREATE';
152
+ if (sql.startsWith('drop'))
153
+ return 'DROP';
154
+ if (sql.startsWith('alter'))
155
+ return 'ALTER';
156
+ if (sql.startsWith('show'))
157
+ return 'SHOW';
158
+ if (sql.startsWith('describe') || sql.startsWith('desc'))
159
+ return 'DESCRIBE';
160
+ return 'UNKNOWN';
161
+ }
162
+ /**
163
+ * Count number of tables in query
164
+ */
165
+ static countTables(sql) {
166
+ // Count FROM clauses
167
+ const fromMatches = sql.match(/\bfrom\s+[\w\.]+/g) || [];
168
+ // Count JOIN clauses
169
+ const joinMatches = sql.match(/\bjoin\s+[\w\.]+/g) || [];
170
+ return fromMatches.length + joinMatches.length;
171
+ }
172
+ /**
173
+ * Count number of joins
174
+ */
175
+ static countJoins(sql) {
176
+ const joinMatches = sql.match(/\b(inner\s+join|left\s+join|right\s+join|full\s+join|join)\b/g) || [];
177
+ return joinMatches.length;
178
+ }
179
+ /**
180
+ * Estimate result set size
181
+ */
182
+ static estimateResultSize(tableCount, hasWhere, hasLimit, limitValue) {
183
+ // If LIMIT is specified, use that as max
184
+ if (hasLimit && limitValue) {
185
+ return Math.min(limitValue, 10000);
186
+ }
187
+ // Base estimate on table count and filtering
188
+ let estimate = Math.pow(1000, tableCount); // Exponential growth with joins
189
+ // Reduce estimate if WHERE clause exists (assumes filtering)
190
+ if (hasWhere) {
191
+ estimate = Math.floor(estimate * 0.1); // Assume WHERE reduces by 90%
192
+ }
193
+ // Cap at reasonable maximum
194
+ return Math.min(estimate, 100000);
195
+ }
196
+ }
197
+ // ==============================================
198
+ // Query Complexity Analyzer
199
+ // ==============================================
200
+ /**
201
+ * Analyzes SQL queries for complexity and security risks
202
+ */
203
+ class QueryComplexityAnalyzer {
204
+ constructor(config = {}) {
205
+ this.config = { ...DEFAULT_CONFIG, ...config };
206
+ this.riskFactors = [
207
+ ...DEFAULT_RISK_FACTORS,
208
+ ...(config.customRiskFactors || [])
209
+ ];
210
+ }
211
+ /**
212
+ * Analyze a SQL query for complexity and security risks
213
+ */
214
+ analyzeQuery(sql, tableMetadata = []) {
215
+ // Parse query structure
216
+ const details = SimpleSQLParser.parseQuery(sql);
217
+ // Calculate scores
218
+ const complexityScore = this.calculateComplexityScore(details, tableMetadata);
219
+ const riskScore = this.calculateRiskScore(sql, details);
220
+ const estimatedCost = this.calculateEstimatedCost(details, tableMetadata);
221
+ // Generate warnings and recommendations
222
+ const securityWarnings = this.generateSecurityWarnings(sql, details);
223
+ const performanceWarnings = this.generatePerformanceWarnings(details, tableMetadata);
224
+ const recommendations = this.generateRecommendations(details, securityWarnings, performanceWarnings);
225
+ // Determine if execution should be allowed
226
+ const allowExecution = this.shouldAllowExecution(sql, riskScore, complexityScore, estimatedCost, details);
227
+ return {
228
+ allowExecution,
229
+ riskScore,
230
+ complexityScore,
231
+ estimatedCost,
232
+ securityWarnings,
233
+ performanceWarnings,
234
+ recommendations,
235
+ details
236
+ };
237
+ }
238
+ /**
239
+ * Calculate query complexity score (0-100)
240
+ */
241
+ calculateComplexityScore(details, tableMetadata) {
242
+ let score = 0;
243
+ // Base score by query type
244
+ switch (details.queryType) {
245
+ case 'SELECT':
246
+ score += 5;
247
+ break;
248
+ case 'INSERT':
249
+ score += 15;
250
+ break;
251
+ case 'UPDATE':
252
+ score += 20;
253
+ break;
254
+ case 'DELETE':
255
+ score += 25;
256
+ break;
257
+ case 'CREATE':
258
+ score += 30;
259
+ break;
260
+ case 'DROP':
261
+ score += 50;
262
+ break;
263
+ case 'ALTER':
264
+ score += 40;
265
+ break;
266
+ default: score += 10;
267
+ }
268
+ // Table complexity
269
+ score += Math.min(details.tableCount * 10, 30); // Max 30 for tables
270
+ // Join complexity
271
+ score += Math.min(details.joinCount * 15, 40); // Max 40 for joins
272
+ // Subquery complexity
273
+ if (details.hasSubqueries)
274
+ score += 20;
275
+ // Aggregation complexity
276
+ if (details.hasAggregations)
277
+ score += 10;
278
+ // Wildcard penalty (SELECT *)
279
+ if (details.hasWildcards)
280
+ score += 15;
281
+ // Missing WHERE clause on multi-table queries
282
+ if (details.tableCount > 1 && !details.hasWhere)
283
+ score += 25;
284
+ // Large result set penalty
285
+ if (details.estimatedResultSize > 1000)
286
+ score += 10;
287
+ if (details.estimatedResultSize > 10000)
288
+ score += 20;
289
+ // No LIMIT on potentially large results
290
+ if (!details.hasLimit && details.estimatedResultSize > 1000)
291
+ score += 15;
292
+ return Math.min(score, 100);
293
+ }
294
+ /**
295
+ * Calculate security risk score (0-100)
296
+ */
297
+ calculateRiskScore(sql, details) {
298
+ let score = 0;
299
+ if (!this.config.enableSecurityAnalysis) {
300
+ return 0;
301
+ }
302
+ // Check against risk factors
303
+ for (const factor of this.riskFactors) {
304
+ if (factor.pattern.test(sql)) {
305
+ score += factor.riskScore;
306
+ }
307
+ }
308
+ // Additional risk factors based on query structure
309
+ if (details.queryType !== 'SELECT' && details.queryType !== 'SHOW' && details.queryType !== 'DESCRIBE') {
310
+ score += 30; // Non-read operations are inherently riskier
311
+ }
312
+ // Multiple statements (potential injection)
313
+ const statementCount = sql.split(';').filter(s => s.trim()).length;
314
+ if (statementCount > 1) {
315
+ score += 40;
316
+ }
317
+ return Math.min(score, 100);
318
+ }
319
+ /**
320
+ * Calculate estimated execution cost
321
+ */
322
+ calculateEstimatedCost(details, tableMetadata) {
323
+ let cost = 1; // Base cost
324
+ // Cost based on estimated result size
325
+ cost += details.estimatedResultSize * 0.1;
326
+ // Join costs (exponential)
327
+ if (details.joinCount > 0) {
328
+ cost *= Math.pow(10, details.joinCount);
329
+ }
330
+ // Table scan costs
331
+ for (let i = 0; i < details.tableCount; i++) {
332
+ const metadata = tableMetadata[i];
333
+ if (metadata) {
334
+ cost += metadata.estimatedRows * 0.01;
335
+ }
336
+ else {
337
+ cost += 10000; // Unknown table - assume large
338
+ }
339
+ }
340
+ // Subquery costs
341
+ if (details.hasSubqueries) {
342
+ cost *= 5;
343
+ }
344
+ // Aggregation costs
345
+ if (details.hasAggregations) {
346
+ cost *= 2;
347
+ }
348
+ // Sorting costs
349
+ if (details.hasOrderBy && !details.hasLimit) {
350
+ cost *= 3;
351
+ }
352
+ return Math.floor(cost);
353
+ }
354
+ /**
355
+ * Generate security warnings
356
+ */
357
+ generateSecurityWarnings(sql, details) {
358
+ const warnings = [];
359
+ if (!this.config.enableSecurityAnalysis) {
360
+ return warnings;
361
+ }
362
+ // Check risk factors
363
+ for (const factor of this.riskFactors) {
364
+ if (factor.pattern.test(sql)) {
365
+ warnings.push(factor.description);
366
+ }
367
+ }
368
+ // Additional security checks
369
+ if (details.queryType !== 'SELECT' && details.queryType !== 'SHOW' && details.queryType !== 'DESCRIBE') {
370
+ warnings.push('Non-read operation detected - ensure proper authorization');
371
+ }
372
+ if (sql.includes(';') && sql.split(';').filter(s => s.trim()).length > 1) {
373
+ warnings.push('Multiple SQL statements detected - potential injection risk');
374
+ }
375
+ if (details.hasWildcards && details.tableCount > 0) {
376
+ warnings.push('SELECT * detected - may expose sensitive columns');
377
+ }
378
+ return warnings;
379
+ }
380
+ /**
381
+ * Generate performance warnings
382
+ */
383
+ generatePerformanceWarnings(details, tableMetadata) {
384
+ const warnings = [];
385
+ if (!this.config.enablePerformanceAnalysis) {
386
+ return warnings;
387
+ }
388
+ // Large result set without LIMIT
389
+ if (details.estimatedResultSize > 1000 && !details.hasLimit) {
390
+ warnings.push(`Large result set estimated (${details.estimatedResultSize}) without LIMIT clause`);
391
+ }
392
+ // Multiple joins without WHERE
393
+ if (details.joinCount > 1 && !details.hasWhere) {
394
+ warnings.push('Multiple JOINs without WHERE clause may produce Cartesian product');
395
+ }
396
+ // SELECT * on large tables
397
+ if (details.hasWildcards && tableMetadata.some(t => t.estimatedRows > 10000)) {
398
+ warnings.push('SELECT * on large table(s) - consider selecting specific columns');
399
+ }
400
+ // ORDER BY without LIMIT on large result
401
+ if (details.hasOrderBy && !details.hasLimit && details.estimatedResultSize > 1000) {
402
+ warnings.push('ORDER BY without LIMIT on large result set - consider adding LIMIT');
403
+ }
404
+ // Subqueries
405
+ if (details.hasSubqueries) {
406
+ warnings.push('Subqueries detected - consider using JOINs for better performance');
407
+ }
408
+ return warnings;
409
+ }
410
+ /**
411
+ * Generate optimization recommendations
412
+ */
413
+ generateRecommendations(details, securityWarnings, performanceWarnings) {
414
+ const recommendations = [];
415
+ // Security recommendations
416
+ if (securityWarnings.length > 0) {
417
+ recommendations.push('Review security warnings and validate query source');
418
+ }
419
+ if (details.hasWildcards) {
420
+ recommendations.push('Replace SELECT * with specific column names');
421
+ }
422
+ // Performance recommendations
423
+ if (!details.hasLimit && details.estimatedResultSize > 1000) {
424
+ recommendations.push('Add LIMIT clause to prevent large result sets');
425
+ }
426
+ if (details.joinCount > 0 && !details.hasWhere) {
427
+ recommendations.push('Add WHERE clause to filter results and improve performance');
428
+ }
429
+ if (details.hasOrderBy && details.estimatedResultSize > 1000) {
430
+ recommendations.push('Consider adding indexes on ORDER BY columns');
431
+ }
432
+ if (details.hasSubqueries) {
433
+ recommendations.push('Consider rewriting subqueries as JOINs');
434
+ }
435
+ if (performanceWarnings.length > 2) {
436
+ recommendations.push('Query complexity is high - consider breaking into smaller queries');
437
+ }
438
+ return recommendations;
439
+ }
440
+ /**
441
+ * Determine if query execution should be allowed
442
+ */
443
+ shouldAllowExecution(sql, riskScore, complexityScore, estimatedCost, details) {
444
+ // Check against thresholds
445
+ if (riskScore > this.config.maxRiskScore) {
446
+ return false;
447
+ }
448
+ if (complexityScore > this.config.maxComplexityScore) {
449
+ return false;
450
+ }
451
+ if (estimatedCost > this.config.maxEstimatedCost) {
452
+ return false;
453
+ }
454
+ if (details.estimatedResultSize > this.config.maxResultSetSize) {
455
+ return false;
456
+ }
457
+ // Check for blocking risk factors
458
+ for (const factor of this.riskFactors) {
459
+ if (factor.blocking && factor.pattern.test(sql)) {
460
+ return false;
461
+ }
462
+ }
463
+ return true;
464
+ }
465
+ /**
466
+ * Update configuration
467
+ */
468
+ updateConfig(config) {
469
+ this.config = { ...this.config, ...config };
470
+ }
471
+ /**
472
+ * Add custom risk factor
473
+ */
474
+ addRiskFactor(factor) {
475
+ this.riskFactors.push(factor);
476
+ }
477
+ /**
478
+ * Get current configuration
479
+ */
480
+ getConfig() {
481
+ return { ...this.config };
482
+ }
483
+ }
484
+ exports.QueryComplexityAnalyzer = QueryComplexityAnalyzer;
485
+ // ==============================================
486
+ // Factory Functions
487
+ // ==============================================
488
+ /**
489
+ * Create a query analyzer with default configuration
490
+ */
491
+ function createQueryAnalyzer(config) {
492
+ return new QueryComplexityAnalyzer(config);
493
+ }
494
+ /**
495
+ * Create a strict security analyzer
496
+ */
497
+ function createSecurityAnalyzer() {
498
+ return new QueryComplexityAnalyzer({
499
+ maxRiskScore: 30,
500
+ maxComplexityScore: 50,
501
+ maxEstimatedCost: 100000,
502
+ maxResultSetSize: 1000,
503
+ enableSecurityAnalysis: true,
504
+ enablePerformanceAnalysis: false
505
+ });
506
+ }
507
+ /**
508
+ * Create a performance-focused analyzer
509
+ */
510
+ function createPerformanceAnalyzer() {
511
+ return new QueryComplexityAnalyzer({
512
+ maxRiskScore: 100, // Allow all from security perspective
513
+ maxComplexityScore: 60,
514
+ maxEstimatedCost: 500000,
515
+ maxResultSetSize: 5000,
516
+ enableSecurityAnalysis: false,
517
+ enablePerformanceAnalysis: true
518
+ });
519
+ }
520
+ // ==============================================
521
+ // Default Analyzer Instance
522
+ // ==============================================
523
+ /**
524
+ * Default query analyzer instance
525
+ */
526
+ exports.defaultQueryAnalyzer = createQueryAnalyzer();
@@ -0,0 +1,94 @@
1
+ import type { StructuredLogger } from '../observability/logger.js';
2
+ import type { MetricsCollector } from '../observability/metrics.js';
3
+ export declare enum CircuitBreakerState {
4
+ CLOSED = "CLOSED",
5
+ OPEN = "OPEN",
6
+ HALF_OPEN = "HALF_OPEN"
7
+ }
8
+ export interface CircuitBreakerConfig {
9
+ failureThreshold: number;
10
+ successThreshold: number;
11
+ recoveryTimeout: number;
12
+ windowSize: number;
13
+ errorFilter?: (error: Error) => boolean;
14
+ name?: string;
15
+ logger?: StructuredLogger;
16
+ metrics?: MetricsCollector;
17
+ enableDetailedLogging?: boolean;
18
+ }
19
+ export interface CircuitBreakerStats {
20
+ state: CircuitBreakerState;
21
+ totalCalls: number;
22
+ successfulCalls: number;
23
+ failedCalls: number;
24
+ rejectedCalls: number;
25
+ lastFailureTime: Date | null;
26
+ lastSuccessTime: Date | null;
27
+ nextAttemptTime: Date | null;
28
+ failureRate: number;
29
+ uptime: number;
30
+ }
31
+ export type CircuitBreakerResult<T> = {
32
+ success: true;
33
+ data: T;
34
+ executionTime: number;
35
+ } | {
36
+ success: false;
37
+ error: CircuitBreakerError;
38
+ executionTime: number;
39
+ };
40
+ export declare class CircuitBreakerError extends Error {
41
+ readonly circuitName: string;
42
+ readonly state: CircuitBreakerState;
43
+ readonly timestamp: Date;
44
+ constructor(message: string, circuitName: string, state: CircuitBreakerState);
45
+ }
46
+ export declare class CircuitOpenError extends CircuitBreakerError {
47
+ readonly nextAttemptTime: Date;
48
+ constructor(circuitName: string, nextAttemptTime: Date);
49
+ }
50
+ export declare class CircuitBreaker {
51
+ private state;
52
+ private failureWindow;
53
+ private successCount;
54
+ private totalCalls;
55
+ private successfulCalls;
56
+ private failedCalls;
57
+ private rejectedCalls;
58
+ private lastFailureTime;
59
+ private lastSuccessTime;
60
+ private nextAttemptTime;
61
+ private readonly config;
62
+ private readonly logger;
63
+ private readonly metrics;
64
+ private readonly enableDetailedLogging;
65
+ private lastStateChangeTime;
66
+ constructor(config: CircuitBreakerConfig);
67
+ execute<T>(fn: () => Promise<T>): Promise<T>;
68
+ executeWithResult<T>(fn: () => Promise<T>): Promise<CircuitBreakerResult<T>>;
69
+ private onSuccess;
70
+ private onFailure;
71
+ private tripCircuit;
72
+ private changeState;
73
+ private recordMetrics;
74
+ private resetFailureCount;
75
+ getStats(): CircuitBreakerStats;
76
+ isCallable(): boolean;
77
+ reset(): void;
78
+ trip(): void;
79
+ getName(): string;
80
+ getState(): CircuitBreakerState;
81
+ }
82
+ export declare function createDatabaseCircuitBreaker(name: string): CircuitBreaker;
83
+ export declare function createApiCircuitBreaker(name: string): CircuitBreaker;
84
+ export declare class CircuitBreakerRegistry {
85
+ private readonly circuits;
86
+ getOrCreate(name: string, config?: CircuitBreakerConfig): CircuitBreaker;
87
+ getAllCircuits(): Map<string, CircuitBreaker>;
88
+ getAllStats(): Record<string, CircuitBreakerStats>;
89
+ resetAll(): void;
90
+ remove(name: string): boolean;
91
+ clear(): void;
92
+ }
93
+ export declare const defaultCircuitBreakerRegistry: CircuitBreakerRegistry;
94
+ //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/resilience/circuit-breaker.ts"],"names":[],"mappings":"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"}