opensecurity 0.1.0 → 0.2.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 (42) hide show
  1. package/README.md +156 -30
  2. package/assets/grammars/README.md +9 -0
  3. package/assets/grammars/tree-sitter-c-sharp.wasm +0 -0
  4. package/assets/grammars/tree-sitter-c.wasm +0 -0
  5. package/assets/grammars/tree-sitter-cpp.wasm +0 -0
  6. package/assets/grammars/tree-sitter-go.wasm +0 -0
  7. package/assets/grammars/tree-sitter-java.wasm +0 -0
  8. package/assets/grammars/tree-sitter-kotlin.wasm +0 -0
  9. package/assets/grammars/tree-sitter-php.wasm +0 -0
  10. package/assets/grammars/tree-sitter-python.wasm +0 -0
  11. package/assets/grammars/tree-sitter-ruby.wasm +0 -0
  12. package/assets/grammars/tree-sitter-rust.wasm +0 -0
  13. package/assets/grammars/tree-sitter-swift.wasm +0 -0
  14. package/dist/adapters/bandit.js +41 -0
  15. package/dist/adapters/brakeman.js +41 -0
  16. package/dist/adapters/gosec.js +49 -0
  17. package/dist/adapters/languages.js +29 -0
  18. package/dist/adapters/runner.js +46 -0
  19. package/dist/adapters/semgrep.js +59 -0
  20. package/dist/adapters/types.js +1 -0
  21. package/dist/adapters/utils.js +52 -0
  22. package/dist/analysis/infraPatterns.js +196 -0
  23. package/dist/analysis/universalPatterns.js +56 -0
  24. package/dist/cli.js +15 -1
  25. package/dist/config.js +2 -1
  26. package/dist/native/languages.js +211 -0
  27. package/dist/native/loader.js +61 -0
  28. package/dist/native/rules.js +14 -0
  29. package/dist/native/taint.js +225 -0
  30. package/dist/scan.js +207 -0
  31. package/package.json +21 -2
  32. package/rules/taint/c.json +47 -0
  33. package/rules/taint/cpp.json +47 -0
  34. package/rules/taint/csharp.json +99 -0
  35. package/rules/taint/go.json +86 -0
  36. package/rules/taint/java.json +101 -0
  37. package/rules/taint/kotlin.json +86 -0
  38. package/rules/taint/php.json +100 -0
  39. package/rules/taint/python.json +108 -0
  40. package/rules/taint/ruby.json +101 -0
  41. package/rules/taint/rust.json +86 -0
  42. package/rules/taint/swift.json +86 -0
@@ -0,0 +1,196 @@
1
+ const RULES = [
2
+ {
3
+ id: "dockerfile-root-user",
4
+ title: "Container Runs as Root",
5
+ description: "Dockerfile runs as root user; consider using a non-root user.",
6
+ severity: "medium",
7
+ owasp: "A05:2021 Security Misconfiguration",
8
+ pattern: /\bUSER\s+root\b/i
9
+ },
10
+ {
11
+ id: "dockerfile-privileged",
12
+ title: "Privileged Container",
13
+ description: "Dockerfile enables privileged mode or sets all capabilities.",
14
+ severity: "high",
15
+ owasp: "A05:2021 Security Misconfiguration",
16
+ pattern: /\b(--privileged|CAP_SYS_ADMIN|cap_add\s*:\s*\[?\s*ALL\s*\]?)/i
17
+ },
18
+ {
19
+ id: "k8s-privileged",
20
+ title: "Privileged Kubernetes Pod",
21
+ description: "Kubernetes manifest enables privileged containers.",
22
+ severity: "high",
23
+ owasp: "A05:2021 Security Misconfiguration",
24
+ pattern: /\bprivileged\s*:\s*true\b/i
25
+ },
26
+ {
27
+ id: "k8s-host-path",
28
+ title: "HostPath Volume",
29
+ description: "Kubernetes HostPath volume may allow host access.",
30
+ severity: "medium",
31
+ owasp: "A05:2021 Security Misconfiguration",
32
+ pattern: /\bhostPath\s*:\b/i
33
+ },
34
+ {
35
+ id: "k8s-host-network",
36
+ title: "Host Network Enabled",
37
+ description: "Kubernetes manifest enables hostNetwork which reduces isolation.",
38
+ severity: "medium",
39
+ owasp: "A05:2021 Security Misconfiguration",
40
+ pattern: /\bhostNetwork\s*:\s*true\b/i
41
+ },
42
+ {
43
+ id: "k8s-host-ipc",
44
+ title: "Host IPC Enabled",
45
+ description: "Kubernetes manifest enables hostIPC which reduces isolation.",
46
+ severity: "medium",
47
+ owasp: "A05:2021 Security Misconfiguration",
48
+ pattern: /\bhostIPC\s*:\s*true\b/i
49
+ },
50
+ {
51
+ id: "k8s-host-pid",
52
+ title: "Host PID Namespace",
53
+ description: "Kubernetes manifest enables hostPID which reduces isolation.",
54
+ severity: "medium",
55
+ owasp: "A05:2021 Security Misconfiguration",
56
+ pattern: /\bhostPID\s*:\s*true\b/i
57
+ },
58
+ {
59
+ id: "k8s-allow-priv-esc",
60
+ title: "Privilege Escalation Enabled",
61
+ description: "Kubernetes manifest allows privilege escalation.",
62
+ severity: "high",
63
+ owasp: "A05:2021 Security Misconfiguration",
64
+ pattern: /\ballowPrivilegeEscalation\s*:\s*true\b/i
65
+ },
66
+ {
67
+ id: "k8s-no-readonly-fs",
68
+ title: "Writable Root Filesystem",
69
+ description: "Kubernetes manifest allows writable root filesystem.",
70
+ severity: "medium",
71
+ owasp: "A05:2021 Security Misconfiguration",
72
+ pattern: /\breadOnlyRootFilesystem\s*:\s*false\b/i
73
+ },
74
+ {
75
+ id: "k8s-run-as-root",
76
+ title: "Container Runs as Root",
77
+ description: "Kubernetes securityContext allows root user.",
78
+ severity: "medium",
79
+ owasp: "A05:2021 Security Misconfiguration",
80
+ pattern: /\brunAsNonRoot\s*:\s*false\b/i
81
+ },
82
+ {
83
+ id: "k8s-run-as-user-root",
84
+ title: "Container Runs as Root UID",
85
+ description: "Kubernetes manifest sets runAsUser to 0 (root).",
86
+ severity: "medium",
87
+ owasp: "A05:2021 Security Misconfiguration",
88
+ pattern: /\brunAsUser\s*:\s*0\b/i
89
+ },
90
+ {
91
+ id: "k8s-seccomp-unconfined",
92
+ title: "Seccomp Unconfined",
93
+ description: "Kubernetes manifest disables seccomp confinement.",
94
+ severity: "high",
95
+ owasp: "A05:2021 Security Misconfiguration",
96
+ pattern: /\bseccompProfile\b[\s\S]*?\btype\s*:\s*Unconfined\b/i
97
+ },
98
+ {
99
+ id: "terraform-public-sg",
100
+ title: "Public Security Group",
101
+ description: "Terraform security group allows ingress from all sources.",
102
+ severity: "high",
103
+ owasp: "A05:2021 Security Misconfiguration",
104
+ pattern: /\b(cidr_blocks|cidr_block)\s*=\s*\[?\s*\"0\.0\.0\.0\/0\"\s*\]?/i
105
+ },
106
+ {
107
+ id: "terraform-public-acl",
108
+ title: "Public Network ACL",
109
+ description: "Terraform network ACL allows ingress from all sources.",
110
+ severity: "high",
111
+ owasp: "A05:2021 Security Misconfiguration",
112
+ pattern: /\b(ingress|egress)\b[\s\S]*0\.0\.0\.0\/0/i
113
+ },
114
+ {
115
+ id: "terraform-open-sg-port",
116
+ title: "Open Security Group Port",
117
+ description: "Terraform security group allows public ingress on common ports.",
118
+ severity: "high",
119
+ owasp: "A05:2021 Security Misconfiguration",
120
+ pattern: /\b(from_port|to_port)\s*=\s*(22|3389|5432|3306|6379|9200|27017)\b[\s\S]*0\.0\.0\.0\/0/i
121
+ },
122
+ {
123
+ id: "terraform-public-s3-acl",
124
+ title: "Public S3 ACL",
125
+ description: "Terraform S3 ACL is public-read or public-read-write.",
126
+ severity: "high",
127
+ owasp: "A05:2021 Security Misconfiguration",
128
+ pattern: /\bacl\s*=\s*\"public-read(-write)?\"/i
129
+ },
130
+ {
131
+ id: "terraform-s3-public-block-disabled",
132
+ title: "S3 Public Access Block Disabled",
133
+ description: "Terraform disables S3 public access block.",
134
+ severity: "high",
135
+ owasp: "A05:2021 Security Misconfiguration",
136
+ pattern: /\b(block_public_acls|block_public_policy|ignore_public_acls|restrict_public_buckets)\s*=\s*false\b/i
137
+ },
138
+ {
139
+ id: "terraform-rds-public",
140
+ title: "Public RDS Instance",
141
+ description: "Terraform RDS instance is publicly accessible.",
142
+ severity: "high",
143
+ owasp: "A05:2021 Security Misconfiguration",
144
+ pattern: /\bpublicly_accessible\s*=\s*true\b/i
145
+ },
146
+ {
147
+ id: "yaml-insecure-tls",
148
+ title: "Insecure TLS",
149
+ description: "Config disables TLS verification.",
150
+ severity: "medium",
151
+ owasp: "A02:2021 Cryptographic Failures",
152
+ pattern: /\b(insecureSkipVerify|ssl_verify\s*:\s*false|verify_ssl\s*:\s*false)\b/i
153
+ }
154
+ ];
155
+ export function runInfraPatterns(content, filePath) {
156
+ const findings = [];
157
+ const lineStarts = [0];
158
+ for (let i = 0; i < content.length; i += 1) {
159
+ if (content[i] === "\n")
160
+ lineStarts.push(i + 1);
161
+ }
162
+ const findLine = (index) => {
163
+ let low = 0;
164
+ let high = lineStarts.length - 1;
165
+ while (low <= high) {
166
+ const mid = Math.floor((low + high) / 2);
167
+ if (lineStarts[mid] <= index) {
168
+ if (mid === lineStarts.length - 1 || lineStarts[mid + 1] > index)
169
+ return mid + 1;
170
+ low = mid + 1;
171
+ }
172
+ else {
173
+ high = mid - 1;
174
+ }
175
+ }
176
+ return 1;
177
+ };
178
+ for (const rule of RULES) {
179
+ const flags = rule.pattern.flags.includes("g") ? rule.pattern.flags : `${rule.pattern.flags}g`;
180
+ const regex = new RegExp(rule.pattern.source, flags);
181
+ let match;
182
+ while ((match = regex.exec(content)) !== null) {
183
+ const line = findLine(match.index);
184
+ findings.push({
185
+ id: rule.id,
186
+ severity: rule.severity,
187
+ owasp: rule.owasp,
188
+ title: rule.title,
189
+ description: rule.description,
190
+ file: filePath,
191
+ line
192
+ });
193
+ }
194
+ }
195
+ return findings;
196
+ }
@@ -0,0 +1,56 @@
1
+ const PATTERNS = [
2
+ {
3
+ id: "exec-os-system",
4
+ title: "Command Execution",
5
+ description: "Potential command execution via system call.",
6
+ severity: "critical",
7
+ owasp: "A03:2021 Injection",
8
+ pattern: /\b(os\.system|subprocess\.(call|run|Popen)|Runtime\.getRuntime\(\)\.exec|ProcessBuilder|exec\.Command|system\s*\(|popen\s*\(|Process\.Start|ShellExecute)\b/gi
9
+ },
10
+ {
11
+ id: "eval-exec",
12
+ title: "Dynamic Code Execution",
13
+ description: "Potential dynamic code execution detected.",
14
+ severity: "high",
15
+ owasp: "A03:2021 Injection",
16
+ pattern: /\b(eval|exec|Function|vm\.runInThisContext|loadstring)\s*\(/gi
17
+ },
18
+ {
19
+ id: "unsafe-deserialization",
20
+ title: "Unsafe Deserialization",
21
+ description: "Potential unsafe deserialization API usage.",
22
+ severity: "high",
23
+ owasp: "A08:2021 Software and Data Integrity Failures",
24
+ pattern: /\b(pickle\.loads?|yaml\.load|YAML\.load|ObjectInputStream|BinaryFormatter|XmlSerializer|Marshal\.load|PHP\s*unserialize|unserialize\s*\()\b/gi
25
+ },
26
+ {
27
+ id: "weak-crypto",
28
+ title: "Weak Crypto",
29
+ description: "Use of weak or deprecated crypto primitives.",
30
+ severity: "medium",
31
+ owasp: "A02:2021 Cryptographic Failures",
32
+ pattern: /\b(MD5|SHA1|RC4|DES|3DES|ECB)\b/gi
33
+ }
34
+ ];
35
+ export function runUniversalPatterns(code, filePath) {
36
+ const findings = [];
37
+ const lines = code.split(/\r?\n/);
38
+ for (let i = 0; i < lines.length; i += 1) {
39
+ const line = lines[i];
40
+ for (const rule of PATTERNS) {
41
+ if (rule.pattern.test(line)) {
42
+ findings.push({
43
+ id: rule.id,
44
+ severity: rule.severity,
45
+ owasp: rule.owasp,
46
+ title: rule.title,
47
+ description: rule.description,
48
+ file: filePath,
49
+ line: i + 1
50
+ });
51
+ }
52
+ rule.pattern.lastIndex = 0;
53
+ }
54
+ }
55
+ return findings;
56
+ }
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ const program = new Command();
13
13
  program
14
14
  .name("opensecurity")
15
15
  .description("openSecurity CLI")
16
- .version("0.1.0");
16
+ .version("0.2.0");
17
17
  program
18
18
  .command("login")
19
19
  .description("Store Codex Access Token in global config")
@@ -72,6 +72,14 @@ program
72
72
  .option("--ai-cache", "enable AI per-file cache")
73
73
  .option("--no-ai-cache", "disable AI per-file cache")
74
74
  .option("--ai-cache-path <path>", "path to AI cache file")
75
+ .option("--native-taint", "enable native multi-language taint engine")
76
+ .option("--no-native-taint", "disable native multi-language taint engine")
77
+ .option("--native-langs <list>", "comma-separated native taint languages", (value) => value.split(",").map((item) => item.trim()).filter(Boolean))
78
+ .option("--native-cache", "enable native taint cache")
79
+ .option("--no-native-cache", "disable native taint cache")
80
+ .option("--native-cache-path <path>", "path to native taint cache file")
81
+ .option("--adapters <list>", "comma-separated external adapters (bandit,gosec,brakeman,semgrep)", (value) => value.split(",").map((item) => item.trim()).filter(Boolean))
82
+ .option("--disable-adapters", "disable external static adapters")
75
83
  .option("--concurrency <n>", "parallel scan workers", (v) => Number(v))
76
84
  .option("--dependency-only", "only run dependency/CVE scanning")
77
85
  .option("--no-ai", "skip AI model scanning")
@@ -161,6 +169,12 @@ async function executeScan(opts) {
161
169
  aiBatchDepth: opts.aiBatchDepth,
162
170
  aiCache: opts.aiCache,
163
171
  aiCachePath: opts.aiCachePath,
172
+ nativeTaint: opts.nativeTaint,
173
+ nativeTaintLanguages: opts.nativeLangs,
174
+ nativeTaintCache: opts.nativeCache,
175
+ nativeTaintCachePath: opts.nativeCachePath,
176
+ adapters: opts.adapters,
177
+ noAdapters: opts.disableAdapters,
164
178
  diffOnly: opts.diffOnly,
165
179
  diffBase: opts.diffBase,
166
180
  concurrency: opts.concurrency
package/dist/config.js CHANGED
@@ -10,7 +10,8 @@ export const DEFAULT_EXCLUDE = [
10
10
  "**/coverage/**",
11
11
  "**/.opensecurity.json",
12
12
  "**/.opensecurity-cache.json",
13
- "**/.opensecurity/ai-cache.json"
13
+ "**/.opensecurity/ai-cache.json",
14
+ "**/.opensecurity/native-taint-cache.json"
14
15
  ];
15
16
  const DEFAULT_GLOBALS = {
16
17
  baseUrl: "https://api.openai.com/v1/responses",
@@ -0,0 +1,211 @@
1
+ const LANGUAGES = [
2
+ {
3
+ id: "python",
4
+ name: "Python",
5
+ extensions: [".py", ".pyw"],
6
+ wasmFile: "tree-sitter-python.wasm",
7
+ nativeModule: "tree-sitter-python",
8
+ callNodes: ["call"],
9
+ callCalleeFields: ["function"],
10
+ callArgumentFields: ["arguments"],
11
+ assignmentNodes: ["assignment"],
12
+ assignmentLeftFields: ["left"],
13
+ assignmentRightFields: ["right"],
14
+ memberNodes: ["attribute"],
15
+ memberObjectFields: ["object"],
16
+ memberPropertyFields: ["attribute"],
17
+ identifierNodes: ["identifier"],
18
+ stringNodes: ["string", "string_literal"]
19
+ },
20
+ {
21
+ id: "go",
22
+ name: "Go",
23
+ extensions: [".go"],
24
+ wasmFile: "tree-sitter-go.wasm",
25
+ nativeModule: "tree-sitter-go",
26
+ callNodes: ["call_expression"],
27
+ callCalleeFields: ["function"],
28
+ callArgumentFields: ["arguments"],
29
+ assignmentNodes: ["assignment_statement"],
30
+ assignmentLeftFields: ["left"],
31
+ assignmentRightFields: ["right"],
32
+ memberNodes: ["selector_expression"],
33
+ memberObjectFields: ["operand"],
34
+ memberPropertyFields: ["field"],
35
+ identifierNodes: ["identifier"],
36
+ stringNodes: ["interpreted_string_literal", "raw_string_literal"]
37
+ },
38
+ {
39
+ id: "java",
40
+ name: "Java",
41
+ extensions: [".java"],
42
+ wasmFile: "tree-sitter-java.wasm",
43
+ nativeModule: "tree-sitter-java",
44
+ callNodes: ["method_invocation"],
45
+ callCalleeFields: ["name", "object"],
46
+ callArgumentFields: ["arguments"],
47
+ assignmentNodes: ["assignment_expression"],
48
+ assignmentLeftFields: ["left"],
49
+ assignmentRightFields: ["right"],
50
+ memberNodes: ["field_access"],
51
+ memberObjectFields: ["object"],
52
+ memberPropertyFields: ["field"],
53
+ identifierNodes: ["identifier"],
54
+ stringNodes: ["string_literal"]
55
+ },
56
+ {
57
+ id: "csharp",
58
+ name: "C#",
59
+ extensions: [".cs"],
60
+ wasmFile: "tree-sitter-c-sharp.wasm",
61
+ nativeModule: "tree-sitter-c-sharp",
62
+ callNodes: ["invocation_expression"],
63
+ callCalleeFields: ["expression"],
64
+ callArgumentFields: ["argument_list"],
65
+ assignmentNodes: ["assignment_expression"],
66
+ assignmentLeftFields: ["left"],
67
+ assignmentRightFields: ["right"],
68
+ memberNodes: ["member_access_expression"],
69
+ memberObjectFields: ["expression"],
70
+ memberPropertyFields: ["name"],
71
+ identifierNodes: ["identifier"],
72
+ stringNodes: ["string_literal"]
73
+ },
74
+ {
75
+ id: "ruby",
76
+ name: "Ruby",
77
+ extensions: [".rb"],
78
+ wasmFile: "tree-sitter-ruby.wasm",
79
+ nativeModule: "tree-sitter-ruby",
80
+ callNodes: ["call", "command_call"],
81
+ callCalleeFields: ["method", "receiver"],
82
+ callArgumentFields: ["arguments"],
83
+ assignmentNodes: ["assignment"],
84
+ assignmentLeftFields: ["left"],
85
+ assignmentRightFields: ["right"],
86
+ memberNodes: ["call"],
87
+ memberObjectFields: ["receiver"],
88
+ memberPropertyFields: ["method"],
89
+ identifierNodes: ["identifier", "constant"],
90
+ stringNodes: ["string", "string_literal"]
91
+ },
92
+ {
93
+ id: "php",
94
+ name: "PHP",
95
+ extensions: [".php", ".phtml", ".php5", ".php7", ".phps"],
96
+ wasmFile: "tree-sitter-php.wasm",
97
+ nativeModule: "tree-sitter-php",
98
+ callNodes: ["function_call_expression", "member_call_expression", "scoped_call_expression"],
99
+ callCalleeFields: ["name", "function", "member", "scope"],
100
+ callArgumentFields: ["arguments"],
101
+ assignmentNodes: ["assignment_expression"],
102
+ assignmentLeftFields: ["left"],
103
+ assignmentRightFields: ["right"],
104
+ memberNodes: ["member_call_expression", "scoped_call_expression"],
105
+ memberObjectFields: ["object", "scope"],
106
+ memberPropertyFields: ["name", "member"],
107
+ identifierNodes: ["name", "variable_name", "identifier"],
108
+ stringNodes: ["string", "string_literal"]
109
+ },
110
+ {
111
+ id: "rust",
112
+ name: "Rust",
113
+ extensions: [".rs"],
114
+ wasmFile: "tree-sitter-rust.wasm",
115
+ nativeModule: "tree-sitter-rust",
116
+ callNodes: ["call_expression", "macro_invocation"],
117
+ callCalleeFields: ["function", "macro"],
118
+ callArgumentFields: ["arguments", "token_tree"],
119
+ assignmentNodes: ["assignment_expression", "let_declaration"],
120
+ assignmentLeftFields: ["left", "pattern"],
121
+ assignmentRightFields: ["right", "value"],
122
+ memberNodes: ["field_expression"],
123
+ memberObjectFields: ["value"],
124
+ memberPropertyFields: ["field"],
125
+ identifierNodes: ["identifier", "self"],
126
+ stringNodes: ["string_literal", "raw_string_literal"]
127
+ },
128
+ {
129
+ id: "kotlin",
130
+ name: "Kotlin",
131
+ extensions: [".kt", ".kts"],
132
+ wasmFile: "tree-sitter-kotlin.wasm",
133
+ nativeModule: "tree-sitter-kotlin",
134
+ callNodes: ["call_expression", "primary_expression"],
135
+ callCalleeFields: ["callee", "reference"],
136
+ callArgumentFields: ["value_arguments"],
137
+ assignmentNodes: ["assignment"],
138
+ assignmentLeftFields: ["left"],
139
+ assignmentRightFields: ["right"],
140
+ memberNodes: ["navigation_expression"],
141
+ memberObjectFields: ["receiver"],
142
+ memberPropertyFields: ["selector"],
143
+ identifierNodes: ["identifier"],
144
+ stringNodes: ["string_literal"]
145
+ },
146
+ {
147
+ id: "swift",
148
+ name: "Swift",
149
+ extensions: [".swift"],
150
+ wasmFile: "tree-sitter-swift.wasm",
151
+ nativeModule: "tree-sitter-swift",
152
+ callNodes: ["function_call_expression"],
153
+ callCalleeFields: ["function"],
154
+ callArgumentFields: ["argument_clause"],
155
+ assignmentNodes: ["assignment_expression"],
156
+ assignmentLeftFields: ["left"],
157
+ assignmentRightFields: ["right"],
158
+ memberNodes: ["member_access_expression"],
159
+ memberObjectFields: ["object"],
160
+ memberPropertyFields: ["name"],
161
+ identifierNodes: ["identifier"],
162
+ stringNodes: ["string_literal"]
163
+ },
164
+ {
165
+ id: "c",
166
+ name: "C",
167
+ extensions: [".c", ".h"],
168
+ wasmFile: "tree-sitter-c.wasm",
169
+ nativeModule: "tree-sitter-c",
170
+ callNodes: ["call_expression"],
171
+ callCalleeFields: ["function"],
172
+ callArgumentFields: ["arguments"],
173
+ assignmentNodes: ["assignment_expression", "init_declarator"],
174
+ assignmentLeftFields: ["left", "declarator"],
175
+ assignmentRightFields: ["right", "value"],
176
+ memberNodes: ["field_expression"],
177
+ memberObjectFields: ["argument"],
178
+ memberPropertyFields: ["field"],
179
+ identifierNodes: ["identifier"],
180
+ stringNodes: ["string_literal"]
181
+ },
182
+ {
183
+ id: "cpp",
184
+ name: "C++",
185
+ extensions: [".cpp", ".hpp", ".cc", ".cxx", ".hh", ".hxx"],
186
+ wasmFile: "tree-sitter-cpp.wasm",
187
+ nativeModule: "tree-sitter-cpp",
188
+ callNodes: ["call_expression"],
189
+ callCalleeFields: ["function"],
190
+ callArgumentFields: ["arguments"],
191
+ assignmentNodes: ["assignment_expression", "init_declarator"],
192
+ assignmentLeftFields: ["left", "declarator"],
193
+ assignmentRightFields: ["right", "value"],
194
+ memberNodes: ["field_expression", "qualified_identifier"],
195
+ memberObjectFields: ["scope", "argument"],
196
+ memberPropertyFields: ["name", "field"],
197
+ identifierNodes: ["identifier"],
198
+ stringNodes: ["string_literal"]
199
+ }
200
+ ];
201
+ export function getNativeLanguages() {
202
+ return [...LANGUAGES];
203
+ }
204
+ export function getLanguageByExtension(filePath) {
205
+ const lower = filePath.toLowerCase();
206
+ for (const lang of LANGUAGES) {
207
+ if (lang.extensions.some((ext) => lower.endsWith(ext)))
208
+ return lang;
209
+ }
210
+ return null;
211
+ }
@@ -0,0 +1,61 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ let treeSitter = null;
5
+ let treeSitterInit = null;
6
+ async function ensureTreeSitter() {
7
+ if (treeSitter)
8
+ return treeSitter;
9
+ const mod = await import("web-tree-sitter");
10
+ const resolved = {
11
+ init: mod.init?.bind(mod),
12
+ Language: mod.Language,
13
+ Parser: mod.Parser
14
+ };
15
+ treeSitter = resolved;
16
+ if (!treeSitterInit) {
17
+ treeSitterInit = resolved.init();
18
+ }
19
+ await treeSitterInit;
20
+ return resolved;
21
+ }
22
+ async function loadNativeLanguage(lang) {
23
+ try {
24
+ const require = createRequire(import.meta.url);
25
+ const Parser = require("tree-sitter");
26
+ const Lang = require(lang.nativeModule);
27
+ const parser = new Parser();
28
+ parser.setLanguage(Lang);
29
+ return { parser, type: "native" };
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ async function loadWasmLanguage(lang, baseDir) {
36
+ const mod = await ensureTreeSitter();
37
+ const wasmPath = path.join(baseDir, "assets", "grammars", lang.wasmFile);
38
+ try {
39
+ await fs.access(wasmPath);
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ const language = await mod.Language.load(wasmPath);
45
+ const parser = new mod.Parser();
46
+ parser.setLanguage(language);
47
+ return { parser, type: "wasm" };
48
+ }
49
+ export async function parseWithTreeSitter(source, lang, baseDir) {
50
+ const native = await loadNativeLanguage(lang);
51
+ if (native) {
52
+ const tree = native.parser.parse(source);
53
+ return { tree, source, language: lang };
54
+ }
55
+ const wasm = await loadWasmLanguage(lang, baseDir);
56
+ if (wasm) {
57
+ const tree = wasm.parser.parse(source);
58
+ return { tree, source, language: lang };
59
+ }
60
+ return null;
61
+ }
@@ -0,0 +1,14 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function loadNativeRules(baseDir, lang) {
4
+ const rulePath = path.join(baseDir, "rules", "taint", `${lang}.json`);
5
+ try {
6
+ const raw = await fs.readFile(rulePath, "utf8");
7
+ return JSON.parse(raw);
8
+ }
9
+ catch (err) {
10
+ if (err?.code === "ENOENT")
11
+ return null;
12
+ throw err;
13
+ }
14
+ }