secure-review-extension 1.0.10 → 1.0.12
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.
- package/package.json +11 -1
- package/src/scanners/bootstrap-tools.js +111 -35
- package/src/scanners/repository-heuristics.js +437 -0
- package/src/scanners/rule-builders.js +66 -0
- package/src/scanners/scan-targets.js +147 -0
- package/src/scanners/static-rules/ansible.js +134 -0
- package/src/scanners/static-rules/common.js +41 -0
- package/src/scanners/static-rules/performance.js +30 -0
- package/src/scanners/static-rules/quality.js +132 -0
- package/src/scanners/static-rules/reliability.js +41 -0
- package/src/scanners/static-rules/security.js +588 -0
- package/src/scanners/static-rules/shell.js +114 -0
- package/src/scanners/static-rules.js +15 -779
- package/src/scanners/static-scan.js +24 -461
- package/src/scanners/tool-integrations/automation.js +125 -0
- package/src/scanners/tool-integrations/compiled.js +310 -0
- package/src/scanners/tool-integrations/ecosystem.js +156 -0
- package/src/scanners/tool-integrations/python.js +93 -0
- package/src/scanners/tool-integrations/shared.js +350 -0
- package/src/scanners/tool-integrations/web.js +146 -0
- package/src/scanners/tool-integrations.js +10 -910
- package/src/scanners/workspace-profile.js +14 -61
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "secure-review-extension",
|
|
3
3
|
"displayName": "Secure Review",
|
|
4
4
|
"description": "Run deep static and Docker-based dynamic secure code reviews directly inside VS Code.",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.12",
|
|
6
6
|
"publisher": "Ankit-QI",
|
|
7
7
|
"icon": "media/shield.png",
|
|
8
8
|
"license": "MIT",
|
|
@@ -272,6 +272,16 @@
|
|
|
272
272
|
"default": true,
|
|
273
273
|
"description": "If cppcheck is installed locally, include C and C++ static analysis findings."
|
|
274
274
|
},
|
|
275
|
+
"secureReview.enableShellcheck": {
|
|
276
|
+
"type": "boolean",
|
|
277
|
+
"default": true,
|
|
278
|
+
"description": "If ShellCheck is installed locally, include shell script lint and safety findings."
|
|
279
|
+
},
|
|
280
|
+
"secureReview.enableAnsibleLint": {
|
|
281
|
+
"type": "boolean",
|
|
282
|
+
"default": true,
|
|
283
|
+
"description": "If ansible-lint is installed locally, include Ansible playbook findings."
|
|
284
|
+
},
|
|
275
285
|
"secureReview.enableDotnetVuln": {
|
|
276
286
|
"type": "boolean",
|
|
277
287
|
"default": true,
|
|
@@ -2,67 +2,95 @@ const fs = require("node:fs");
|
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
const os = require("node:os");
|
|
4
4
|
const { execFileSync } = require("node:child_process");
|
|
5
|
+
const { SHELL_EXTENSIONS, looksLikeAnsibleContent, looksLikeAnsiblePath } = require("./scan-targets");
|
|
5
6
|
|
|
6
7
|
function detectWorkspace(workspaceRoot) {
|
|
7
8
|
const manifestNames = new Set(walkFiles(workspaceRoot, 4).map((file) => path.relative(workspaceRoot, file)));
|
|
8
9
|
const languages = new Set();
|
|
9
10
|
const frameworks = new Set();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
const packageJsonFiles = [...manifestNames].filter((file) => path.basename(file) === "package.json");
|
|
13
|
+
if (packageJsonFiles.length) {
|
|
12
14
|
languages.add("javascript");
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
for (const packageJsonPath of packageJsonFiles) {
|
|
16
|
+
const packageJson = readJson(path.join(workspaceRoot, packageJsonPath));
|
|
17
|
+
const dependencies = {
|
|
18
|
+
...(packageJson.dependencies || {}),
|
|
19
|
+
...(packageJson.devDependencies || {}),
|
|
20
|
+
...(packageJson.peerDependencies || {})
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (dependencies.react) frameworks.add("react");
|
|
24
|
+
if (dependencies.next) frameworks.add("nextjs");
|
|
25
|
+
if (dependencies.vue) frameworks.add("vue");
|
|
26
|
+
if (dependencies["@angular/core"]) frameworks.add("angular");
|
|
27
|
+
if (dependencies.express) frameworks.add("express");
|
|
28
|
+
if (dependencies["@nestjs/core"]) frameworks.add("nestjs");
|
|
29
|
+
if (dependencies.svelte) frameworks.add("svelte");
|
|
30
|
+
if (dependencies.koa) frameworks.add("koa");
|
|
31
|
+
if (dependencies.fastify) frameworks.add("fastify");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const pythonManifestFiles = [...manifestNames].filter((file) => {
|
|
36
|
+
const base = path.basename(file);
|
|
37
|
+
return base === "requirements.txt" || base === "pyproject.toml" || base === "Pipfile";
|
|
38
|
+
});
|
|
39
|
+
if (pythonManifestFiles.length) {
|
|
29
40
|
languages.add("python");
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
41
|
+
for (const manifestPath of pythonManifestFiles) {
|
|
42
|
+
const pythonText = readTextIfExists(manifestPath);
|
|
43
|
+
if (/django/i.test(pythonText)) frameworks.add("django");
|
|
44
|
+
if (/flask/i.test(pythonText)) frameworks.add("flask");
|
|
45
|
+
if (/fastapi/i.test(pythonText)) frameworks.add("fastapi");
|
|
46
|
+
}
|
|
34
47
|
}
|
|
35
48
|
|
|
36
|
-
|
|
49
|
+
const goModFiles = [...manifestNames].filter((file) => path.basename(file) === "go.mod");
|
|
50
|
+
if (goModFiles.length) {
|
|
37
51
|
languages.add("go");
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
for (const goModPath of goModFiles) {
|
|
53
|
+
const goMod = readTextIfExists(goModPath);
|
|
54
|
+
if (/gin-gonic\/gin/i.test(goMod)) frameworks.add("gin");
|
|
55
|
+
if (/labstack\/echo/i.test(goMod)) frameworks.add("echo");
|
|
56
|
+
if (/gofiber\/fiber/i.test(goMod)) frameworks.add("fiber");
|
|
57
|
+
}
|
|
41
58
|
}
|
|
42
59
|
|
|
43
|
-
|
|
60
|
+
const cargoTomlFiles = [...manifestNames].filter((file) => path.basename(file) === "Cargo.toml");
|
|
61
|
+
if (cargoTomlFiles.length) {
|
|
44
62
|
languages.add("rust");
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
63
|
+
for (const cargoTomlPath of cargoTomlFiles) {
|
|
64
|
+
const cargoToml = readTextIfExists(cargoTomlPath);
|
|
65
|
+
if (/actix-web/i.test(cargoToml)) frameworks.add("actix-web");
|
|
66
|
+
if (/\baxum\b/i.test(cargoToml)) frameworks.add("axum");
|
|
67
|
+
if (/rocket/i.test(cargoToml)) frameworks.add("rocket");
|
|
68
|
+
}
|
|
48
69
|
}
|
|
49
70
|
|
|
50
|
-
|
|
71
|
+
const javaManifestFiles = [...manifestNames].filter((file) => {
|
|
72
|
+
const base = path.basename(file);
|
|
73
|
+
return base === "pom.xml" || base === "build.gradle" || base === "build.gradle.kts";
|
|
74
|
+
});
|
|
75
|
+
if (javaManifestFiles.length) {
|
|
51
76
|
languages.add("java");
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
for (const manifestPath of javaManifestFiles) {
|
|
78
|
+
const javaText = readTextIfExists(manifestPath);
|
|
79
|
+
if (/spring/i.test(javaText)) frameworks.add("spring");
|
|
80
|
+
if (/quarkus/i.test(javaText)) frameworks.add("quarkus");
|
|
81
|
+
}
|
|
54
82
|
}
|
|
55
83
|
|
|
56
|
-
if (
|
|
84
|
+
if (manifestNames.has("CMakeLists.txt") || manifestNames.has("Makefile")) {
|
|
57
85
|
languages.add("c");
|
|
58
86
|
languages.add("cpp");
|
|
59
87
|
}
|
|
60
88
|
|
|
61
|
-
if (
|
|
89
|
+
if ([...manifestNames].some((file) => path.basename(file) === "composer.json")) {
|
|
62
90
|
languages.add("php");
|
|
63
91
|
}
|
|
64
92
|
|
|
65
|
-
if (
|
|
93
|
+
if ([...manifestNames].some((file) => path.basename(file) === "Gemfile")) {
|
|
66
94
|
languages.add("ruby");
|
|
67
95
|
frameworks.add("rails");
|
|
68
96
|
}
|
|
@@ -71,6 +99,10 @@ function detectWorkspace(workspaceRoot) {
|
|
|
71
99
|
languages.add("csharp");
|
|
72
100
|
}
|
|
73
101
|
|
|
102
|
+
if ([...manifestNames].some((file) => path.basename(file) === "ansible.cfg")) {
|
|
103
|
+
frameworks.add("ansible");
|
|
104
|
+
}
|
|
105
|
+
|
|
74
106
|
for (const file of manifestNames) {
|
|
75
107
|
const ext = path.extname(file).toLowerCase();
|
|
76
108
|
if ([".c", ".h"].includes(ext)) languages.add("c");
|
|
@@ -82,7 +114,9 @@ function detectWorkspace(workspaceRoot) {
|
|
|
82
114
|
if (ext === ".php") languages.add("php");
|
|
83
115
|
if (ext === ".rb") languages.add("ruby");
|
|
84
116
|
if (ext === ".cs") languages.add("csharp");
|
|
117
|
+
if (SHELL_EXTENSIONS.has(ext)) languages.add("shell");
|
|
85
118
|
if ([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"].includes(ext)) languages.add("javascript");
|
|
119
|
+
if ([".yml", ".yaml"].includes(ext) && looksLikeAnsibleFile(workspaceRoot, file)) frameworks.add("ansible");
|
|
86
120
|
}
|
|
87
121
|
|
|
88
122
|
return {
|
|
@@ -202,6 +236,24 @@ function buildInstallPlan(profile) {
|
|
|
202
236
|
});
|
|
203
237
|
}
|
|
204
238
|
|
|
239
|
+
if (hasAny(profile.languages, ["shell"])) {
|
|
240
|
+
addTool(plan, seen, {
|
|
241
|
+
tool: "ShellCheck",
|
|
242
|
+
requiredFor: ["Shell scripts"],
|
|
243
|
+
install: resolveShellcheckInstall(),
|
|
244
|
+
verify: ["shellcheck", "--version"]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (hasAny(profile.frameworks, ["ansible"])) {
|
|
249
|
+
addTool(plan, seen, {
|
|
250
|
+
tool: "ansible-lint",
|
|
251
|
+
requiredFor: ["Ansible / playbooks"],
|
|
252
|
+
install: resolvePythonInstall("ansible-lint"),
|
|
253
|
+
verify: ["ansible-lint", "--version"]
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
205
257
|
if (hasAny(profile.languages, ["csharp"])) {
|
|
206
258
|
addTool(plan, seen, {
|
|
207
259
|
tool: ".NET package audit",
|
|
@@ -294,6 +346,13 @@ function resolveCppcheckInstall() {
|
|
|
294
346
|
return { kind: "manual", note: "Install cppcheck with your system package manager." };
|
|
295
347
|
}
|
|
296
348
|
|
|
349
|
+
function resolveShellcheckInstall() {
|
|
350
|
+
if (hasCommand("shellcheck")) return { kind: "already-installed" };
|
|
351
|
+
if (os.platform() === "linux") return { kind: "command", command: ["sudo", "apt-get", "install", "-y", "shellcheck"] };
|
|
352
|
+
if (hasCommand("brew")) return { kind: "command", command: ["brew", "install", "shellcheck"] };
|
|
353
|
+
return { kind: "manual", note: "Install shellcheck with your system package manager." };
|
|
354
|
+
}
|
|
355
|
+
|
|
297
356
|
function resolveSpotBugsInstall() {
|
|
298
357
|
if (hasCommand("spotbugs")) return { kind: "already-installed" };
|
|
299
358
|
if (hasCommand("brew")) return { kind: "command", command: ["brew", "install", "spotbugs"] };
|
|
@@ -354,6 +413,23 @@ function readJson(filePath) {
|
|
|
354
413
|
}
|
|
355
414
|
}
|
|
356
415
|
|
|
416
|
+
function looksLikeAnsibleFile(workspaceRoot, relativePath) {
|
|
417
|
+
if (looksLikeAnsiblePath(relativePath)) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const fullPath = path.join(workspaceRoot, relativePath);
|
|
422
|
+
if (!fs.existsSync(fullPath)) {
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
try {
|
|
427
|
+
return looksLikeAnsibleContent(fs.readFileSync(fullPath, "utf8"));
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
357
433
|
module.exports = {
|
|
358
434
|
detectWorkspace,
|
|
359
435
|
buildInstallPlan,
|