secure-review-extension 1.0.7 → 1.0.10
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/extension.js +92 -11
- package/package.json +1 -1
- package/src/scanners/static-scan.js +33 -7
package/extension.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const vscode = require("vscode");
|
|
2
2
|
const { FindingsStore } = require("./src/store");
|
|
3
3
|
const { FindingsProvider } = require("./src/findings-provider");
|
|
4
|
-
const { scanWorkspace,
|
|
4
|
+
const { scanWorkspace, scanFileAtPath } = require("./src/scanners/static-scan");
|
|
5
5
|
const { runDockerZapScan } = require("./src/scanners/dynamic-scan");
|
|
6
6
|
const { detectWorkspace, buildInstallPlan, formatInstallPlan } = require("./src/scanners/bootstrap-tools");
|
|
7
7
|
const { applyDiagnostics } = require("./src/diagnostics");
|
|
@@ -11,6 +11,7 @@ const { severityOrder } = require("./src/constants");
|
|
|
11
11
|
|
|
12
12
|
let reportPanel;
|
|
13
13
|
let lastReportMetadata;
|
|
14
|
+
const saveScanTimers = new Map();
|
|
14
15
|
|
|
15
16
|
function activate(context) {
|
|
16
17
|
const store = new FindingsStore();
|
|
@@ -82,13 +83,7 @@ function activate(context) {
|
|
|
82
83
|
cancellable: false
|
|
83
84
|
},
|
|
84
85
|
async () => {
|
|
85
|
-
|
|
86
|
-
const otherStaticFindings = store.findings.filter(
|
|
87
|
-
(item) => item.source === "static" && item.filePath !== editor.document.uri.fsPath
|
|
88
|
-
);
|
|
89
|
-
const dynamicFindings = store.findings.filter((item) => item.source === "dynamic");
|
|
90
|
-
|
|
91
|
-
store.setFindings(sortFindings([...otherStaticFindings, ...currentFileFindings, ...dynamicFindings]));
|
|
86
|
+
await rescanFile(editor.document.uri.fsPath);
|
|
92
87
|
vscode.window.showInformationMessage(`Secure Review updated findings for ${editor.document.fileName}.`);
|
|
93
88
|
}
|
|
94
89
|
);
|
|
@@ -263,11 +258,97 @@ function activate(context) {
|
|
|
263
258
|
vscode.commands.registerCommand("secureReview.ignoreFinding", ignoreFinding)
|
|
264
259
|
);
|
|
265
260
|
|
|
261
|
+
async function rescanFile(filePath) {
|
|
262
|
+
const currentFileFindings = await scanFileAtPath(filePath, {
|
|
263
|
+
workspaceRoot: vscode.workspace.workspaceFolders?.[0]?.uri.fsPath,
|
|
264
|
+
excludeGlobs: configAccessor().get("excludeGlobs", []),
|
|
265
|
+
maxFiles: configAccessor().get("maxFiles", 400)
|
|
266
|
+
});
|
|
267
|
+
const otherStaticFindings = store.findings.filter(
|
|
268
|
+
(item) => item.source === "static" && item.filePath !== filePath
|
|
269
|
+
);
|
|
270
|
+
const dynamicFindings = store.findings.filter((item) => item.source === "dynamic");
|
|
271
|
+
store.setFindings(sortFindings([...otherStaticFindings, ...currentFileFindings, ...dynamicFindings]));
|
|
272
|
+
return currentFileFindings;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function handleRunOnSave(document) {
|
|
276
|
+
if (!document || document.uri.scheme !== "file") {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
|
|
281
|
+
if (!workspaceFolder) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const filePath = document.uri.fsPath;
|
|
286
|
+
const existingFindings = store.findings.filter(
|
|
287
|
+
(item) => item.source === "static" && item.filePath === filePath
|
|
288
|
+
);
|
|
289
|
+
const previousIds = new Set(existingFindings.map((item) => item.id));
|
|
290
|
+
|
|
291
|
+
const updatedFindings = await rescanFile(filePath);
|
|
292
|
+
const nextIds = new Set(updatedFindings.map((item) => item.id));
|
|
293
|
+
const newCount = updatedFindings.filter((item) => !previousIds.has(item.id)).length;
|
|
294
|
+
const resolvedCount = existingFindings.filter((item) => !nextIds.has(item.id)).length;
|
|
295
|
+
|
|
296
|
+
if (!updatedFindings.length && !resolvedCount) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const message = updatedFindings.length
|
|
301
|
+
? `Secure Review found ${updatedFindings.length} finding${updatedFindings.length === 1 ? "" : "s"} in ${document.fileName}${newCount || resolvedCount ? ` (${newCount} new, ${resolvedCount} resolved)` : ""}.`
|
|
302
|
+
: `Secure Review resolved all findings in ${document.fileName}.`;
|
|
303
|
+
|
|
304
|
+
const action = await vscode.window.showInformationMessage(
|
|
305
|
+
message,
|
|
306
|
+
"Open Findings",
|
|
307
|
+
"Open Report",
|
|
308
|
+
"Export DOCX"
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (action === "Open Findings") {
|
|
312
|
+
await vscode.commands.executeCommand("secureReview.findingsView.focus");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (action === "Open Report") {
|
|
317
|
+
openReport();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (action === "Export DOCX") {
|
|
322
|
+
await exportFindingsReport();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
266
326
|
context.subscriptions.push(
|
|
267
|
-
vscode.workspace.onDidSaveTextDocument(async () => {
|
|
268
|
-
if (configAccessor().get("runOnSave", false)) {
|
|
269
|
-
|
|
327
|
+
vscode.workspace.onDidSaveTextDocument(async (document) => {
|
|
328
|
+
if (!configAccessor().get("runOnSave", false)) {
|
|
329
|
+
return;
|
|
270
330
|
}
|
|
331
|
+
|
|
332
|
+
const filePath = document?.uri?.fsPath;
|
|
333
|
+
if (!filePath) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const existingTimer = saveScanTimers.get(filePath);
|
|
338
|
+
if (existingTimer) {
|
|
339
|
+
clearTimeout(existingTimer);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const timer = setTimeout(async () => {
|
|
343
|
+
saveScanTimers.delete(filePath);
|
|
344
|
+
try {
|
|
345
|
+
await handleRunOnSave(document);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
vscode.window.showErrorMessage(`Secure Review run-on-save scan failed: ${error.message}`);
|
|
348
|
+
}
|
|
349
|
+
}, 250);
|
|
350
|
+
|
|
351
|
+
saveScanTimers.set(filePath, timer);
|
|
271
352
|
})
|
|
272
353
|
);
|
|
273
354
|
|
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.10",
|
|
6
6
|
"publisher": "Ankit-QI",
|
|
7
7
|
"icon": "media/shield.png",
|
|
8
8
|
"license": "MIT",
|
|
@@ -135,19 +135,44 @@ async function scanCurrentFile() {
|
|
|
135
135
|
return [];
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
return scanFileAtPath(editor.document.uri.fsPath);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function scanFileAtPath(filePath, options = {}) {
|
|
142
|
+
const content = await readText(filePath);
|
|
139
143
|
if (content === null) {
|
|
140
144
|
return [];
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
const workspaceRoot =
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
const workspaceRoot = options.workspaceRoot
|
|
148
|
+
|| vscode?.workspace?.workspaceFolders?.[0]?.uri.fsPath
|
|
149
|
+
|| path.dirname(filePath);
|
|
150
|
+
const workspaceProfile = await buildWorkspaceProfileForRoot(workspaceRoot, {
|
|
151
|
+
excludeGlobs: options.excludeGlobs || [],
|
|
152
|
+
maxFiles: options.maxFiles || 400
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (!shouldAnalyzeSingleFile(filePath)) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return dedupeFindings(scanContent(filePath, content, workspaceRoot, {
|
|
160
|
+
languages: workspaceProfile.languages,
|
|
161
|
+
frameworks: workspaceProfile.frameworks,
|
|
162
|
+
isSecureReviewWorkspace: workspaceProfile.isSecureReviewWorkspace
|
|
148
163
|
}));
|
|
149
164
|
}
|
|
150
165
|
|
|
166
|
+
function shouldAnalyzeSingleFile(filePath) {
|
|
167
|
+
const baseName = path.basename(filePath);
|
|
168
|
+
if (LOCKFILE_NAMES.has(baseName) || ENV_LIKE_NAMES.has(baseName)) {
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
173
|
+
return ALLOWED_EXTENSIONS.has(ext);
|
|
174
|
+
}
|
|
175
|
+
|
|
151
176
|
function scanProfile(workspaceProfile, config) {
|
|
152
177
|
const findings = [];
|
|
153
178
|
const notify = typeof config.get === "function"
|
|
@@ -654,7 +679,8 @@ async function readText(filePath) {
|
|
|
654
679
|
module.exports = {
|
|
655
680
|
scanWorkspace,
|
|
656
681
|
scanWorkspaceAtPath,
|
|
657
|
-
scanCurrentFile
|
|
682
|
+
scanCurrentFile,
|
|
683
|
+
scanFileAtPath
|
|
658
684
|
};
|
|
659
685
|
|
|
660
686
|
function analyzeRepository(files, workspaceRoot) {
|