Skip to content

Commit 62e1451

Browse files
authored
Merge pull request #18 from RefactorSecurity/add-sast-tools
Add sast tools
2 parents 630b81e + e19ea44 commit 62e1451

File tree

10 files changed

+210
-19
lines changed

10 files changed

+210
-19
lines changed

README.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
Security Notes allows the creation of notes within source files, which can be replied to, reacted to using emojis, and assigned statuses such as "TODO", "Vulnerable" and "Not Vulnerable".
1616

17-
Also, it allows importing the output from SAST tools (currently only [Semgrep](https://semgrep.dev/)) into notes, making the processing of the findings much easier.
17+
Also, it allows importing the output from SAST tools (such as semgrep, bandit and brakeman), into notes, making the processing of the findings much easier.
1818

1919
Finally, collaborate with others by using a centralized database for notes that will be automatically synced in **real-time**! Create a note locally, and it will be automatically pushed to whoever is working with you on the project.
2020

@@ -64,13 +64,36 @@ Naturally, you will want to collaborate with remote peers. To do so in a secure
6464
6565
## Importing SAST results
6666

67-
The extension allows you to import the output from SAST tools (currently only [Semgrep](https://semgrep.dev/)) into notes, making the processing of the findings much easier:
67+
The extension allows you to import the output from SAST tools into notes, making the processing of the findings much easier:
6868

6969
![Demo for semgrep import](images/demo-semgrep-import.gif)
7070

71+
Currently supported tools include:
72+
73+
- bandit (https://bandit.readthedocs.io/en/latest/)
74+
- brakeman (https://brakemanscanner.org/)
75+
- checkov (https://www.checkov.io/)
76+
- gosec (https://github.com/securego/gosec)
77+
- semgrep (https://semgrep.dev/)
78+
79+
For imports to be successful, we recommend running commands as follows (exporting results as JSON), and making sure to run these tools from the project's folder (so that all relative paths can be processed correctly):
80+
81+
```bash
82+
# bandit
83+
bandit -f json -o bandit-results.json -r .
84+
# brakeman
85+
brakeman -f json -o brakeman-results.json .
86+
# checkov
87+
checkov -d . -o json --output-file-path checkov-results.json
88+
# gosec
89+
gosec -fmt=json -out=gosec-results.json ./...
90+
# semgrep
91+
semgrep scan --json -o semgrep-results.json --config=auto .
92+
```
93+
7194
## Extension Settings
7295

73-
Various settings for the extension can be configured in VSCode's User Settings page (`CMD+Shift+P` / `Ctrl + Shift + P` -> *Preferences: Open Settings (UI)*):
96+
Various settings for the extension can be configured in VSCode's User Settings page (`CMD+Shift+P` / `Ctrl + Shift + P` -> _Preferences: Open Settings (UI)_):
7497

7598
![Extension Settings](images/settings.png)
7699

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Security Notes",
44
"description": "Create notes during a security code review. Import your favorite SAST tool results and collaborate with others.",
55
"icon": "resources/security_notes_logo.png",
6-
"version": "1.1.1",
6+
"version": "1.2.0",
77
"publisher": "refactor-security",
88
"private": false,
99
"license": "MIT",

src/parsers/bandit.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
import * as vscode from 'vscode';
4+
import { ToolFinding } from '../models/toolFinding';
5+
import { relativePathToFull } from '../utils';
6+
7+
class BanditParser {
8+
static parse(fileContent: string) {
9+
const toolFindings: ToolFinding[] = [];
10+
11+
try {
12+
const banditFindings = JSON.parse(fileContent).results;
13+
banditFindings.map((banditFinding: any) => {
14+
// uri
15+
const uri = vscode.Uri.file(relativePathToFull(banditFinding.filename));
16+
17+
// range
18+
const lineRange = banditFinding.line_range;
19+
const range = new vscode.Range(
20+
lineRange[0] - 1,
21+
0,
22+
(lineRange[1] ? lineRange[1] : lineRange[0]) - 1,
23+
0,
24+
);
25+
26+
// instantiate tool finding and add to list
27+
const toolFinding = new ToolFinding(uri, range, banditFinding.issue_text);
28+
toolFindings.push(toolFinding);
29+
});
30+
} catch {
31+
/* empty */
32+
}
33+
34+
return toolFindings;
35+
}
36+
}
37+
38+
export { BanditParser };

src/parsers/brakeman.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use strict';
2+
3+
import * as vscode from 'vscode';
4+
import { ToolFinding } from '../models/toolFinding';
5+
import { relativePathToFull } from '../utils';
6+
7+
class BrakemanParser {
8+
static parse(fileContent: string) {
9+
const toolFindings: ToolFinding[] = [];
10+
11+
try {
12+
const brakemanFindings = JSON.parse(fileContent).warnings;
13+
brakemanFindings.map((brakemanFinding: any) => {
14+
// uri
15+
const uri = vscode.Uri.file(relativePathToFull(brakemanFinding.file));
16+
17+
// range
18+
const range = new vscode.Range(
19+
brakemanFinding.line - 1,
20+
0,
21+
brakemanFinding.line - 1,
22+
0,
23+
);
24+
25+
// instantiate tool finding and add to list
26+
const toolFinding = new ToolFinding(
27+
uri,
28+
range,
29+
`${brakemanFinding.warning_type}: ${brakemanFinding.message}`,
30+
);
31+
toolFindings.push(toolFinding);
32+
});
33+
} catch {
34+
/* empty */
35+
}
36+
37+
return toolFindings;
38+
}
39+
}
40+
41+
export { BrakemanParser };

src/parsers/checkov.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
import * as vscode from 'vscode';
4+
import { ToolFinding } from '../models/toolFinding';
5+
import { relativePathToFull } from '../utils';
6+
7+
class CheckovParser {
8+
static parse(fileContent: string) {
9+
const toolFindings: ToolFinding[] = [];
10+
11+
try {
12+
const checkovCheckTypes = JSON.parse(fileContent);
13+
checkovCheckTypes.map((checkovCheckType: any) => {
14+
const checkovFindings = checkovCheckType.results.failed_checks;
15+
checkovFindings.map((checkovFinding: any) => {
16+
// uri
17+
const uri = vscode.Uri.file(relativePathToFull(checkovFinding.file_path));
18+
19+
// range
20+
const range = new vscode.Range(
21+
checkovFinding.file_line_range[0] - 1,
22+
0,
23+
checkovFinding.file_line_range[1] - 1,
24+
0,
25+
);
26+
27+
// instantiate tool finding and add to list
28+
const toolFinding = new ToolFinding(uri, range, checkovFinding.check_name);
29+
toolFindings.push(toolFinding);
30+
});
31+
});
32+
} catch {
33+
/* empty */
34+
}
35+
36+
return toolFindings;
37+
}
38+
}
39+
40+
export { CheckovParser };

src/parsers/gosec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
'use strict';
2+
3+
import * as vscode from 'vscode';
4+
import { ToolFinding } from '../models/toolFinding';
5+
6+
class GosecParser {
7+
static parse(fileContent: string) {
8+
const toolFindings: ToolFinding[] = [];
9+
10+
try {
11+
const gosecFindings = JSON.parse(fileContent).Issues;
12+
gosecFindings.map((gosecFinding: any) => {
13+
// uri
14+
const uri = vscode.Uri.file(gosecFinding.file);
15+
16+
// range
17+
const line = gosecFinding.line;
18+
const range = new vscode.Range(line - 1, 0, line - 1, 0);
19+
20+
// instantiate tool finding and add to list
21+
const toolFinding = new ToolFinding(uri, range, gosecFinding.details);
22+
toolFindings.push(toolFinding);
23+
});
24+
} catch {
25+
/* empty */
26+
}
27+
28+
return toolFindings;
29+
}
30+
}
31+
32+
export { GosecParser };

src/parsers/semgrep.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import * as vscode from 'vscode';
44
import { ToolFinding } from '../models/toolFinding';
5+
import { relativePathToFull } from '../utils';
56

67
class SemgrepParser {
78
static parse(fileContent: string) {
@@ -11,11 +12,7 @@ class SemgrepParser {
1112
const semgrepFindings = JSON.parse(fileContent).results;
1213
semgrepFindings.map((semgrepFinding: any) => {
1314
// uri
14-
let fullPath = '';
15-
if (vscode.workspace.workspaceFolders) {
16-
fullPath = vscode.workspace.workspaceFolders[0].uri.fsPath + '/';
17-
}
18-
const uri = vscode.Uri.file(`${fullPath}${semgrepFinding.path}`);
15+
const uri = vscode.Uri.file(relativePathToFull(semgrepFinding.path));
1916

2017
// range
2118
const range = new vscode.Range(

src/webviews/assets/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
function onButtonClicked() {
1313
let toolSelect = document.getElementById('toolSelect');
14-
let toolName = toolSelect.options[toolSelect.selectedIndex].text;
14+
let toolName = toolSelect.options[toolSelect.selectedIndex].value;
1515

1616
let selectedFile = document.getElementById('fileInput').files[0];
1717
readFile(selectedFile).then((fileContent) => {

src/webviews/importToolResultsWebview.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
import * as vscode from 'vscode';
44
import { commentController } from '../controllers/comments';
5+
import { BanditParser } from '../parsers/bandit';
6+
import { BrakemanParser } from '../parsers/brakeman';
7+
import { CheckovParser } from '../parsers/checkov';
8+
import { GosecParser } from '../parsers/gosec';
59
import { SemgrepParser } from '../parsers/semgrep';
610
import { ToolFinding } from '../models/toolFinding';
711
import { saveNoteComment } from '../helpers';
@@ -42,12 +46,7 @@ export class ImportToolResultsWebview implements vscode.WebviewViewProvider {
4246
webviewView.webview.onDidReceiveMessage((data) => {
4347
switch (data.type) {
4448
case 'processToolFile': {
45-
processToolFile(
46-
data.toolName,
47-
data.fileContent,
48-
this.noteMap,
49-
this.remoteDb,
50-
);
49+
processToolFile(data.toolName, data.fileContent, this.noteMap, this.remoteDb);
5150
}
5251
}
5352
});
@@ -85,7 +84,11 @@ export class ImportToolResultsWebview implements vscode.WebviewViewProvider {
8584
<p>Select tool:</p>
8685
<p>
8786
<select id="toolSelect">
88-
<option value="semgrep">semgrep</option>
87+
<option value="bandit">bandit (JSON)</option>
88+
<option value="brakeman">brakeman (JSON)</option>
89+
<option value="checkov">checkov (JSON)</option>
90+
<option value="gosec">gosec (JSON)</option>
91+
<option value="semgrep">semgrep (JSON)</option>
8992
</select>
9093
</p>
9194
<p>Select file:</p>
@@ -111,8 +114,25 @@ function processToolFile(
111114

112115
// parse tool findings
113116
switch (toolName) {
117+
case 'bandit': {
118+
toolFindings = BanditParser.parse(fileContent);
119+
break;
120+
}
121+
case 'brakeman': {
122+
toolFindings = BrakemanParser.parse(fileContent);
123+
break;
124+
}
125+
case 'checkov': {
126+
toolFindings = CheckovParser.parse(fileContent);
127+
break;
128+
}
129+
case 'gosec': {
130+
toolFindings = GosecParser.parse(fileContent);
131+
break;
132+
}
114133
case 'semgrep': {
115134
toolFindings = SemgrepParser.parse(fileContent);
135+
break;
116136
}
117137
}
118138

0 commit comments

Comments
 (0)