@memberjunction/global 6.2.0-edge.4 → 6.2.0-edge.6

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 (75) hide show
  1. package/dist/ClassFactory.d.ts +16 -3
  2. package/dist/ClassFactory.d.ts.map +1 -1
  3. package/dist/ClassFactory.js +112 -48
  4. package/dist/ClassFactory.js.map +1 -1
  5. package/dist/DeepDiff.d.ts +2 -0
  6. package/dist/DeepDiff.d.ts.map +1 -1
  7. package/dist/DeepDiff.js +40 -38
  8. package/dist/DeepDiff.js.map +1 -1
  9. package/dist/KeyedSerialTaskQueue.d.ts +18 -1
  10. package/dist/KeyedSerialTaskQueue.d.ts.map +1 -1
  11. package/dist/KeyedSerialTaskQueue.js +24 -3
  12. package/dist/KeyedSerialTaskQueue.js.map +1 -1
  13. package/dist/ObjectCache.d.ts +1 -0
  14. package/dist/ObjectCache.d.ts.map +1 -1
  15. package/dist/ObjectCache.js +14 -22
  16. package/dist/ObjectCache.js.map +1 -1
  17. package/dist/SQLExpressionValidator.d.ts +70 -41
  18. package/dist/SQLExpressionValidator.d.ts.map +1 -1
  19. package/dist/SQLExpressionValidator.js +183 -134
  20. package/dist/SQLExpressionValidator.js.map +1 -1
  21. package/dist/SQLScreeningScanner.d.ts +117 -0
  22. package/dist/SQLScreeningScanner.d.ts.map +1 -0
  23. package/dist/SQLScreeningScanner.js +475 -0
  24. package/dist/SQLScreeningScanner.js.map +1 -0
  25. package/dist/__tests__/FieldSecurityEgressCompliance.test.d.ts +2 -0
  26. package/dist/__tests__/FieldSecurityEgressCompliance.test.d.ts.map +1 -0
  27. package/dist/__tests__/FieldSecurityEgressCompliance.test.js +228 -0
  28. package/dist/__tests__/FieldSecurityEgressCompliance.test.js.map +1 -0
  29. package/dist/__tests__/KeyedSerialTaskQueue.test.js +92 -0
  30. package/dist/__tests__/KeyedSerialTaskQueue.test.js.map +1 -1
  31. package/dist/__tests__/ProofOfPossession.test.d.ts +2 -0
  32. package/dist/__tests__/ProofOfPossession.test.d.ts.map +1 -0
  33. package/dist/__tests__/ProofOfPossession.test.js +123 -0
  34. package/dist/__tests__/ProofOfPossession.test.js.map +1 -0
  35. package/dist/__tests__/SQLExpressionValidator.dialect.test.d.ts +2 -0
  36. package/dist/__tests__/SQLExpressionValidator.dialect.test.d.ts.map +1 -0
  37. package/dist/__tests__/SQLExpressionValidator.dialect.test.js +183 -0
  38. package/dist/__tests__/SQLExpressionValidator.dialect.test.js.map +1 -0
  39. package/dist/__tests__/SQLExpressionValidator.security.test.js +176 -2
  40. package/dist/__tests__/SQLExpressionValidator.security.test.js.map +1 -1
  41. package/dist/__tests__/SQLScreeningScanner.test.d.ts +2 -0
  42. package/dist/__tests__/SQLScreeningScanner.test.d.ts.map +1 -0
  43. package/dist/__tests__/SQLScreeningScanner.test.js +172 -0
  44. package/dist/__tests__/SQLScreeningScanner.test.js.map +1 -0
  45. package/dist/__tests__/SQLScreeningScanner.tokens.test.d.ts +2 -0
  46. package/dist/__tests__/SQLScreeningScanner.tokens.test.d.ts.map +1 -0
  47. package/dist/__tests__/SQLScreeningScanner.tokens.test.js +82 -0
  48. package/dist/__tests__/SQLScreeningScanner.tokens.test.js.map +1 -0
  49. package/dist/__tests__/UUIDCompliance.test.js +4 -4
  50. package/dist/__tests__/UUIDCompliance.test.js.map +1 -1
  51. package/dist/__tests__/UserScopedCacheKey.test.d.ts +2 -0
  52. package/dist/__tests__/UserScopedCacheKey.test.d.ts.map +1 -0
  53. package/dist/__tests__/UserScopedCacheKey.test.js +25 -0
  54. package/dist/__tests__/UserScopedCacheKey.test.js.map +1 -0
  55. package/dist/__tests__/util.valuesEqual.test.d.ts +2 -0
  56. package/dist/__tests__/util.valuesEqual.test.d.ts.map +1 -0
  57. package/dist/__tests__/util.valuesEqual.test.js +37 -0
  58. package/dist/__tests__/util.valuesEqual.test.js.map +1 -0
  59. package/dist/index.d.ts +3 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/util/ProofOfPossession.d.ts +68 -0
  64. package/dist/util/ProofOfPossession.d.ts.map +1 -0
  65. package/dist/util/ProofOfPossession.js +163 -0
  66. package/dist/util/ProofOfPossession.js.map +1 -0
  67. package/dist/util/UserScopedCacheKey.d.ts +42 -0
  68. package/dist/util/UserScopedCacheKey.d.ts.map +1 -0
  69. package/dist/util/UserScopedCacheKey.js +49 -0
  70. package/dist/util/UserScopedCacheKey.js.map +1 -0
  71. package/dist/util.d.ts +25 -0
  72. package/dist/util.d.ts.map +1 -1
  73. package/dist/util.js +38 -0
  74. package/dist/util.js.map +1 -1
  75. package/package.json +1 -1
@@ -10,6 +10,7 @@
10
10
  * @module @memberjunction/global/SQLExpressionValidator
11
11
  */
12
12
  import { BaseSingleton } from './BaseSingleton.js';
13
+ import { ScanSQLForScreening, SQL_SCREENING_DIALECTS, } from './SQLScreeningScanner.js';
13
14
  /**
14
15
  * Dangerous SQL keywords that are never allowed in user-provided expressions
15
16
  */
@@ -67,7 +68,8 @@ export const FULL_QUERY_ALLOWED_KEYWORDS = [
67
68
  * a schema-enumeration and credential-exfiltration primitive
68
69
  * (e.g. `SELECT name, password_hash FROM sys.sql_logins`,
69
70
  * `SELECT * FROM INFORMATION_SCHEMA.COLUMNS`, `SELECT * FROM pg_catalog.pg_authid`).
70
- * String literals are stripped before this check runs, so a literal value like `'sys.x'` is safe.
71
+ * The check reads {@link SQLScreeningScan.CodeWithIdentifiers}: string literals are removed, so a
72
+ * literal value like `'sys.x'` is safe, and quoted names are unwrapped, so `[sys].[x]` is caught.
71
73
  */
72
74
  export const BLOCKED_SYSTEM_OBJECT_PATTERNS = [
73
75
  /\bSYS\s*\.\s*\w/i, // SQL Server system catalog schema: sys.sql_logins, sys.objects, sys.fn_*, ...
@@ -77,65 +79,111 @@ export const BLOCKED_SYSTEM_OBJECT_PATTERNS = [
77
79
  /\bPG_(AUTHID|SHADOW|USER|ROLES)\b/i, // PostgreSQL credential / role catalogs
78
80
  ];
79
81
  /**
80
- * Matches SQL string literals the way SQL Server and PostgreSQL (with the default
81
- * `standard_conforming_strings=on`) actually parse them: a literal is closed by the next
82
- * single quote, `''` is an embedded quote, and a backslash is an ORDINARY character that
83
- * does NOT escape the closing quote. Double-quoted runs are matched with the same doubling
84
- * rule so a delimited identifier can't hide a payload either.
85
- *
86
- * @see StripSQLStringLiterals — use that, not this constant, so every caller gets the
87
- * documented semantics and the two validators in MJ can never drift apart again.
82
+ * The keywords a full query may open with. Without the `u` flag, the `i` flag matches ASCII letters
83
+ * only, so no other letter is case-mapped to S, E, L, C, T, W, I or H.
88
84
  */
89
- const SQL_STRING_LITERAL_PATTERN = /'(?:[^']|'')*'|"(?:[^"]|"")*"/g;
85
+ const FULL_QUERY_OPENING_KEYWORD = /^(?:SELECT|WITH)$/i;
90
86
  /**
91
- * Removes SQL string literals from a clause or expression so that a keyword denylist can be
92
- * applied to the *code* portion without tripping over keywords that appear inside quoted data
93
- * (e.g. `Comments LIKE '%--%'`).
94
- *
95
- * 🚨 SECURITY — this is the single implementation of literal-stripping for MJ's SQL screens, and
96
- * it MUST stay byte-for-byte consistent with how the database parses literals. If the stripper
97
- * removes a span the database does NOT treat as a literal, everything hidden inside that span
98
- * bypasses the denylist while the database still executes it.
99
- *
100
- * An earlier version of this logic (duplicated in two places, which is how it survived) honored
101
- * **backslash escaping** — `/(['"])(?:(?=(\\?))\2[\s\S])*?\1/g`. SQL Server and PostgreSQL do not
102
- * treat `\` as an escape character, so `x = 'a\') ; DROP TABLE Users; --'` was swallowed whole as
103
- * one "literal" and stripped to `x = `, which passed every denylist — while the database closed
104
- * the literal at the real quote and executed the stacked statement.
87
+ * Replaces each SQL string literal with one space so that a keyword denylist can be applied to the
88
+ * rest without tripping over keywords inside quoted data (e.g. `Comments LIKE '%--%'`). Quoted
89
+ * identifiers and comments are left as written.
105
90
  *
106
- * **Do NOT reintroduce backslash-escape handling here, and do NOT inline a second copy of this
107
- * regex anywhere else — call this function.**
91
+ * 🚨 SECURITY — literals are found by {@link ScanSQLForScreening}, which reads the text the way the
92
+ * database does: a quote inside a T-SQL `[…]` identifier, a comment or a PostgreSQL `$$…$$` string
93
+ * opens no literal, and a backslash escapes a quote only inside a PostgreSQL `E'…'` string. A
94
+ * stripper that removes a span the database does NOT treat as a literal hides everything inside it
95
+ * from the denylist while the database still runs it. Screens should call
96
+ * {@link ScanSQLForScreening} directly, which also reports comments and text it cannot classify.
108
97
  *
109
98
  * @param sql The clause, expression, or query to strip literals from
110
- * @returns The input with every complete string literal removed. An UNTERMINATED literal is left
111
- * in place on purpose: the stray quote and everything after it stay visible to the
112
- * denylist rather than being silently swallowed.
99
+ * @param dialect The dialect that will run the SQL. Without one, the text must read the same way
100
+ * under every dialect the scanner knows.
101
+ * @returns The input with each string literal replaced by a space. The input is returned unchanged
102
+ * when it cannot be classified (for example an unterminated literal) or when the dialects
103
+ * disagree on where its literals are, so a denylist still sees all of it.
113
104
  */
114
- export function StripSQLStringLiterals(sql) {
115
- return sql.replace(SQL_STRING_LITERAL_PATTERN, '');
105
+ export function StripSQLStringLiterals(sql, dialect) {
106
+ const scans = (dialect ? [dialect] : SQL_SCREENING_DIALECTS).map(d => ScanSQLForScreening(sql, d));
107
+ if (scans.some(scan => !scan.Classified))
108
+ return sql;
109
+ const readings = scans.map(scan => replaceStringSpans(sql, scan.Spans));
110
+ return readings.every(reading => reading === readings[0]) ? readings[0] : sql;
111
+ }
112
+ /** Writes `sql` with each string span replaced by one space. */
113
+ function replaceStringSpans(sql, spans) {
114
+ let result = '';
115
+ let from = 0;
116
+ for (const span of spans) {
117
+ if (span.Kind !== 'string')
118
+ continue;
119
+ result += sql.slice(from, span.Start) + ' ';
120
+ from = span.End;
121
+ }
122
+ return result + sql.slice(from);
116
123
  }
117
124
  /**
118
- * Safe SQL functions allowed in expressions, organized by category
125
+ * Safe SQL functions allowed in expressions, organized by category.
126
+ *
127
+ * WHY THIS IS A UNION RATHER THAN A PER-DIALECT TABLE
128
+ * ---------------------------------------------------
129
+ * This is a deny-by-default SAFETY list, not a dialect grammar. Its job is to
130
+ * stop a user-supplied expression from smuggling in something that is not a
131
+ * read-only scalar/aggregate call — it is not, and must not become, a
132
+ * per-platform parser. So it carries the union of every supported dialect's
133
+ * read-only vocabulary, and `checkFunctionNames` consults the flattened union.
134
+ *
135
+ * Keying the buckets off `ResolvePlatformKey` is the alternative, and it costs
136
+ * more than it buys: a PostgreSQL name that reaches a SQL Server tenant is
137
+ * rejected by the SQL Server parser on its own — `DATE_TRUNC(...)` simply never
138
+ * parses there. The union therefore cannot widen the blast radius past what the
139
+ * target server will already accept, whereas a dialect switch here would add a
140
+ * second independent place for the dialect to be resolved, and so a second place
141
+ * for it to be wrong.
142
+ *
143
+ * Before this list held the PostgreSQL names, a perfectly ordinary PG filter
144
+ * such as `DATE_TRUNC('month', CreatedAt)` was rejected outright with
145
+ * "Function 'DATE_TRUNC' is not allowed" on every PostgreSQL tenant.
146
+ *
147
+ * Every name here is read-only by construction. Anything that writes, escalates,
148
+ * or reaches outside the statement stays on {@link DANGEROUS_SQL_KEYWORDS}, which
149
+ * is checked FIRST (step 2 of `validate`) — a name added here can never unblock
150
+ * one that is denied there.
119
151
  */
120
152
  export const ALLOWED_SQL_FUNCTIONS = {
121
- // Aggregate functions
122
- aggregates: ['COUNT', 'COUNT_BIG', 'SUM', 'AVG', 'MIN', 'MAX', 'STDEV', 'STDEVP', 'VAR', 'VARP', 'STRING_AGG', 'CHECKSUM_AGG'],
123
- // Math functions
124
- math: ['ABS', 'CEILING', 'FLOOR', 'ROUND', 'POWER', 'SQRT', 'LOG', 'LOG10', 'EXP', 'SIGN', 'RAND'],
125
- // String functions (read-only)
126
- string: ['LEN', 'LENGTH', 'UPPER', 'LOWER', 'LTRIM', 'RTRIM', 'TRIM', 'LEFT', 'RIGHT', 'SUBSTRING', 'CHARINDEX', 'REPLACE', 'CONCAT', 'STUFF'],
127
- // Date functions
128
- date: ['DATEPART', 'DATEDIFF', 'DATEADD', 'YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'GETDATE', 'GETUTCDATE', 'SYSDATETIME', 'EOMONTH'],
129
- // Type conversion (safe subset)
130
- conversion: ['CAST', 'CONVERT', 'TRY_CAST', 'TRY_CONVERT', 'FORMAT'],
131
- // Null handling
132
- nullHandling: ['ISNULL', 'COALESCE', 'NULLIF', 'IIF'],
153
+ // Aggregate functions. PG: ARRAY_AGG / JSONB_AGG / JSON_AGG / BOOL_AND / BOOL_OR /
154
+ // EVERY / PERCENTILE_* / MODE.
155
+ aggregates: ['COUNT', 'COUNT_BIG', 'SUM', 'AVG', 'MIN', 'MAX', 'STDEV', 'STDEVP', 'VAR', 'VARP', 'STRING_AGG', 'CHECKSUM_AGG',
156
+ 'ARRAY_AGG', 'JSONB_AGG', 'JSON_AGG', 'BOOL_AND', 'BOOL_OR', 'EVERY', 'PERCENTILE_CONT', 'PERCENTILE_DISC', 'MODE'],
157
+ // Math functions. PG: CEIL (vs CEILING), RANDOM (vs RAND), TRUNC, MOD, DIV,
158
+ // GREATEST, LEAST, WIDTH_BUCKET.
159
+ math: ['ABS', 'CEILING', 'FLOOR', 'ROUND', 'POWER', 'SQRT', 'LOG', 'LOG10', 'EXP', 'SIGN', 'RAND',
160
+ 'CEIL', 'RANDOM', 'TRUNC', 'MOD', 'DIV', 'GREATEST', 'LEAST', 'WIDTH_BUCKET'],
161
+ // String functions (read-only). PG: POSITION / STRPOS (vs CHARINDEX), SPLIT_PART,
162
+ // INITCAP, LPAD, RPAD, REPEAT, REVERSE, TRANSLATE, REGEXP_*, TO_CHAR, MD5, ASCII, CHR.
163
+ string: ['LEN', 'LENGTH', 'UPPER', 'LOWER', 'LTRIM', 'RTRIM', 'TRIM', 'LEFT', 'RIGHT', 'SUBSTRING', 'CHARINDEX', 'REPLACE', 'CONCAT', 'STUFF',
164
+ 'POSITION', 'STRPOS', 'SPLIT_PART', 'INITCAP', 'LPAD', 'RPAD', 'REPEAT', 'REVERSE', 'TRANSLATE',
165
+ 'REGEXP_REPLACE', 'REGEXP_MATCHES', 'REGEXP_SUBSTR', 'TO_CHAR', 'MD5', 'ASCII', 'CHR'],
166
+ // Date functions. PG: NOW / CURRENT_* / LOCALTIMESTAMP (vs GETDATE), DATE_TRUNC,
167
+ // DATE_PART / EXTRACT (vs DATEPART), AGE, TO_DATE, TO_TIMESTAMP, MAKE_*, JUSTIFY_DAYS.
168
+ date: ['DATEPART', 'DATEDIFF', 'DATEADD', 'YEAR', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND', 'GETDATE', 'GETUTCDATE', 'SYSDATETIME', 'EOMONTH',
169
+ 'NOW', 'CURRENT_DATE', 'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'LOCALTIMESTAMP', 'DATE_TRUNC', 'DATE_PART', 'EXTRACT', 'AGE',
170
+ 'TO_DATE', 'TO_TIMESTAMP', 'MAKE_DATE', 'MAKE_TIMESTAMP', 'JUSTIFY_DAYS'],
171
+ // Type conversion (safe subset). PG: TO_CHAR / TO_DATE / TO_NUMBER / TO_TIMESTAMP
172
+ // (vs CAST/CONVERT/FORMAT).
173
+ conversion: ['CAST', 'CONVERT', 'TRY_CAST', 'TRY_CONVERT', 'FORMAT', 'TO_CHAR', 'TO_DATE', 'TO_NUMBER', 'TO_TIMESTAMP'],
174
+ // Null handling. PG has no ISNULL/IIF; GREATEST/LEAST are its null-tolerant pickers.
175
+ nullHandling: ['ISNULL', 'COALESCE', 'NULLIF', 'IIF', 'GREATEST', 'LEAST'],
133
176
  // Case expressions
134
177
  conditional: ['CASE', 'WHEN', 'THEN', 'ELSE', 'END'],
135
- // Logical operators (as keywords)
136
- logical: ['AND', 'OR', 'NOT', 'IS', 'NULL', 'LIKE', 'BETWEEN', 'IN'],
137
- // Sort/order and windowing
138
- ordering: ['ASC', 'ASCENDING', 'DESC', 'DESCENDING', 'OVER', 'PARTITION', 'BY', 'ORDER', 'ROWS', 'RANGE', 'UNBOUNDED', 'PRECEDING', 'FOLLOWING', 'CURRENT', 'ROW']
178
+ // Logical operators (as keywords). PG: ILIKE, SIMILAR TO. ANY/ALL/SOME/EXISTS are
179
+ // listed so that the full-query context, which unblocks them from
180
+ // DANGEROUS_SQL_KEYWORDS via FULL_QUERY_ALLOWED_KEYWORDS, does not then trip over
181
+ // them here as unknown function-shaped tokens.
182
+ logical: ['AND', 'OR', 'NOT', 'IS', 'NULL', 'LIKE', 'BETWEEN', 'IN', 'ILIKE', 'SIMILAR', 'ANY', 'ALL', 'SOME', 'EXISTS'],
183
+ // Sort/order and windowing. PG: NULLS FIRST/LAST, aggregate FILTER (WHERE …),
184
+ // WITHIN GROUP, GROUPING.
185
+ ordering: ['ASC', 'ASCENDING', 'DESC', 'DESCENDING', 'OVER', 'PARTITION', 'BY', 'ORDER', 'ROWS', 'RANGE', 'UNBOUNDED', 'PRECEDING', 'FOLLOWING', 'CURRENT', 'ROW',
186
+ 'NULLS', 'FIRST', 'LAST', 'FILTER', 'WITHIN', 'GROUPING']
139
187
  };
140
188
  /**
141
189
  * Central SQL expression validator for preventing SQL injection.
@@ -186,75 +234,65 @@ export class SQLExpressionValidator extends BaseSingleton {
186
234
  if (!trimmed) {
187
235
  return { valid: false, error: 'Expression cannot be empty' };
188
236
  }
189
- // Step 1: Remove string literals to avoid false positives
190
- const withoutStrings = this.removeStringLiterals(trimmed);
191
- // Step 2: Check for dangerous patterns
192
- const dangerCheck = this.checkDangerousPatterns(withoutStrings, options);
237
+ // Read the text the way the database will. When the dialect is unknown, every reading must pass.
238
+ const dialects = options.Dialect ? [options.Dialect] : SQL_SCREENING_DIALECTS;
239
+ for (const dialect of dialects) {
240
+ const result = this.validateReading(ScanSQLForScreening(trimmed, dialect), options);
241
+ if (!result.valid)
242
+ return result;
243
+ }
244
+ return { valid: true };
245
+ }
246
+ /**
247
+ * Runs every check against one dialect's reading of the expression.
248
+ *
249
+ * 🚨 SECURITY: the checks read the scanner's projections, never the raw text. Do not add a regex
250
+ * that finds literals, identifiers or comments here; see {@link ScanSQLForScreening}.
251
+ */
252
+ validateReading(scan, options) {
253
+ if (!scan.Classified) {
254
+ return {
255
+ valid: false,
256
+ error: `SQL could not be read safely as ${scan.Dialect}: ${scan.Error}`,
257
+ trigger: 'unclassified'
258
+ };
259
+ }
260
+ const dangerCheck = this.checkDangerousPatterns(scan, options);
193
261
  if (!dangerCheck.valid)
194
262
  return dangerCheck;
195
- // Step 3: Validate function names are in allowlist (skip for full queries —
196
- // the function allowlist is designed for expression fragments, not full SQL statements)
263
+ // The function allowlist is designed for expression fragments, not full SQL statements.
264
+ // Quoted names are unwrapped, so a quoted function call is checked too.
197
265
  if (options.context !== 'full_query') {
198
- const functionCheck = this.checkFunctionNames(withoutStrings, options);
266
+ const functionCheck = this.checkFunctionNames(scan.CodeWithIdentifiers, options);
199
267
  if (!functionCheck.valid)
200
268
  return functionCheck;
201
269
  }
202
- // Step 4: Context-specific validation
203
- const contextCheck = this.checkContextRules(withoutStrings, options);
270
+ const contextCheck = this.checkContextRules(scan, options);
204
271
  if (!contextCheck.valid)
205
272
  return contextCheck;
206
- // Step 5: Optional field reference validation (lenient - just logs warnings)
273
+ // Optional field reference validation (lenient - just logs warnings)
207
274
  if (options.entityFields?.length) {
208
- this.checkFieldReferences(withoutStrings, options.entityFields);
275
+ this.checkFieldReferences(scan.Code, options.entityFields);
209
276
  }
210
277
  return { valid: true };
211
278
  }
212
- /**
213
- * Remove string literals to avoid false positives in keyword detection.
214
- *
215
- * 🚨 SECURITY: delegates to {@link StripSQLStringLiterals} — read the warning there before
216
- * changing anything about how literals are matched. This must never grow its own regex again.
217
- */
218
- removeStringLiterals(expression) {
219
- return StripSQLStringLiterals(expression);
220
- }
221
279
  /**
222
280
  * Check for dangerous SQL patterns that indicate injection attempts
223
281
  */
224
- checkDangerousPatterns(expression, options) {
282
+ checkDangerousPatterns(scan, options) {
225
283
  const isFullQuery = options.context === 'full_query';
226
- // Build blocked list - explicitly typed as string[] for mutability
227
- let blocked = [...DANGEROUS_SQL_KEYWORDS];
228
- if (options.additionalBlocked) {
229
- blocked.push(...options.additionalBlocked);
230
- }
231
- // For full_query context, remove keywords that are legitimate in SELECT statements
232
- if (isFullQuery) {
233
- const allowedSet = new Set(FULL_QUERY_ALLOWED_KEYWORDS.map(k => k.toUpperCase()));
234
- blocked = blocked.filter(kw => !allowedSet.has(kw.toUpperCase()));
235
- }
236
- // Add SELECT to blocked unless context allows it (prevents subqueries in expressions)
237
- if (!isFullQuery && !options.allowSubqueries && !blocked.includes('SELECT')) {
238
- blocked.push('SELECT');
239
- }
240
- // For full_query, strip comments before keyword checking (agent SQL has header comment blocks).
241
- // For expressions, comments are still rejected outright as injection vectors.
242
- let textToCheck;
243
- if (isFullQuery) {
244
- textToCheck = this.stripSQLComments(expression).toUpperCase();
245
- }
246
- else {
247
- const upper = expression.toUpperCase();
248
- if (upper.includes('--') || upper.includes('/*') || upper.includes('*/')) {
249
- return {
250
- valid: false,
251
- error: 'Comments are not allowed in SQL expressions',
252
- trigger: 'comment'
253
- };
254
- }
255
- textToCheck = upper;
284
+ const textToCheck = scan.Code.toUpperCase();
285
+ // Full queries may carry comments (agent SQL has header comment blocks); the scan has already
286
+ // blanked them. In expressions, comments are rejected outright as injection vectors, and so is
287
+ // a stray `*/`, which is code.
288
+ if (!isFullQuery && (scan.CommentCount > 0 || textToCheck.includes('*/'))) {
289
+ return {
290
+ valid: false,
291
+ error: 'Comments are not allowed in SQL expressions',
292
+ trigger: 'comment'
293
+ };
256
294
  }
257
- for (const keyword of blocked) {
295
+ for (const keyword of this.blockedKeywords(options)) {
258
296
  // Use word boundaries to avoid false positives (e.g., "DESCRIPTION" containing "EXEC")
259
297
  const pattern = new RegExp(`\\b${this.escapeRegex(keyword)}\\b`, 'i');
260
298
  if (pattern.test(textToCheck)) {
@@ -269,9 +307,10 @@ export class SQLExpressionValidator extends BaseSingleton {
269
307
  // Block references to database system catalogs / metadata objects in ALL contexts
270
308
  // (including full_query). These live outside MemberJunction's entity-permission model, so
271
309
  // permitting them turns a validated SELECT into a schema-enumeration / credential-exfiltration
272
- // primitive. String literals were already stripped upstream, so legitimate literal values are safe.
310
+ // primitive. Literals are blanked and quoted names unwrapped, so `[sys].[x]` is caught too.
311
+ const namesToCheck = scan.CodeWithIdentifiers.toUpperCase();
273
312
  for (const sysPattern of BLOCKED_SYSTEM_OBJECT_PATTERNS) {
274
- if (sysPattern.test(textToCheck)) {
313
+ if (sysPattern.test(namesToCheck)) {
275
314
  return {
276
315
  valid: false,
277
316
  error: 'Access to database system catalogs / metadata objects is not allowed',
@@ -279,13 +318,10 @@ export class SQLExpressionValidator extends BaseSingleton {
279
318
  };
280
319
  }
281
320
  }
282
- // Check statement terminator (prevents multi-statement injection).
283
- // For full_query context, strip comments first (a trailing semicolon may be followed by
284
- // an inline comment like `ORDER BY x DESC; -- highest first`), then strip the trailing
285
- // semicolon. Only reject if semicolons remain mid-statement, indicating injection.
286
- const textForSemicolonCheck = isFullQuery
287
- ? this.stripSQLComments(expression).replace(/;\s*$/, '') // strip comments then trailing semicolon
288
- : expression;
321
+ // Check statement terminator (prevents multi-statement injection). Comments are already
322
+ // blanked, so a full query may end with one semicolon even when a comment follows it
323
+ // (`ORDER BY x DESC; -- highest first`). A semicolon anywhere else indicates injection.
324
+ const textForSemicolonCheck = isFullQuery ? textToCheck.replace(/;\s*$/, '') : textToCheck;
289
325
  if (textForSemicolonCheck.includes(';')) {
290
326
  return {
291
327
  valid: false,
@@ -295,6 +331,27 @@ export class SQLExpressionValidator extends BaseSingleton {
295
331
  }
296
332
  return { valid: true };
297
333
  }
334
+ /**
335
+ * The keywords {@link checkDangerousPatterns} rejects in this context.
336
+ */
337
+ blockedKeywords(options) {
338
+ const isFullQuery = options.context === 'full_query';
339
+ // Build blocked list - explicitly typed as string[] for mutability
340
+ let blocked = [...DANGEROUS_SQL_KEYWORDS];
341
+ if (options.additionalBlocked) {
342
+ blocked.push(...options.additionalBlocked);
343
+ }
344
+ // For full_query context, remove keywords that are legitimate in SELECT statements
345
+ if (isFullQuery) {
346
+ const allowedSet = new Set(FULL_QUERY_ALLOWED_KEYWORDS.map(k => k.toUpperCase()));
347
+ blocked = blocked.filter(kw => !allowedSet.has(kw.toUpperCase()));
348
+ }
349
+ // Add SELECT to blocked unless context allows it (prevents subqueries in expressions)
350
+ if (!isFullQuery && !options.allowSubqueries && !blocked.includes('SELECT')) {
351
+ blocked.push('SELECT');
352
+ }
353
+ return blocked;
354
+ }
298
355
  /**
299
356
  * Check that function names are in the allowlist
300
357
  */
@@ -324,12 +381,12 @@ export class SQLExpressionValidator extends BaseSingleton {
324
381
  /**
325
382
  * Context-specific validation rules
326
383
  */
327
- checkContextRules(expression, options) {
384
+ checkContextRules(scan, options) {
328
385
  // For aggregate context, require at least one aggregate function (unless explicitly disabled)
329
386
  if (options.context === 'aggregate' && options.requireAggregate !== false) {
330
387
  const hasAggregate = ALLOWED_SQL_FUNCTIONS.aggregates.some(fn => {
331
388
  const pattern = new RegExp(`\\b${fn}\\s*\\(`, 'i');
332
- return pattern.test(expression);
389
+ return pattern.test(scan.Code);
333
390
  });
334
391
  if (!hasAggregate) {
335
392
  return {
@@ -339,17 +396,15 @@ export class SQLExpressionValidator extends BaseSingleton {
339
396
  };
340
397
  }
341
398
  }
342
- // For full_query context, the query must start with SELECT or WITH (CTE)
343
- if (options.context === 'full_query') {
344
- const stripped = this.stripSQLComments(expression).trim();
345
- const upper = stripped.toUpperCase();
346
- if (!upper.startsWith('SELECT') && !upper.startsWith('WITH')) {
347
- return {
348
- valid: false,
349
- error: 'Ad-hoc query must start with SELECT or WITH',
350
- suggestion: 'Only SELECT statements and CTEs (WITH ... AS) are allowed'
351
- };
352
- }
399
+ // For full_query context, the first token (after comments) must be the unquoted keyword SELECT or
400
+ // WITH, compared in ASCII. SQL Server runs a batch that opens with any other name, quoted or not,
401
+ // as a procedure call.
402
+ if (options.context === 'full_query' && !FULL_QUERY_OPENING_KEYWORD.test(scan.LeadingWord ?? '')) {
403
+ return {
404
+ valid: false,
405
+ error: 'Ad-hoc query must start with SELECT or WITH',
406
+ suggestion: 'Only SELECT statements and CTEs (WITH ... AS) are allowed'
407
+ };
353
408
  }
354
409
  return { valid: true };
355
410
  }
@@ -379,16 +434,6 @@ export class SQLExpressionValidator extends BaseSingleton {
379
434
  // For now, we allow it to pass
380
435
  }
381
436
  }
382
- /**
383
- * Strip SQL comments (single-line -- and multi-line block comments) from a query.
384
- * Used by full_query context to allow agent-generated header comments
385
- * without triggering the comment injection check.
386
- */
387
- stripSQLComments(sql) {
388
- return sql
389
- .replace(/--[^\n]*/g, '') // Single-line comments
390
- .replace(/\/\*[\s\S]*?\*\//g, ''); // Block comments
391
- }
392
437
  /**
393
438
  * Escape special regex characters in a string
394
439
  */
@@ -414,13 +459,17 @@ export class SQLExpressionValidator extends BaseSingleton {
414
459
  * Validate a full SQL query (SELECT or WITH/CTE statement).
415
460
  * Blocks mutations, dangerous operations, and multi-statement injection.
416
461
  * Allows SELECT, subqueries, set operations, and SQL comments.
462
+ *
463
+ * @param sql The query to validate
464
+ * @param dialect The dialect that will run the query. Without one, the query must pass under
465
+ * every dialect {@link ScanSQLForScreening} knows.
417
466
  */
418
- ValidateFullQuery(sql) {
467
+ ValidateFullQuery(sql, dialect) {
419
468
  // Normalize literal escape sequences before validation — agent-generated
420
469
  // SQL may arrive with literal \n instead of real newlines, which breaks
421
470
  // comment stripping and the "must start with SELECT" check.
422
471
  const normalized = this.normalizeSQLWhitespace(sql);
423
- return this.validate(normalized, { context: 'full_query' });
472
+ return this.validate(normalized, { context: 'full_query', Dialect: dialect });
424
473
  }
425
474
  /** @deprecated Use {@link ValidateFullQuery}. */
426
475
  validateFullQuery(sql) {
@@ -1 +1 @@
1
- {"version":3,"file":"SQLExpressionValidator.js","sourceRoot":"","sources":["../src/SQLExpressionValidator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SQLExpressionValidator.js","sourceRoot":"","sources":["../src/SQLExpressionValidator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @fileoverview Dialect-aware SQL scanner for MemberJunction's SQL denylist screens.
3
+ *
4
+ * A denylist screen is only as good as its idea of which text is code. This scanner reads SQL the
5
+ * way SQL Server or PostgreSQL splits it into tokens and marks every string literal, quoted
6
+ * identifier and comment, so a screen checks exactly the text the database runs as code. When it
7
+ * cannot read the text the way the database would, it reports that, so the screen rejects the text
8
+ * instead of guessing.
9
+ *
10
+ * Used by `SQLExpressionValidator` and `DatabaseProviderBase.ValidateUserProvidedSQLClause`.
11
+ *
12
+ * @module @memberjunction/global/SQLScreeningScanner
13
+ */
14
+ /** SQL dialects whose lexical rules {@link ScanSQLForScreening} knows. */
15
+ export type SQLScreeningDialect = 'sqlserver' | 'postgresql';
16
+ /**
17
+ * Every dialect {@link ScanSQLForScreening} knows. A screen that does not know which database will
18
+ * run the SQL must accept it under each one.
19
+ */
20
+ export declare const SQL_SCREENING_DIALECTS: readonly SQLScreeningDialect[];
21
+ /** Kinds of text that the database never runs as code. */
22
+ export type SQLScreeningSpanKind = 'string' | 'identifier' | 'comment';
23
+ /** One string literal, quoted identifier or comment found by {@link ScanSQLForScreening}. */
24
+ export interface SQLScreeningSpan {
25
+ Kind: SQLScreeningSpanKind;
26
+ /** Offset of the first character, including a prefix such as `N` or `E`. */
27
+ Start: number;
28
+ /** Offset just past the last character. */
29
+ End: number;
30
+ }
31
+ /** Kinds of token {@link ScanSQLForScreening} reports. Comments are not tokens. */
32
+ export type SQLScreeningTokenKind = 'word' | 'identifier' | 'number' | 'string' | 'symbol';
33
+ /**
34
+ * One token the database reads as code, or one literal. Whitespace and comments are not tokens.
35
+ */
36
+ export interface SQLScreeningToken {
37
+ Kind: SQLScreeningTokenKind;
38
+ /**
39
+ * The text as the database reads it: a quoted identifier's bare name with its doubled quote
40
+ * characters undone (`[a]]b]` reads `a]b`); every other kind exactly as written.
41
+ */
42
+ Text: string;
43
+ /** Offset of the first character, including a quote or prefix. */
44
+ Start: number;
45
+ /** Offset just past the last character. */
46
+ End: number;
47
+ }
48
+ /** Result of {@link ScanSQLForScreening}. */
49
+ export interface SQLScreeningScan {
50
+ /** The dialect whose rules were applied. */
51
+ Dialect: SQLScreeningDialect;
52
+ /** True when every character was read the way the database reads it. When false, reject the text. */
53
+ Classified: boolean;
54
+ /** Why the text could not be classified; null when {@link Classified} is true. */
55
+ Error: string | null;
56
+ /** The string literals, quoted identifiers and comments found, in order. */
57
+ Spans: SQLScreeningSpan[];
58
+ /** Number of comments found. */
59
+ CommentCount: number;
60
+ /**
61
+ * The input with each string literal, quoted identifier and comment replaced by one space, and a
62
+ * space between a number and a word written against it. Only code remains, split where the
63
+ * database splits it, so `N'x'UNION` reads as ` UNION` and `1UNION` as `1 UNION`. Keyword,
64
+ * statement-separator and comment-marker checks read this. When {@link Classified} is false,
65
+ * the text from the point where the scan stopped is left as written.
66
+ */
67
+ Code: string;
68
+ /**
69
+ * {@link Code}, except each quoted identifier is written as its bare name between spaces, so
70
+ * `[sys].[objects]` reads as ` sys . objects `. Checks for object and function names read this,
71
+ * so quoting a name does not hide it.
72
+ */
73
+ CodeWithIdentifiers: string;
74
+ /**
75
+ * The first token that is not whitespace or a comment, when that token is an unquoted word and
76
+ * the character after it is ASCII, so every server ends the word in the same place. Null when a
77
+ * literal, quoted identifier, number or symbol comes first, or there is no token. Checks for the
78
+ * statement's opening keyword read this.
79
+ */
80
+ LeadingWord: string | null;
81
+ /**
82
+ * Every token in order: unquoted words, quoted identifiers, numbers, string literals and
83
+ * single-character symbols. When {@link Classified} is false, the list stops where the scan
84
+ * stopped.
85
+ */
86
+ Tokens: SQLScreeningToken[];
87
+ }
88
+ /**
89
+ * Reads `sql` the way `dialect` splits it into tokens. Returns the string literals, quoted
90
+ * identifiers and comments it contains, and the code that remains.
91
+ *
92
+ * Both dialects: `'…'` and `N'…'` strings, in which `''` is an escaped quote and a backslash is an
93
+ * ordinary character; `"…"` identifiers with `""` escapes; `--` comments to the end of the line; and
94
+ * nested block comments. SQL Server adds `[…]` identifiers with `]]` escapes. PostgreSQL adds `E'…'`
95
+ * strings, in which a backslash escapes the next character, dollar-quoted strings (`$$…$$`,
96
+ * `$tag$…$tag$`), and `U&"…"` identifiers, read as one identifier whose `\XXXX` and `\+XXXXXX`
97
+ * escapes (or those of the character a following `UESCAPE '<c>'` names) are decoded, so the name is
98
+ * the one PostgreSQL resolves; a `[` is code there.
99
+ *
100
+ * 🚨 Do not add backslash escapes to plain `'…'` strings. Neither server honors them (PostgreSQL is
101
+ * assumed to run with `standard_conforming_strings = on`, its default since 9.1), and a scanner that
102
+ * does reads `x = 'a\') ; DROP TABLE t; --'` as one literal while the database runs the DROP.
103
+ *
104
+ * The scan stops and reports an error, instead of guessing, when:
105
+ * - a string, quoted identifier, dollar-quoted string or block comment is not closed;
106
+ * - the text contains a NUL character;
107
+ * - a `--` comment contains a line break other than `\n` or `\r\n`;
108
+ * - a PostgreSQL `E'…'` string is followed by another quoted segment, which PostgreSQL can join to
109
+ * it and read with backslash escapes;
110
+ * - a PostgreSQL `U&"…"` identifier has an escape or a `UESCAPE` clause PostgreSQL would reject;
111
+ * - on SQL Server, a number is followed by `e` and a word character (`1EXEC` splits two ways).
112
+ *
113
+ * @param sql The clause, expression or query to read
114
+ * @param dialect The dialect whose lexical rules apply
115
+ */
116
+ export declare function ScanSQLForScreening(sql: string, dialect: SQLScreeningDialect): SQLScreeningScan;
117
+ //# sourceMappingURL=SQLScreeningScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SQLScreeningScanner.d.ts","sourceRoot":"","sources":["../src/SQLScreeningScanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,0EAA0E;AAC1E,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,YAAY,CAAC;AAE7D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,EAAgC,CAAC;AAElG,0DAA0D;AAC1D,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;AAEvE,6FAA6F;AAC7F,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACf;AAED,mFAAmF;AACnF,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3F;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;CACf;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC7B,4CAA4C;IAC5C,OAAO,EAAE,mBAAmB,CAAC;IAC7B,qGAAqG;IACrG,UAAU,EAAE,OAAO,CAAC;IACpB,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4EAA4E;IAC5E,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,gCAAgC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;OAIG;IACH,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAqED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,gBAAgB,CAE/F"}