circle-ir 3.56.0 → 3.58.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 (39) hide show
  1. package/configs/sinks/golang.json +61 -0
  2. package/configs/sinks/nodejs.json +11 -6
  3. package/configs/sinks/python.json +24 -0
  4. package/configs/sinks/rust.json +30 -0
  5. package/configs/sinks/sql.yaml +53 -0
  6. package/dist/analysis/config-loader.d.ts.map +1 -1
  7. package/dist/analysis/config-loader.js +57 -9
  8. package/dist/analysis/config-loader.js.map +1 -1
  9. package/dist/analysis/constant-propagation/patterns.d.ts.map +1 -1
  10. package/dist/analysis/constant-propagation/patterns.js +12 -0
  11. package/dist/analysis/constant-propagation/patterns.js.map +1 -1
  12. package/dist/analysis/constant-propagation/propagator.d.ts +62 -0
  13. package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
  14. package/dist/analysis/constant-propagation/propagator.js +321 -7
  15. package/dist/analysis/constant-propagation/propagator.js.map +1 -1
  16. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  17. package/dist/analysis/passes/language-sources-pass.js +55 -14
  18. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  19. package/dist/analysis/passes/security-headers-pass.d.ts.map +1 -1
  20. package/dist/analysis/passes/security-headers-pass.js +93 -0
  21. package/dist/analysis/passes/security-headers-pass.js.map +1 -1
  22. package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
  23. package/dist/analysis/passes/sink-filter-pass.js +16 -1
  24. package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
  25. package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
  26. package/dist/analysis/passes/taint-propagation-pass.js +153 -9
  27. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  28. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  29. package/dist/analysis/taint-matcher.js +116 -2
  30. package/dist/analysis/taint-matcher.js.map +1 -1
  31. package/dist/analysis/taint-propagation.d.ts.map +1 -1
  32. package/dist/analysis/taint-propagation.js +25 -1
  33. package/dist/analysis/taint-propagation.js.map +1 -1
  34. package/dist/browser/circle-ir.js +533 -45
  35. package/dist/core/circle-ir-core.cjs +401 -21
  36. package/dist/core/circle-ir-core.js +401 -21
  37. package/dist/types/config.d.ts +7 -0
  38. package/dist/types/config.d.ts.map +1 -1
  39. package/package.json +1 -1
@@ -157,6 +157,67 @@
157
157
  "class": "template",
158
158
  "removes": ["xss"],
159
159
  "note": "template.HTMLEscapeString escapes HTML"
160
+ },
161
+ {
162
+ "method": "Atoi",
163
+ "class": "strconv",
164
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
165
+ "note": "Numeric cast — output cannot carry string injection"
166
+ },
167
+ {
168
+ "method": "ParseInt",
169
+ "class": "strconv",
170
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
171
+ },
172
+ {
173
+ "method": "ParseFloat",
174
+ "class": "strconv",
175
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
176
+ },
177
+ {
178
+ "method": "ParseUint",
179
+ "class": "strconv",
180
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
181
+ },
182
+ {
183
+ "method": "ParseBool",
184
+ "class": "strconv",
185
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
186
+ },
187
+ {
188
+ "method": "Parse",
189
+ "class": "uuid",
190
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
191
+ "note": "uuid.Parse() — throws on non-UUID input"
192
+ },
193
+ {
194
+ "method": "MustParse",
195
+ "class": "uuid",
196
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
197
+ },
198
+ {
199
+ "method": "Base",
200
+ "class": "filepath",
201
+ "removes": ["path_traversal"],
202
+ "note": "filepath.Base returns final element only — strips traversal segments"
203
+ },
204
+ {
205
+ "method": "EvalSymlinks",
206
+ "class": "filepath",
207
+ "removes": ["path_traversal"],
208
+ "note": "filepath.EvalSymlinks resolves symlinks to absolute path"
209
+ },
210
+ {
211
+ "method": "Clean",
212
+ "class": "path",
213
+ "removes": ["path_traversal"],
214
+ "note": "path.Clean (slash-only) normalizes ../ segments"
215
+ },
216
+ {
217
+ "method": "Base",
218
+ "class": "path",
219
+ "removes": ["path_traversal"],
220
+ "note": "path.Base returns final slash-element only"
160
221
  }
161
222
  ]
162
223
  }
@@ -625,18 +625,23 @@
625
625
  },
626
626
  {
627
627
  "method": "parseInt",
628
- "removes": ["sql_injection", "command_injection"],
629
- "note": "Converts to integer - safe for numeric values"
628
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
629
+ "note": "Numeric cast output cannot carry string injection"
630
630
  },
631
631
  {
632
632
  "method": "parseFloat",
633
- "removes": ["sql_injection", "command_injection"],
634
- "note": "Converts to float - safe for numeric values"
633
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
634
+ "note": "Numeric cast output cannot carry string injection"
635
635
  },
636
636
  {
637
637
  "method": "Number",
638
- "removes": ["sql_injection", "command_injection"],
639
- "note": "Number constructor - safe for numeric values"
638
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
639
+ "note": "Number() constructor output cannot carry string injection"
640
+ },
641
+ {
642
+ "method": "BigInt",
643
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
644
+ "note": "BigInt() constructor — output cannot carry string injection"
640
645
  },
641
646
  {
642
647
  "method": "encodeURIComponent",
@@ -505,6 +505,30 @@
505
505
  "class": "werkzeug.utils",
506
506
  "sanitizes": ["path_traversal"],
507
507
  "note": "werkzeug secure_filename() - filename sanitization"
508
+ },
509
+ {
510
+ "method": "int",
511
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
512
+ "note": "Python numeric cast — output cannot carry string injection"
513
+ },
514
+ {
515
+ "method": "float",
516
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
517
+ },
518
+ {
519
+ "method": "bool",
520
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
521
+ },
522
+ {
523
+ "method": "UUID",
524
+ "class": "uuid",
525
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
526
+ "note": "uuid.UUID() throws on non-UUID input — output is a typed UUID"
527
+ },
528
+ {
529
+ "method": "Decimal",
530
+ "class": "decimal",
531
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
508
532
  }
509
533
  ]
510
534
  }
@@ -474,6 +474,18 @@
474
474
  "removes": ["xss"],
475
475
  "note": "html_escape::encode_quoted_attribute()"
476
476
  },
477
+ {
478
+ "method": "encode_safe",
479
+ "class": "html_escape",
480
+ "removes": ["xss"],
481
+ "note": "html_escape::encode_safe() — broad-safe HTML entity encoding"
482
+ },
483
+ {
484
+ "method": "encode_double_quoted_attribute",
485
+ "class": "html_escape",
486
+ "removes": ["xss"],
487
+ "note": "html_escape::encode_double_quoted_attribute()"
488
+ },
477
489
  {
478
490
  "method": "clean",
479
491
  "class": "ammonia",
@@ -485,6 +497,24 @@
485
497
  "class": "Builder",
486
498
  "removes": ["xss"],
487
499
  "note": "ammonia::Builder::clean() HTML sanitizer"
500
+ },
501
+ {
502
+ "method": "file_name",
503
+ "class": "Path",
504
+ "removes": ["path_traversal"],
505
+ "note": "Path::file_name() returns final component only — strips traversal segments"
506
+ },
507
+ {
508
+ "method": "canonicalize",
509
+ "class": "Path",
510
+ "removes": ["path_traversal"],
511
+ "note": "Path::canonicalize() resolves symlinks and `..` to absolute path"
512
+ },
513
+ {
514
+ "method": "components",
515
+ "class": "Path",
516
+ "removes": ["path_traversal"],
517
+ "note": "Path::components() — iterator gives normalized components, used for prefix checks"
488
518
  }
489
519
  ]
490
520
  }
@@ -262,6 +262,59 @@
262
262
  "removes": [
263
263
  "sql_injection"
264
264
  ]
265
+ },
266
+ {
267
+ "method": "parseInt",
268
+ "class": "Integer",
269
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
270
+ "note": "Java numeric cast — output cannot carry string injection"
271
+ },
272
+ {
273
+ "method": "parseLong",
274
+ "class": "Long",
275
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
276
+ },
277
+ {
278
+ "method": "parseDouble",
279
+ "class": "Double",
280
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
281
+ },
282
+ {
283
+ "method": "parseFloat",
284
+ "class": "Float",
285
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
286
+ },
287
+ {
288
+ "method": "parseShort",
289
+ "class": "Short",
290
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
291
+ },
292
+ {
293
+ "method": "parseByte",
294
+ "class": "Byte",
295
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
296
+ },
297
+ {
298
+ "method": "valueOf",
299
+ "class": "Integer",
300
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
301
+ },
302
+ {
303
+ "method": "valueOf",
304
+ "class": "Long",
305
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"]
306
+ },
307
+ {
308
+ "method": "fromString",
309
+ "class": "UUID",
310
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
311
+ "note": "UUID.fromString throws on non-UUID input — output is a typed UUID"
312
+ },
313
+ {
314
+ "method": "valueOf",
315
+ "class": "Enum",
316
+ "removes": ["sql_injection", "command_injection", "path_traversal", "code_injection"],
317
+ "note": "Enum.valueOf throws on unknown name — output is a typed enum"
265
318
  }
266
319
  ]
267
320
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/analysis/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAEjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAiB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG;IACtD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAcA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,GACrB,WAAW,CAQb;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,EAob1C,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAAW,EAs2CtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,gBAAgB,EAoMhD,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAM9C;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,UAAU,EA8F5C,CAAC"}
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/analysis/config-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAE5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAEjD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAiB1E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG;IACtD,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAcA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EAAE,EACxB,YAAY,EAAE,MAAM,EAAE,GACrB,WAAW,CAQb;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,EAub1C,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,WAAW,EA22CtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,gBAAgB,EAiPhD,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAM9C;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,UAAU,EA8F5C,CAAC"}
@@ -418,11 +418,14 @@ export const DEFAULT_SOURCES = [
418
418
  // Rocket
419
419
  { method: 'param', class: 'Request', type: 'http_param', severity: 'high', return_tainted: true },
420
420
  { method: 'cookies', class: 'Request', type: 'http_cookie', severity: 'high', return_tainted: true },
421
- // Axum extractors
422
- { method: 'Json', type: 'http_body', severity: 'high', return_tainted: true },
423
- { method: 'Query', type: 'http_param', severity: 'high', return_tainted: true },
424
- { method: 'Path', type: 'http_path', severity: 'high', return_tainted: true },
425
- { method: 'Form', type: 'http_param', severity: 'high', return_tainted: true },
421
+ // Axum extractors — Rust-only. The simple names `Json`/`Query`/`Path`/`Form`
422
+ // collide with stdlib types in other ecosystems (notably Python's
423
+ // `pathlib.Path` constructor and `flask.Form`), so they MUST be
424
+ // language-scoped to Rust to avoid spurious source matches.
425
+ { method: 'Json', type: 'http_body', severity: 'high', return_tainted: true, languages: ['rust'] },
426
+ { method: 'Query', type: 'http_param', severity: 'high', return_tainted: true, languages: ['rust'] },
427
+ { method: 'Path', type: 'http_path', severity: 'high', return_tainted: true, languages: ['rust'] },
428
+ { method: 'Form', type: 'http_param', severity: 'high', return_tainted: true, languages: ['rust'] },
426
429
  // Rust std library
427
430
  { method: 'var', class: 'env', type: 'env_input', severity: 'medium', return_tainted: true },
428
431
  { method: 'var_os', class: 'env', type: 'env_input', severity: 'medium', return_tainted: true },
@@ -625,10 +628,15 @@ export const DEFAULT_SINKS = [
625
628
  { method: 'PathResource', class: 'constructor', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
626
629
  // Additional resource/file patterns
627
630
  { method: 'forFile', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
628
- { method: 'resolve', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
629
- { method: 'resolve', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
630
- { method: 'resolveSibling', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
631
- { method: 'relativize', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'medium', arg_positions: [0] },
631
+ // Java NIO `Path.resolve(other)` joining with an untrusted `other` can
632
+ // escape the parent directory. Language-scoped to Java because the simple
633
+ // name `resolve` collides with Python `pathlib.Path.resolve()`
634
+ // (a canonicalization SANITIZER, no argument), JS `Promise.resolve(...)`,
635
+ // and Rust `Path::canonicalize` variants. Sprint 9 #48.2.
636
+ { method: 'resolve', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0], languages: ['java'] },
637
+ { method: 'resolve', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0], languages: ['java'] },
638
+ { method: 'resolveSibling', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0], languages: ['java'] },
639
+ { method: 'relativize', class: 'Path', type: 'path_traversal', cwe: 'CWE-22', severity: 'medium', arg_positions: [0], languages: ['java'] },
632
640
  // Static file configuration
633
641
  { method: 'staticFiles', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
634
642
  { method: 'setRoot', type: 'path_traversal', cwe: 'CWE-22', severity: 'high', arg_positions: [0] },
@@ -1775,6 +1783,16 @@ export const DEFAULT_SANITIZERS = [
1775
1783
  // Rust path sanitizers
1776
1784
  { method: 'file_name', removes: ['path_traversal'] }, // Returns just filename, strips path
1777
1785
  { method: 'canonicalize', removes: ['path_traversal'] }, // Resolves symlinks and normalizes
1786
+ // Go path sanitizers (#51) — filepath.Base strips directory components
1787
+ // (fully sanitizes), filepath.Clean / path.Clean normalize away ../ segments
1788
+ // (defense-in-depth — mirrors Java getCanonicalPath in this table; the
1789
+ // stricter Clean+HasPrefix guard recognition is tracked separately).
1790
+ // EvalSymlinks is the Go equivalent of Java's Path.toRealPath.
1791
+ { method: 'Base', class: 'filepath', removes: ['path_traversal'] },
1792
+ { method: 'Base', class: 'path', removes: ['path_traversal'] },
1793
+ { method: 'Clean', class: 'filepath', removes: ['path_traversal'] },
1794
+ { method: 'Clean', class: 'path', removes: ['path_traversal'] },
1795
+ { method: 'EvalSymlinks', class: 'filepath', removes: ['path_traversal'] },
1778
1796
  // Log Injection sanitizers
1779
1797
  { method: 'replace', removes: ['log_injection'] }, // Used to remove newlines/control chars
1780
1798
  // LDAP Injection
@@ -1868,6 +1886,8 @@ export const DEFAULT_SANITIZERS = [
1868
1886
  { method: 'abspath', class: 'os.path', removes: ['path_traversal'] },
1869
1887
  { method: 'realpath', class: 'path', removes: ['path_traversal'] },
1870
1888
  { method: 'abspath', class: 'path', removes: ['path_traversal'] },
1889
+ // pathlib.Path.resolve() — canonicalizes path, resolves symlinks (Python 3)
1890
+ { method: 'resolve', class: 'Path', removes: ['path_traversal'] },
1871
1891
  // Python Type coercion
1872
1892
  { method: 'int', removes: ['sql_injection', 'command_injection', 'xss'] },
1873
1893
  { method: 'float', removes: ['sql_injection', 'command_injection'] },
@@ -1898,6 +1918,34 @@ export const DEFAULT_SANITIZERS = [
1898
1918
  { method: 'escape_html', removes: ['xss'] },
1899
1919
  // Rust Type coercion (parsing)
1900
1920
  { method: 'parse', removes: ['sql_injection', 'command_injection', 'xss'] }, // str.parse::<i32>()
1921
+ // =========================================================================
1922
+ // Type-cast taint barriers (#57)
1923
+ // Numeric/UUID casts cannot carry a string-injection payload.
1924
+ // =========================================================================
1925
+ // Java numeric parse — Integer.parseInt, Long.parseLong, etc.
1926
+ { method: 'parseInt', class: 'Integer', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1927
+ { method: 'parseLong', class: 'Long', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1928
+ { method: 'parseFloat', class: 'Float', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1929
+ { method: 'parseDouble', class: 'Double', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1930
+ { method: 'parseShort', class: 'Short', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1931
+ { method: 'parseByte', class: 'Byte', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1932
+ // Java UUID parse — UUID.fromString rejects non-UUID strings
1933
+ { method: 'fromString', class: 'UUID', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1934
+ // JavaScript numeric coercion (Number/parseInt/parseFloat already covered above; add path_traversal/code_injection)
1935
+ { method: 'BigInt', removes: ['sql_injection', 'nosql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1936
+ // Go numeric parse — strconv.Atoi, ParseInt, ParseFloat, ParseUint, ParseBool
1937
+ { method: 'Atoi', class: 'strconv', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1938
+ { method: 'ParseInt', class: 'strconv', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1939
+ { method: 'ParseFloat', class: 'strconv', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1940
+ { method: 'ParseUint', class: 'strconv', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1941
+ { method: 'ParseBool', class: 'strconv', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1942
+ // Go UUID parse
1943
+ { method: 'Parse', class: 'uuid', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1944
+ { method: 'MustParse', class: 'uuid', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1945
+ // Python — int/float already covered above; add bool + UUID/Decimal casts
1946
+ { method: 'bool', removes: ['sql_injection', 'command_injection', 'xss', 'code_injection'] },
1947
+ { method: 'UUID', class: 'uuid', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1948
+ { method: 'Decimal', class: 'decimal', removes: ['sql_injection', 'command_injection', 'path_traversal', 'code_injection'] },
1901
1949
  ];
1902
1950
  /**
1903
1951
  * Get the default taint configuration.