kirograph 0.20.0 → 0.20.1

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 (41) hide show
  1. package/dist/bin/installer/index.js +29 -0
  2. package/dist/bin/installer/index.js.map +2 -2
  3. package/dist/bin/kirograph.js +1 -1
  4. package/dist/extraction/extractor.js +26 -3
  5. package/dist/extraction/extractor.js.map +2 -2
  6. package/dist/extraction/grammars.js +1 -0
  7. package/dist/extraction/grammars.js.map +2 -2
  8. package/dist/extraction/languages.js +5 -1
  9. package/dist/extraction/languages.js.map +2 -2
  10. package/dist/patterns/library/code-eval-elixir.yaml +11 -0
  11. package/dist/patterns/library/command-injection-c.yaml +11 -0
  12. package/dist/patterns/library/command-injection-go.yaml +21 -3
  13. package/dist/patterns/library/command-injection-kotlin.yaml +10 -0
  14. package/dist/patterns/library/command-injection-php.yaml +11 -0
  15. package/dist/patterns/library/command-injection-ruby.yaml +11 -0
  16. package/dist/patterns/library/command-injection-rust.yaml +11 -0
  17. package/dist/patterns/library/dangerous-eval-bash.yaml +11 -0
  18. package/dist/patterns/library/dangerous-eval-php.yaml +11 -0
  19. package/dist/patterns/library/dangerous-eval-ruby.yaml +11 -0
  20. package/dist/patterns/library/dynamic-load-lua.yaml +10 -0
  21. package/dist/patterns/library/os-cmd-elixir.yaml +16 -0
  22. package/dist/patterns/library/os-execute-lua.yaml +10 -0
  23. package/dist/patterns/library/path-traversal-go.yaml +4 -4
  24. package/dist/patterns/library/process-launch-swift.yaml +11 -0
  25. package/dist/patterns/library/process-run-dart.yaml +15 -0
  26. package/dist/patterns/library/source-injection-bash.yaml +11 -0
  27. package/dist/patterns/library/sql-injection-dart.yaml +9 -0
  28. package/dist/patterns/library/sql-injection-kotlin.yaml +11 -0
  29. package/dist/patterns/library/sql-injection-php.yaml +9 -0
  30. package/dist/patterns/library/sql-injection-swift.yaml +11 -0
  31. package/dist/patterns/library/subshell-injection-ruby.yaml +8 -0
  32. package/dist/patterns/library/unsafe-block-rust.yaml +8 -0
  33. package/dist/patterns/library/unsafe-string-c.yaml +11 -0
  34. package/dist/patterns/runner.js +45 -27
  35. package/dist/patterns/runner.js.map +2 -2
  36. package/dist/security/integrator.js +8 -8
  37. package/dist/security/integrator.js.map +2 -2
  38. package/dist/types.js.map +2 -2
  39. package/dist/vectors/turboquant-index.js +261 -0
  40. package/dist/vectors/turboquant-index.js.map +7 -0
  41. package/package.json +20 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extraction/grammars.ts"],
4
- "sourcesContent": ["/**\n * Grammar Module \u2014 lazy WASM grammar loading for tree-sitter.\n * Centralises all grammar management previously inline in extractor.ts.\n * Sequential loading via grammarLoadChain prevents WASM race condition on Node 20+.\n */\n\nimport * as path from 'path';\nimport type { Language } from '../types';\n\n// \u2500\u2500 Module-level singletons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet parserLib: any = null;\nlet parserInitPromise: Promise<void> | null = null;\nconst loadedGrammars = new Map<Language, any>();\nlet grammarLoadChain = Promise.resolve();\n\n// \u2500\u2500 GRAMMAR_FILE_MAP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Maps each Language to its WASM file name (without .wasm extension).\n * Empty string means no WASM is available for that language.\n * Pascal is bundled separately; all others come from tree-sitter-wasms.\n */\nexport const GRAMMAR_FILE_MAP: Record<Language, string> = {\n typescript: 'tree-sitter-typescript',\n tsx: 'tree-sitter-tsx',\n javascript: 'tree-sitter-javascript',\n jsx: 'tree-sitter-javascript',\n python: 'tree-sitter-python',\n go: 'tree-sitter-go',\n rust: 'tree-sitter-rust',\n java: 'tree-sitter-java',\n c: 'tree-sitter-c',\n cpp: 'tree-sitter-cpp',\n csharp: 'tree-sitter-c_sharp',\n php: 'tree-sitter-php',\n ruby: 'tree-sitter-ruby',\n swift: 'tree-sitter-swift',\n kotlin: 'tree-sitter-kotlin',\n dart: 'tree-sitter-dart',\n svelte: 'tree-sitter-svelte',\n elixir: 'tree-sitter-elixir',\n scala: 'tree-sitter-scala',\n lua: 'tree-sitter-lua',\n zig: 'tree-sitter-zig',\n bash: 'tree-sitter-bash',\n ocaml: 'tree-sitter-ocaml',\n elm: 'tree-sitter-elm',\n solidity: 'tree-sitter-solidity',\n vue: 'tree-sitter-vue',\n objc: 'tree-sitter-objc',\n yaml: 'tree-sitter-yaml',\n // HCL (Terraform) is bundled in src/extraction/wasm/ (not in tree-sitter-wasms)\n hcl: 'tree-sitter-hcl',\n // CSS is in tree-sitter-wasms; SCSS is bundled in src/extraction/wasm/\n css: 'tree-sitter-css',\n scss: 'tree-sitter-scss',\n html: 'tree-sitter-html',\n // Pascal is bundled in src/extraction/wasm/ (not in tree-sitter-wasms)\n pascal: 'tree-sitter-pascal',\n // No WASM available\n liquid: '',\n // Jupyter notebooks are parsed via the notebook extractor (Python grammar on extracted code cells)\n jupyter: '',\n // ReScript: WASM available in tree-sitter-wasms\n rescript: 'tree-sitter-rescript',\n // Bundled WASMs \u2014 compiled from grammar sources, stored in src/extraction/wasm/\n // resolveWasmPath handles these via BUNDLED_WASM_LANGS set (no entry needed here)\n sql: '', r: '', julia: '', powershell: '', perl: '',\n astro: '', gdscript: '', nix: '', verilog: '',\n unknown: '',\n};\n\n// \u2500\u2500 initGrammars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Initialises the tree-sitter Parser runtime without loading any language grammars.\n * Safe to call multiple times \u2014 idempotent.\n */\nexport async function initGrammars(): Promise<void> {\n if (parserLib) return;\n if (parserInitPromise) return parserInitPromise;\n parserInitPromise = (async () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const TreeSitter = require('web-tree-sitter');\n await TreeSitter.Parser.init();\n parserLib = TreeSitter;\n })();\n return parserInitPromise;\n}\n\n// \u2500\u2500 resolveWasmPath \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Languages whose WASM is bundled in src/extraction/wasm/ (not from tree-sitter-wasms). */\nconst BUNDLED_WASM_LANGS = new Set<Language>([\n 'pascal', 'hcl', 'scss',\n // Compiled and bundled locally \u2014 no npm package with pre-built WASM available\n 'sql', 'r', 'julia', 'powershell', 'perl', 'gdscript', 'nix', 'verilog', 'astro',\n]);\n\n/**\n * Resolves the filesystem path to the WASM file for a given language.\n * Languages in BUNDLED_WASM_LANGS use bundled wasm files in src/extraction/wasm/.\n * All others are resolved from the tree-sitter-wasms npm package.\n * Returns null if the file cannot be located.\n */\nfunction resolveWasmPath(lang: Language): string | null {\n if (BUNDLED_WASM_LANGS.has(lang)) {\n return path.join(__dirname, 'wasm', `tree-sitter-${lang}.wasm`);\n }\n const grammarFile = GRAMMAR_FILE_MAP[lang];\n if (!grammarFile) return null;\n try {\n return require.resolve(`tree-sitter-wasms/out/${grammarFile}.wasm`);\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 loadGrammarsForLanguages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Loads WASM grammars for the given languages sequentially.\n * Already-loaded grammars are skipped.\n * WASM load failures are swallowed silently \u2014 no throw.\n */\nexport async function loadGrammarsForLanguages(languages: Language[]): Promise<void> {\n for (const lang of languages) {\n if (loadedGrammars.has(lang)) continue;\n\n await new Promise<void>((resolve) => {\n grammarLoadChain = grammarLoadChain.then(async () => {\n if (loadedGrammars.has(lang)) { resolve(); return; }\n const wasmPath = resolveWasmPath(lang);\n if (!wasmPath) { resolve(); return; }\n try {\n await initGrammars();\n const langObj = await parserLib.Language.load(wasmPath);\n loadedGrammars.set(lang, langObj);\n } catch {\n // Silently skip \u2014 no WASM or load failure\n }\n resolve();\n });\n });\n }\n}\n\n// \u2500\u2500 getParser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Returns a configured Parser instance for the given language.\n * Loads the grammar on demand if not yet cached.\n * Returns null if no grammar is available (unsupported/unknown language).\n */\nexport async function getParser(language: Language): Promise<any | null> {\n // Fast path \u2014 already loaded\n if (loadedGrammars.has(language)) {\n await initGrammars();\n const parser = new parserLib.Parser();\n parser.setLanguage(loadedGrammars.get(language));\n return parser;\n }\n\n // No WASM available for this language\n if (!resolveWasmPath(language)) return null;\n\n // Load on demand\n await loadGrammarsForLanguages([language]);\n\n if (!loadedGrammars.has(language)) return null;\n\n await initGrammars();\n const parser = new parserLib.Parser();\n parser.setLanguage(loadedGrammars.get(language));\n return parser;\n}\n\n// \u2500\u2500 Remaining exports \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Returns true when the grammar for the given language is already in the cache.\n */\nexport function isGrammarLoaded(language: Language): boolean {\n return loadedGrammars.has(language);\n}\n\n/**\n * Returns true if a WASM grammar file exists for the given language,\n * regardless of whether it has been loaded yet.\n * Use this to distinguish \"language has no grammar\" from \"grammar failed to load\".\n */\nexport function hasWasmGrammar(language: Language): boolean {\n return resolveWasmPath(language) !== null;\n}\n\n/**\n * Returns the list of languages for which a WASM grammar file is known.\n * Languages with an empty GRAMMAR_FILE_MAP entry are excluded.\n */\nexport function getSupportedLanguages(): Language[] {\n return (Object.keys(GRAMMAR_FILE_MAP) as Language[]).filter(\n (lang) => GRAMMAR_FILE_MAP[lang] !== ''\n );\n}\n\n/**\n * Removes all cached grammars and resets the module to uninitialised state.\n * Primarily for testing.\n */\nexport function clearParserCache(): void {\n parserLib = null;\n parserInitPromise = null;\n loadedGrammars.clear();\n grammarLoadChain = Promise.resolve();\n}\n\n/**\n * Convenience wrapper \u2014 loads all known supported language grammars.\n */\nexport async function loadAllGrammars(): Promise<void> {\n return loadGrammarsForLanguages(getSupportedLanguages());\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Grammar Module \u2014 lazy WASM grammar loading for tree-sitter.\n * Centralises all grammar management previously inline in extractor.ts.\n * Sequential loading via grammarLoadChain prevents WASM race condition on Node 20+.\n */\n\nimport * as path from 'path';\nimport type { Language } from '../types';\n\n// \u2500\u2500 Module-level singletons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet parserLib: any = null;\nlet parserInitPromise: Promise<void> | null = null;\nconst loadedGrammars = new Map<Language, any>();\nlet grammarLoadChain = Promise.resolve();\n\n// \u2500\u2500 GRAMMAR_FILE_MAP \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Maps each Language to its WASM file name (without .wasm extension).\n * Empty string means no WASM is available for that language.\n * Pascal is bundled separately; all others come from tree-sitter-wasms.\n */\nexport const GRAMMAR_FILE_MAP: Record<Language, string> = {\n typescript: 'tree-sitter-typescript',\n tsx: 'tree-sitter-tsx',\n javascript: 'tree-sitter-javascript',\n jsx: 'tree-sitter-javascript',\n python: 'tree-sitter-python',\n go: 'tree-sitter-go',\n rust: 'tree-sitter-rust',\n java: 'tree-sitter-java',\n c: 'tree-sitter-c',\n cpp: 'tree-sitter-cpp',\n csharp: 'tree-sitter-c_sharp',\n php: 'tree-sitter-php',\n ruby: 'tree-sitter-ruby',\n swift: 'tree-sitter-swift',\n kotlin: 'tree-sitter-kotlin',\n dart: 'tree-sitter-dart',\n svelte: 'tree-sitter-svelte',\n elixir: 'tree-sitter-elixir',\n scala: 'tree-sitter-scala',\n lua: 'tree-sitter-lua',\n zig: 'tree-sitter-zig',\n bash: 'tree-sitter-bash',\n ocaml: 'tree-sitter-ocaml',\n elm: 'tree-sitter-elm',\n solidity: 'tree-sitter-solidity',\n vue: 'tree-sitter-vue',\n objc: 'tree-sitter-objc',\n yaml: 'tree-sitter-yaml',\n // HCL (Terraform) is bundled in src/extraction/wasm/ (not in tree-sitter-wasms)\n hcl: 'tree-sitter-hcl',\n // CSS is in tree-sitter-wasms; SCSS is bundled in src/extraction/wasm/\n css: 'tree-sitter-css',\n scss: 'tree-sitter-scss',\n html: 'tree-sitter-html',\n // Pascal is bundled in src/extraction/wasm/ (not in tree-sitter-wasms)\n pascal: 'tree-sitter-pascal',\n // No WASM available\n liquid: '',\n // Jupyter notebooks are parsed via the notebook extractor (Python grammar on extracted code cells)\n jupyter: '',\n // ReScript: WASM available in tree-sitter-wasms\n rescript: 'tree-sitter-rescript',\n // Bundled WASMs \u2014 compiled from grammar sources, stored in src/extraction/wasm/\n // resolveWasmPath handles these via BUNDLED_WASM_LANGS set (no entry needed here)\n sql: '', r: '', julia: '', powershell: '', perl: '',\n astro: '', gdscript: '', nix: '', verilog: '', haskell: '',\n unknown: '',\n};\n\n// \u2500\u2500 initGrammars \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Initialises the tree-sitter Parser runtime without loading any language grammars.\n * Safe to call multiple times \u2014 idempotent.\n */\nexport async function initGrammars(): Promise<void> {\n if (parserLib) return;\n if (parserInitPromise) return parserInitPromise;\n parserInitPromise = (async () => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const TreeSitter = require('web-tree-sitter');\n await TreeSitter.Parser.init();\n parserLib = TreeSitter;\n })();\n return parserInitPromise;\n}\n\n// \u2500\u2500 resolveWasmPath \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Languages whose WASM is bundled in src/extraction/wasm/ (not from tree-sitter-wasms). */\nconst BUNDLED_WASM_LANGS = new Set<Language>([\n 'pascal', 'hcl', 'scss',\n // Compiled and bundled locally \u2014 no npm package with pre-built WASM available\n 'sql', 'r', 'julia', 'powershell', 'perl', 'gdscript', 'nix', 'verilog', 'astro',\n]);\n\n/**\n * Resolves the filesystem path to the WASM file for a given language.\n * Languages in BUNDLED_WASM_LANGS use bundled wasm files in src/extraction/wasm/.\n * All others are resolved from the tree-sitter-wasms npm package.\n * Returns null if the file cannot be located.\n */\nfunction resolveWasmPath(lang: Language): string | null {\n if (BUNDLED_WASM_LANGS.has(lang)) {\n return path.join(__dirname, 'wasm', `tree-sitter-${lang}.wasm`);\n }\n const grammarFile = GRAMMAR_FILE_MAP[lang];\n if (!grammarFile) return null;\n try {\n return require.resolve(`tree-sitter-wasms/out/${grammarFile}.wasm`);\n } catch {\n return null;\n }\n}\n\n// \u2500\u2500 loadGrammarsForLanguages \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Loads WASM grammars for the given languages sequentially.\n * Already-loaded grammars are skipped.\n * WASM load failures are swallowed silently \u2014 no throw.\n */\nexport async function loadGrammarsForLanguages(languages: Language[]): Promise<void> {\n for (const lang of languages) {\n if (loadedGrammars.has(lang)) continue;\n\n await new Promise<void>((resolve) => {\n grammarLoadChain = grammarLoadChain.then(async () => {\n if (loadedGrammars.has(lang)) { resolve(); return; }\n const wasmPath = resolveWasmPath(lang);\n if (!wasmPath) { resolve(); return; }\n try {\n await initGrammars();\n const langObj = await parserLib.Language.load(wasmPath);\n loadedGrammars.set(lang, langObj);\n } catch {\n // Silently skip \u2014 no WASM or load failure\n }\n resolve();\n });\n });\n }\n}\n\n// \u2500\u2500 getParser \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Returns a configured Parser instance for the given language.\n * Loads the grammar on demand if not yet cached.\n * Returns null if no grammar is available (unsupported/unknown language).\n */\nexport async function getParser(language: Language): Promise<any | null> {\n // Fast path \u2014 already loaded\n if (loadedGrammars.has(language)) {\n await initGrammars();\n const parser = new parserLib.Parser();\n parser.setLanguage(loadedGrammars.get(language));\n return parser;\n }\n\n // No WASM available for this language\n if (!resolveWasmPath(language)) return null;\n\n // Load on demand\n await loadGrammarsForLanguages([language]);\n\n if (!loadedGrammars.has(language)) return null;\n\n await initGrammars();\n const parser = new parserLib.Parser();\n parser.setLanguage(loadedGrammars.get(language));\n return parser;\n}\n\n// \u2500\u2500 Remaining exports \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Returns true when the grammar for the given language is already in the cache.\n */\nexport function isGrammarLoaded(language: Language): boolean {\n return loadedGrammars.has(language);\n}\n\n/**\n * Returns true if a WASM grammar file exists for the given language,\n * regardless of whether it has been loaded yet.\n * Use this to distinguish \"language has no grammar\" from \"grammar failed to load\".\n */\nexport function hasWasmGrammar(language: Language): boolean {\n return resolveWasmPath(language) !== null;\n}\n\n/**\n * Returns the list of languages for which a WASM grammar file is known.\n * Languages with an empty GRAMMAR_FILE_MAP entry are excluded.\n */\nexport function getSupportedLanguages(): Language[] {\n return (Object.keys(GRAMMAR_FILE_MAP) as Language[]).filter(\n (lang) => GRAMMAR_FILE_MAP[lang] !== ''\n );\n}\n\n/**\n * Removes all cached grammars and resets the module to uninitialised state.\n * Primarily for testing.\n */\nexport function clearParserCache(): void {\n parserLib = null;\n parserInitPromise = null;\n loadedGrammars.clear();\n grammarLoadChain = Promise.resolve();\n}\n\n/**\n * Convenience wrapper \u2014 loads all known supported language grammars.\n */\nexport async function loadAllGrammars(): Promise<void> {\n return loadGrammarsForLanguages(getSupportedLanguages());\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["parser"]
7
7
  }
@@ -112,7 +112,10 @@ const EXTENSION_MAP = {
112
112
  // Verilog / SystemVerilog (.v is Verilog, not Vim script)
113
113
  ".v": "verilog",
114
114
  ".sv": "verilog",
115
- ".svh": "verilog"
115
+ ".svh": "verilog",
116
+ // Haskell
117
+ ".hs": "haskell",
118
+ ".lhs": "haskell"
116
119
  };
117
120
  const GRAMMAR_MAP = {
118
121
  typescript: "tree-sitter-typescript",
@@ -164,6 +167,7 @@ const GRAMMAR_MAP = {
164
167
  gdscript: "tree-sitter-gdscript",
165
168
  nix: "tree-sitter-nix",
166
169
  verilog: "tree-sitter-verilog",
170
+ haskell: "tree-sitter-haskell",
167
171
  unknown: ""
168
172
  };
169
173
  function detectLanguage(filePath) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/extraction/languages.ts"],
4
- "sourcesContent": ["/**\n * Language detection and tree-sitter grammar mapping\n */\n\nimport type { Language } from '../types';\n\nexport const EXTENSION_MAP: Record<string, Language> = {\n '.ts': 'typescript',\n '.tsx': 'tsx',\n '.js': 'javascript',\n '.jsx': 'jsx',\n '.mjs': 'javascript',\n '.cjs': 'javascript',\n '.py': 'python',\n '.pyw': 'python',\n '.go': 'go',\n '.rs': 'rust',\n '.java': 'java',\n '.c': 'c',\n '.h': 'c',\n '.cpp': 'cpp',\n '.cc': 'cpp',\n '.cxx': 'cpp',\n '.hxx': 'cpp',\n '.hpp': 'cpp',\n '.cs': 'csharp',\n '.php': 'php',\n '.rb': 'ruby',\n '.rake': 'ruby',\n '.swift': 'swift',\n '.kt': 'kotlin',\n '.kts': 'kotlin',\n '.dart': 'dart',\n '.svelte': 'svelte',\n '.pas': 'pascal',\n '.dpr': 'pascal',\n '.dpk': 'pascal',\n '.lpr': 'pascal',\n '.dfm': 'pascal',\n '.fmx': 'pascal',\n '.liquid': 'liquid',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.scala': 'scala',\n '.sc': 'scala',\n '.sbt': 'scala',\n '.lua': 'lua',\n '.zig': 'zig',\n '.zon': 'zig',\n '.sh': 'bash',\n '.bash': 'bash',\n '.zsh': 'bash',\n '.ml': 'ocaml',\n '.mli': 'ocaml',\n '.elm': 'elm',\n '.sol': 'solidity',\n '.vue': 'vue',\n '.m': 'objc',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.tf': 'hcl',\n '.tfvars': 'hcl',\n '.css': 'css',\n '.scss': 'scss',\n '.sass': 'scss',\n '.html': 'html',\n '.htm': 'html',\n '.ipynb': 'jupyter',\n // ReScript\n '.res': 'rescript',\n '.resi': 'rescript',\n // SQL\n '.sql': 'sql',\n // R\n '.r': 'r',\n '.R': 'r',\n // Julia\n '.jl': 'julia',\n // PowerShell\n '.ps1': 'powershell',\n '.psm1': 'powershell',\n '.psd1': 'powershell',\n // Perl\n '.pl': 'perl',\n '.pm': 'perl',\n // Astro\n '.astro': 'astro',\n // GDScript (Godot)\n '.gd': 'gdscript',\n // Nix\n '.nix': 'nix',\n // Verilog / SystemVerilog (.v is Verilog, not Vim script)\n '.v': 'verilog',\n '.sv': 'verilog',\n '.svh': 'verilog',\n};\n\nexport const GRAMMAR_MAP: Record<Language, string> = {\n typescript: 'tree-sitter-typescript',\n tsx: 'tree-sitter-tsx',\n javascript: 'tree-sitter-javascript',\n jsx: 'tree-sitter-javascript',\n python: 'tree-sitter-python',\n go: 'tree-sitter-go',\n rust: 'tree-sitter-rust',\n java: 'tree-sitter-java',\n c: 'tree-sitter-c',\n cpp: 'tree-sitter-cpp',\n csharp: 'tree-sitter-c-sharp',\n php: 'tree-sitter-php',\n ruby: 'tree-sitter-ruby',\n swift: 'tree-sitter-swift',\n kotlin: 'tree-sitter-kotlin',\n dart: 'tree-sitter-dart',\n svelte: 'tree-sitter-svelte',\n elixir: 'tree-sitter-elixir',\n scala: 'tree-sitter-scala',\n lua: 'tree-sitter-lua',\n zig: 'tree-sitter-zig',\n bash: 'tree-sitter-bash',\n ocaml: 'tree-sitter-ocaml',\n elm: 'tree-sitter-elm',\n solidity: 'tree-sitter-solidity',\n vue: 'tree-sitter-vue',\n objc: 'tree-sitter-objc',\n yaml: 'tree-sitter-yaml',\n hcl: 'tree-sitter-hcl',\n css: 'tree-sitter-css',\n scss: 'tree-sitter-scss',\n html: 'tree-sitter-html',\n // Pascal and Liquid require custom WASM not bundled in tree-sitter-wasms\n pascal: '',\n liquid: '',\n // Jupyter notebooks are parsed via the notebook extractor (Python grammar on extracted code cells)\n jupyter: '',\n // ReScript: WASM available in tree-sitter-wasms\n rescript: 'tree-sitter-rescript',\n // Languages with WASMs TBD \u2014 files tracked, symbol extraction pending WASM compilation\n sql: 'tree-sitter-sql',\n r: 'tree-sitter-r',\n julia: 'tree-sitter-julia',\n powershell: 'tree-sitter-powershell',\n perl: 'tree-sitter-perl',\n astro: 'tree-sitter-astro',\n gdscript: 'tree-sitter-gdscript',\n nix: 'tree-sitter-nix',\n verilog: 'tree-sitter-verilog',\n unknown: '',\n};\n\nexport function detectLanguage(filePath: string): Language {\n const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();\n return EXTENSION_MAP[ext] ?? 'unknown';\n}\n\nexport function isSupportedLanguage(lang: Language): boolean {\n // All known languages are \"supported\" for indexing purposes.\n // Pascal and Liquid don't have a grammar WASM, so they'll be indexed\n // with empty symbol lists (file tracked but no AST extraction).\n return lang !== 'unknown';\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Language detection and tree-sitter grammar mapping\n */\n\nimport type { Language } from '../types';\n\nexport const EXTENSION_MAP: Record<string, Language> = {\n '.ts': 'typescript',\n '.tsx': 'tsx',\n '.js': 'javascript',\n '.jsx': 'jsx',\n '.mjs': 'javascript',\n '.cjs': 'javascript',\n '.py': 'python',\n '.pyw': 'python',\n '.go': 'go',\n '.rs': 'rust',\n '.java': 'java',\n '.c': 'c',\n '.h': 'c',\n '.cpp': 'cpp',\n '.cc': 'cpp',\n '.cxx': 'cpp',\n '.hxx': 'cpp',\n '.hpp': 'cpp',\n '.cs': 'csharp',\n '.php': 'php',\n '.rb': 'ruby',\n '.rake': 'ruby',\n '.swift': 'swift',\n '.kt': 'kotlin',\n '.kts': 'kotlin',\n '.dart': 'dart',\n '.svelte': 'svelte',\n '.pas': 'pascal',\n '.dpr': 'pascal',\n '.dpk': 'pascal',\n '.lpr': 'pascal',\n '.dfm': 'pascal',\n '.fmx': 'pascal',\n '.liquid': 'liquid',\n '.ex': 'elixir',\n '.exs': 'elixir',\n '.scala': 'scala',\n '.sc': 'scala',\n '.sbt': 'scala',\n '.lua': 'lua',\n '.zig': 'zig',\n '.zon': 'zig',\n '.sh': 'bash',\n '.bash': 'bash',\n '.zsh': 'bash',\n '.ml': 'ocaml',\n '.mli': 'ocaml',\n '.elm': 'elm',\n '.sol': 'solidity',\n '.vue': 'vue',\n '.m': 'objc',\n '.yaml': 'yaml',\n '.yml': 'yaml',\n '.tf': 'hcl',\n '.tfvars': 'hcl',\n '.css': 'css',\n '.scss': 'scss',\n '.sass': 'scss',\n '.html': 'html',\n '.htm': 'html',\n '.ipynb': 'jupyter',\n // ReScript\n '.res': 'rescript',\n '.resi': 'rescript',\n // SQL\n '.sql': 'sql',\n // R\n '.r': 'r',\n '.R': 'r',\n // Julia\n '.jl': 'julia',\n // PowerShell\n '.ps1': 'powershell',\n '.psm1': 'powershell',\n '.psd1': 'powershell',\n // Perl\n '.pl': 'perl',\n '.pm': 'perl',\n // Astro\n '.astro': 'astro',\n // GDScript (Godot)\n '.gd': 'gdscript',\n // Nix\n '.nix': 'nix',\n // Verilog / SystemVerilog (.v is Verilog, not Vim script)\n '.v': 'verilog',\n '.sv': 'verilog',\n '.svh': 'verilog',\n // Haskell\n '.hs': 'haskell',\n '.lhs': 'haskell',\n};\n\nexport const GRAMMAR_MAP: Record<Language, string> = {\n typescript: 'tree-sitter-typescript',\n tsx: 'tree-sitter-tsx',\n javascript: 'tree-sitter-javascript',\n jsx: 'tree-sitter-javascript',\n python: 'tree-sitter-python',\n go: 'tree-sitter-go',\n rust: 'tree-sitter-rust',\n java: 'tree-sitter-java',\n c: 'tree-sitter-c',\n cpp: 'tree-sitter-cpp',\n csharp: 'tree-sitter-c-sharp',\n php: 'tree-sitter-php',\n ruby: 'tree-sitter-ruby',\n swift: 'tree-sitter-swift',\n kotlin: 'tree-sitter-kotlin',\n dart: 'tree-sitter-dart',\n svelte: 'tree-sitter-svelte',\n elixir: 'tree-sitter-elixir',\n scala: 'tree-sitter-scala',\n lua: 'tree-sitter-lua',\n zig: 'tree-sitter-zig',\n bash: 'tree-sitter-bash',\n ocaml: 'tree-sitter-ocaml',\n elm: 'tree-sitter-elm',\n solidity: 'tree-sitter-solidity',\n vue: 'tree-sitter-vue',\n objc: 'tree-sitter-objc',\n yaml: 'tree-sitter-yaml',\n hcl: 'tree-sitter-hcl',\n css: 'tree-sitter-css',\n scss: 'tree-sitter-scss',\n html: 'tree-sitter-html',\n // Pascal and Liquid require custom WASM not bundled in tree-sitter-wasms\n pascal: '',\n liquid: '',\n // Jupyter notebooks are parsed via the notebook extractor (Python grammar on extracted code cells)\n jupyter: '',\n // ReScript: WASM available in tree-sitter-wasms\n rescript: 'tree-sitter-rescript',\n // Languages with WASMs TBD \u2014 files tracked, symbol extraction pending WASM compilation\n sql: 'tree-sitter-sql',\n r: 'tree-sitter-r',\n julia: 'tree-sitter-julia',\n powershell: 'tree-sitter-powershell',\n perl: 'tree-sitter-perl',\n astro: 'tree-sitter-astro',\n gdscript: 'tree-sitter-gdscript',\n nix: 'tree-sitter-nix',\n verilog: 'tree-sitter-verilog',\n haskell: 'tree-sitter-haskell',\n unknown: '',\n};\n\nexport function detectLanguage(filePath: string): Language {\n const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();\n return EXTENSION_MAP[ext] ?? 'unknown';\n}\n\nexport function isSupportedLanguage(lang: Language): boolean {\n // All known languages are \"supported\" for indexing purposes.\n // Pascal and Liquid don't have a grammar WASM, so they'll be indexed\n // with empty symbol lists (file tracked but no AST extraction).\n return lang !== 'unknown';\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,11 @@
1
+ id: code-eval-elixir
2
+ language: elixir
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: Code.eval_string() executes arbitrary Elixir code — code injection vector
6
+ fixHint: Remove Code.eval_string/eval_quoted; use pattern matching or a restricted DSL instead. Never evaluate user-controlled strings.
7
+ rule:
8
+ kind: call
9
+ has:
10
+ kind: dot
11
+ regex: '^Code\.eval_string'
@@ -0,0 +1,11 @@
1
+ id: command-injection-c
2
+ language: [c, cpp]
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: system() or popen() with a variable argument — command injection vector
6
+ fixHint: Avoid system()/popen(); use execv/execvp with an array of fixed arguments. Validate all inputs.
7
+ rule:
8
+ kind: call_expression
9
+ has:
10
+ kind: identifier
11
+ regex: '^(system|popen)$'
@@ -4,8 +4,26 @@ severity: critical
4
4
  owaspCategory: A03
5
5
  description: exec.Command() with string concatenation — command injection vector
6
6
  fixHint: Use exec.Command() with separate arguments (never shell=true). Validate all inputs.
7
- fix: "exec.Command($CMD, args...)"
8
7
  rule:
9
8
  any:
10
- - pattern: exec.Command($A + $B)
11
- - pattern: exec.Command(fmt.Sprintf($$$))
9
+ - kind: call_expression
10
+ all:
11
+ - has:
12
+ kind: selector_expression
13
+ regex: '^exec\.Command$'
14
+ - has:
15
+ kind: argument_list
16
+ has:
17
+ kind: binary_expression
18
+ - kind: call_expression
19
+ all:
20
+ - has:
21
+ kind: selector_expression
22
+ regex: '^exec\.Command$'
23
+ - has:
24
+ kind: argument_list
25
+ has:
26
+ kind: call_expression
27
+ has:
28
+ kind: selector_expression
29
+ regex: '^fmt\.Sprintf$'
@@ -0,0 +1,10 @@
1
+ id: command-injection-kotlin
2
+ language: kotlin
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: Runtime.exec() or ProcessBuilder — command injection vector if arguments are user-controlled
6
+ fixHint: Pass a fixed executable and a validated argument array. Never build command strings from user input.
7
+ rule:
8
+ any:
9
+ - pattern: Runtime.getRuntime().exec($CMD)
10
+ - pattern: ProcessBuilder($CMD).start()
@@ -0,0 +1,11 @@
1
+ id: command-injection-php
2
+ language: php
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: system()/exec()/shell_exec()/passthru() — command injection vector
6
+ fixHint: Use escapeshellarg() on every argument, or replace with a native PHP library that avoids shell invocation entirely.
7
+ rule:
8
+ kind: function_call_expression
9
+ has:
10
+ kind: name
11
+ regex: '^(system|exec|shell_exec|passthru|popen|proc_open)$'
@@ -0,0 +1,11 @@
1
+ id: command-injection-ruby
2
+ language: ruby
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: system()/exec()/spawn() with variable argument — command injection vector
6
+ fixHint: Pass an array of fixed arguments to system/exec to disable shell interpretation. Validate all user input.
7
+ rule:
8
+ kind: call
9
+ has:
10
+ kind: identifier
11
+ regex: '^(system|exec|spawn)$'
@@ -0,0 +1,11 @@
1
+ id: command-injection-rust
2
+ language: rust
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: std::process::Command built with a variable argument — command injection vector
6
+ fixHint: Pass a fixed executable path and validate each argument individually. Never construct commands from user input.
7
+ rule:
8
+ kind: call_expression
9
+ has:
10
+ kind: scoped_identifier
11
+ regex: 'Command::new'
@@ -0,0 +1,11 @@
1
+ id: dangerous-eval-bash
2
+ language: bash
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: eval command executes arbitrary shell code — command injection vector
6
+ fixHint: Remove eval; use explicit variable assignments and function calls. Never evaluate user-controlled strings.
7
+ rule:
8
+ kind: command
9
+ has:
10
+ kind: command_name
11
+ regex: '^eval$'
@@ -0,0 +1,11 @@
1
+ id: dangerous-eval-php
2
+ language: php
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: eval() executes arbitrary PHP code — code injection vector
6
+ fixHint: Remove eval(); parse data with json_decode() or use explicit logic. Never evaluate user-supplied strings.
7
+ rule:
8
+ kind: function_call_expression
9
+ has:
10
+ kind: name
11
+ regex: '^eval$'
@@ -0,0 +1,11 @@
1
+ id: dangerous-eval-ruby
2
+ language: ruby
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: eval() executes arbitrary Ruby code — code injection vector
6
+ fixHint: Remove eval(); use a safe parser (e.g. JSON.parse, Integer()) or explicit case/when logic instead.
7
+ rule:
8
+ kind: call
9
+ has:
10
+ kind: identifier
11
+ regex: '^eval$'
@@ -0,0 +1,10 @@
1
+ id: dynamic-load-lua
2
+ language: lua
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: load()/loadstring() executes arbitrary Lua code — code injection vector
6
+ fixHint: Remove dynamic code loading; use explicit functions or a restricted sandbox. Never evaluate user-controlled strings.
7
+ rule:
8
+ any:
9
+ - pattern: load($CODE)()
10
+ - pattern: loadstring($CODE)()
@@ -0,0 +1,16 @@
1
+ id: os-cmd-elixir
2
+ language: elixir
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: ':os.cmd() or System.cmd() with a variable argument — command injection vector'
6
+ fixHint: "Use System.cmd/2 with a fixed executable and a list of validated arguments. Never interpolate user input into shell strings."
7
+ rule:
8
+ any:
9
+ - kind: call
10
+ has:
11
+ kind: dot
12
+ regex: '^:os\.cmd'
13
+ - kind: call
14
+ has:
15
+ kind: dot
16
+ regex: '^System\.cmd'
@@ -0,0 +1,10 @@
1
+ id: os-execute-lua
2
+ language: lua
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: os.execute() or io.popen() with a variable argument — command injection vector
6
+ fixHint: Avoid shell execution; use Lua native I/O functions. If unavoidable, validate and escape all arguments.
7
+ rule:
8
+ any:
9
+ - pattern: os.execute($CMD)
10
+ - pattern: io.popen($CMD)
@@ -5,7 +5,7 @@ owaspCategory: A01
5
5
  description: os.Open() or ioutil.ReadFile() with user-controlled path — path traversal vector
6
6
  fixHint: Use filepath.Clean() and verify the result is within an allowed base directory.
7
7
  rule:
8
- any:
9
- - pattern: os.Open($PATH)
10
- - pattern: ioutil.ReadFile($PATH)
11
- - pattern: os.ReadFile($PATH)
8
+ kind: call_expression
9
+ has:
10
+ kind: selector_expression
11
+ regex: '^(os\.Open|ioutil\.ReadFile|os\.ReadFile)$'
@@ -0,0 +1,11 @@
1
+ id: process-launch-swift
2
+ language: swift
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: Process.launch() — command injection vector if launchPath or arguments are user-controlled
6
+ fixHint: Validate executableURL and all arguments against an allowlist before calling run(). Never set launchPath from user input.
7
+ rule:
8
+ kind: call_expression
9
+ has:
10
+ kind: navigation_expression
11
+ regex: '\.launch$'
@@ -0,0 +1,15 @@
1
+ id: process-run-dart
2
+ language: dart
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: Process.run() or Process.start() — command injection vector if arguments are user-controlled
6
+ fixHint: Pass a fixed executable and a validated list of arguments. Never build arguments from unsanitised user input.
7
+ rule:
8
+ kind: expression_statement
9
+ all:
10
+ - has:
11
+ kind: identifier
12
+ regex: '^Process$'
13
+ - has:
14
+ kind: selector
15
+ regex: '^\.run|^\.start'
@@ -0,0 +1,11 @@
1
+ id: source-injection-bash
2
+ language: bash
3
+ severity: high
4
+ owaspCategory: A03
5
+ description: source/. with a variable path — arbitrary script execution
6
+ fixHint: Hardcode the script path or validate it against an allowlist before sourcing.
7
+ rule:
8
+ kind: command
9
+ has:
10
+ kind: command_name
11
+ regex: '^(source|\.)$'
@@ -0,0 +1,9 @@
1
+ id: sql-injection-dart
2
+ language: dart
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: SQL query built by string concatenation — SQL injection vector
6
+ fixHint: Use parameterized queries with ? placeholder lists. Never concatenate user input into SQL strings.
7
+ rule:
8
+ kind: additive_expression
9
+ regex: '(SELECT|INSERT|UPDATE|DELETE|UNION)'
@@ -0,0 +1,11 @@
1
+ id: sql-injection-kotlin
2
+ language: kotlin
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: JDBC query built by string concatenation — SQL injection vector
6
+ fixHint: Use PreparedStatement with ? placeholders. Never concatenate user input into SQL strings.
7
+ rule:
8
+ any:
9
+ - pattern: µSTMT.executeQuery(µA + µB)
10
+ - pattern: µSTMT.execute(µA + µB)
11
+ - pattern: µSTMT.executeUpdate(µA + µB)
@@ -0,0 +1,9 @@
1
+ id: sql-injection-php
2
+ language: php
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: SQL query built by string concatenation (.) — SQL injection vector
6
+ fixHint: Use PDO prepared statements with ? placeholders and bound parameters. Never concatenate user input into SQL.
7
+ rule:
8
+ kind: binary_expression
9
+ regex: '(SELECT|INSERT|UPDATE|DELETE|UNION)'
@@ -0,0 +1,11 @@
1
+ id: sql-injection-swift
2
+ language: swift
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: SQL query built by string concatenation — SQL injection vector
6
+ fixHint: Use parameterized queries with ? placeholders. Never concatenate user input into SQL strings.
7
+ rule:
8
+ any:
9
+ - pattern: µDB.execute(µA + µB)
10
+ - pattern: µDB.query(µA + µB)
11
+ - pattern: µDB.run(µA + µB)
@@ -0,0 +1,8 @@
1
+ id: subshell-injection-ruby
2
+ language: ruby
3
+ severity: critical
4
+ owaspCategory: A03
5
+ description: Backtick or %x{} subshell execution — command injection vector
6
+ fixHint: Replace subshell calls with Open3.capture2e using an argument array. Never interpolate user input into shell strings.
7
+ rule:
8
+ kind: subshell
@@ -0,0 +1,8 @@
1
+ id: unsafe-block-rust
2
+ language: rust
3
+ severity: high
4
+ owaspCategory: A04
5
+ description: unsafe block — bypasses Rust memory-safety guarantees
6
+ fixHint: Minimise unsafe surface; wrap raw pointer operations in a safe abstraction and document invariants.
7
+ rule:
8
+ kind: unsafe_block
@@ -0,0 +1,11 @@
1
+ id: unsafe-string-c
2
+ language: [c, cpp]
3
+ severity: high
4
+ owaspCategory: A04
5
+ description: Unsafe C string function (strcpy/strcat/gets/sprintf) — buffer overflow vector
6
+ fixHint: Use strncpy/strncat/fgets/snprintf with explicit size limits. Prefer bounded alternatives everywhere.
7
+ rule:
8
+ kind: call_expression
9
+ has:
10
+ kind: identifier
11
+ regex: '^(strcpy|strcat|gets|sprintf|vsprintf)$'
@@ -33,43 +33,56 @@ __export(runner_exports, {
33
33
  module.exports = __toCommonJS(runner_exports);
34
34
  var import_types = require("./types");
35
35
  var import_errors = require("../errors");
36
- const NAPI_SUPPORTED_LANGUAGES = /* @__PURE__ */ new Set([
36
+ const NAPI_BUILTIN_LANGUAGES = /* @__PURE__ */ new Set([
37
+ "html",
37
38
  "javascript",
39
+ "jsx",
38
40
  "typescript",
39
41
  "tsx",
40
- "jsx",
41
- "python",
42
- "go",
43
- "rust",
44
- "java",
45
- "c",
46
- "cpp",
47
- "cs",
48
- "swift",
49
- "kotlin",
50
- "html",
51
- "css",
52
- "json",
53
- "yaml",
54
- "bash",
55
- "ruby",
56
- "php",
57
- "scala",
58
- "haskell",
59
- "lua",
60
- "r",
61
- "dart",
62
- "elixir",
63
- "erlang",
64
- "ocaml"
42
+ "css"
65
43
  ]);
66
44
  let _napi = void 0;
67
45
  let _napiWarnLogged = false;
46
+ let _napiRuntimeLangs = null;
47
+ const DYNAMIC_LANG_PACKAGES = {
48
+ go: "@ast-grep/lang-go",
49
+ python: "@ast-grep/lang-python",
50
+ java: "@ast-grep/lang-java",
51
+ rust: "@ast-grep/lang-rust",
52
+ c: "@ast-grep/lang-c",
53
+ cpp: "@ast-grep/lang-cpp",
54
+ cs: "@ast-grep/lang-csharp",
55
+ kotlin: "@ast-grep/lang-kotlin",
56
+ swift: "@ast-grep/lang-swift",
57
+ ruby: "@ast-grep/lang-ruby",
58
+ php: "@ast-grep/lang-php",
59
+ bash: "@ast-grep/lang-bash",
60
+ scala: "@ast-grep/lang-scala",
61
+ dart: "@ast-grep/lang-dart",
62
+ lua: "@ast-grep/lang-lua",
63
+ elixir: "@ast-grep/lang-elixir",
64
+ haskell: "@ast-grep/lang-haskell"
65
+ };
68
66
  async function getNapi() {
69
67
  if (_napi !== void 0) return _napi;
70
68
  try {
71
69
  require.resolve("@ast-grep/napi");
72
70
  _napi = require("@ast-grep/napi");
71
+ const registrations = {};
72
+ for (const [langName, pkgName] of Object.entries(DYNAMIC_LANG_PACKAGES)) {
73
+ try {
74
+ require.resolve(pkgName);
75
+ registrations[langName] = require(pkgName);
76
+ } catch {
77
+ }
78
+ }
79
+ if (Object.keys(registrations).length > 0 && typeof _napi.registerDynamicLanguage === "function") {
80
+ _napi.registerDynamicLanguage(registrations);
81
+ }
82
+ _napiRuntimeLangs = /* @__PURE__ */ new Set([
83
+ ...NAPI_BUILTIN_LANGUAGES,
84
+ ...Object.keys(registrations)
85
+ ]);
73
86
  return _napi;
74
87
  } catch {
75
88
  _napi = null;
@@ -108,7 +121,9 @@ class PatternRunner {
108
121
  * native C++ exception that bypasses JS try/catch and corrupts internal mutexes.
109
122
  */
110
123
  isSupportedLanguage(language) {
111
- return NAPI_SUPPORTED_LANGUAGES.has(language.toLowerCase());
124
+ const lang = language.toLowerCase();
125
+ if (_napiRuntimeLangs !== null) return _napiRuntimeLangs.has(lang);
126
+ return NAPI_BUILTIN_LANGUAGES.has(lang);
112
127
  }
113
128
  /**
114
129
  * Lazy async loader — returns the napi module or null if missing.
@@ -125,6 +140,7 @@ class PatternRunner {
125
140
  if (!this.isSupportedLanguage(language)) return [];
126
141
  const napi = await getNapi();
127
142
  if (!napi) return [];
143
+ if (!this.isSupportedLanguage(language)) return [];
128
144
  try {
129
145
  const { parse } = napi;
130
146
  const root = parse(language, fileContent);
@@ -163,6 +179,7 @@ class PatternRunner {
163
179
  if (!this.isSupportedLanguage(language)) return [];
164
180
  const napi = await getNapi();
165
181
  if (!napi) return [];
182
+ if (!this.isSupportedLanguage(language)) return [];
166
183
  try {
167
184
  const { parse } = napi;
168
185
  const root = parse(language, fileContent);
@@ -218,6 +235,7 @@ class PatternRunner {
218
235
  async applyFix(filePath, fileContent, language, rule) {
219
236
  const napi = await this.getNapi();
220
237
  if (!napi || !rule.fix) return null;
238
+ if (!this.isSupportedLanguage(language)) return null;
221
239
  try {
222
240
  const { parse } = napi;
223
241
  const root = parse(language, fileContent);