@codacy/tools-bandit-1 0.1.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.
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +1041 -0
- package/package.json +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Codacy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @codacy/tools-bandit-1
|
|
2
|
+
|
|
3
|
+
## Table of Contents
|
|
4
|
+
- [Overview](#overview)
|
|
5
|
+
- [Updating patterns](#updating-patterns)
|
|
6
|
+
- [Updating the Bandit version](#updating-the-bandit-version)
|
|
7
|
+
- [Development](#development)
|
|
8
|
+
- [Notes for maintainers](#notes-for-maintainers)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
Python security linter using the [Bandit](https://bandit.readthedocs.io/) binary. Uses the CLI execution strategy -- spawns `bandit` from a managed Python venv via `spawnTool()` and parses its JSON output.
|
|
15
|
+
|
|
16
|
+
| Property | Value |
|
|
17
|
+
|----------|-------|
|
|
18
|
+
| Tool ID | `Bandit` |
|
|
19
|
+
| Codacy UUID | `89b6fc6a-9137-4cf3-8b9f-dcc3bedfa738` |
|
|
20
|
+
| Strategy | CLI |
|
|
21
|
+
| Languages | Python |
|
|
22
|
+
| Binary | `bandit` (pip-installed in venv) |
|
|
23
|
+
| File patterns | `**/*.py` |
|
|
24
|
+
|
|
25
|
+
## Updating patterns
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Re-fetch pattern metadata from the Codacy API
|
|
29
|
+
pnpm prefetch
|
|
30
|
+
|
|
31
|
+
# Commit the result
|
|
32
|
+
git add src/patterns.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Pattern IDs follow the format `Bandit_B602`, `Bandit_B301`, etc.
|
|
36
|
+
|
|
37
|
+
## Updating the Bandit version
|
|
38
|
+
|
|
39
|
+
1. Update `preferredVersion` in `src/adapter.ts`
|
|
40
|
+
2. Update the `PIP_PACKAGES` array with the new pinned version
|
|
41
|
+
3. Run `pnpm prefetch` to check for new/removed rules
|
|
42
|
+
4. Run `pnpm test` to verify compatibility
|
|
43
|
+
5. If the major version changes, create a new adapter package (`bandit-2/`)
|
|
44
|
+
|
|
45
|
+
## Development
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pnpm build # Build with tsup
|
|
49
|
+
pnpm test # Run tests (requires bandit in PATH or venv)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
To install bandit locally for testing:
|
|
53
|
+
```bash
|
|
54
|
+
pip install bandit==1.8.3
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Notes for maintainers
|
|
58
|
+
|
|
59
|
+
- Bandit requires Python 3 as a runtime. The adapter uses an isolated venv at `~/.codacy/runtimes/bandit-1/venv/`.
|
|
60
|
+
- Exit code 0 = no findings, 1 = findings found (normal), >1 = error.
|
|
61
|
+
- `col_offset` in bandit output is 0-based; the adapter adds 1 for the 1-based Issue model.
|
|
62
|
+
- Bandit's `issue_confidence` (LOW/MEDIUM/HIGH) maps to numeric confidence (1/2/3) on the Issue.
|
|
63
|
+
- CWE information from `issue_cwe` is appended to the issue message as `(CWE-{id})`.
|
|
64
|
+
- Config file detection supports `.bandit` (INI, passed with `--ini`), `bandit.yml` (YAML, passed with `-c`), and `pyproject.toml` (TOML, passed with `-c`).
|
|
65
|
+
- Pattern filtering uses the `-t` flag to pass comma-separated test IDs to bandit.
|
|
66
|
+
- The `errors` array in bandit's JSON output (file-level errors like syntax errors) is mapped to `AnalysisError` objects with level "warning".
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,1041 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
default: () => index_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
var import_tooling2 = require("@codacy/tooling");
|
|
37
|
+
|
|
38
|
+
// src/adapter.ts
|
|
39
|
+
var import_tooling = require("@codacy/tooling");
|
|
40
|
+
var import_path = __toESM(require("path"));
|
|
41
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
42
|
+
|
|
43
|
+
// src/patterns.json
|
|
44
|
+
var patterns_default = [
|
|
45
|
+
{
|
|
46
|
+
id: "Bandit_B101",
|
|
47
|
+
category: "Security",
|
|
48
|
+
severityLevel: "High",
|
|
49
|
+
title: "Avoid Use of Assert Statements",
|
|
50
|
+
enabled: false,
|
|
51
|
+
parameters: []
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "Bandit_B102",
|
|
55
|
+
category: "Security",
|
|
56
|
+
severityLevel: "Warning",
|
|
57
|
+
title: "Avoid use of exec",
|
|
58
|
+
enabled: true,
|
|
59
|
+
parameters: []
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "Bandit_B103",
|
|
63
|
+
category: "Security",
|
|
64
|
+
severityLevel: "High",
|
|
65
|
+
title: "Detect Setting Permissive File Permissions",
|
|
66
|
+
enabled: true,
|
|
67
|
+
parameters: []
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: "Bandit_B104",
|
|
71
|
+
category: "Security",
|
|
72
|
+
severityLevel: "Warning",
|
|
73
|
+
title: "Avoid binding to all network interfaces",
|
|
74
|
+
enabled: true,
|
|
75
|
+
parameters: []
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "Bandit_B105",
|
|
79
|
+
category: "Security",
|
|
80
|
+
severityLevel: "High",
|
|
81
|
+
title: "Detect Hardcoded Password Strings",
|
|
82
|
+
enabled: true,
|
|
83
|
+
parameters: []
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "Bandit_B106",
|
|
87
|
+
category: "Security",
|
|
88
|
+
severityLevel: "High",
|
|
89
|
+
title: "Detect Hardcoded Password Function Arguments",
|
|
90
|
+
enabled: false,
|
|
91
|
+
parameters: []
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: "Bandit_B107",
|
|
95
|
+
category: "Security",
|
|
96
|
+
severityLevel: "High",
|
|
97
|
+
title: "Avoid Hardcoded Password Argument Defaults",
|
|
98
|
+
enabled: true,
|
|
99
|
+
parameters: []
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "Bandit_B108",
|
|
103
|
+
category: "Security",
|
|
104
|
+
severityLevel: "Warning",
|
|
105
|
+
title: "Avoid Hardcoded Temporary Directory Usage",
|
|
106
|
+
enabled: true,
|
|
107
|
+
parameters: []
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "Bandit_B109",
|
|
111
|
+
category: "Security",
|
|
112
|
+
severityLevel: "Warning",
|
|
113
|
+
title: "Detect Password Based Config Options Not Marked Secret",
|
|
114
|
+
enabled: true,
|
|
115
|
+
parameters: []
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: "Bandit_B110",
|
|
119
|
+
category: "Security",
|
|
120
|
+
severityLevel: "Warning",
|
|
121
|
+
title: "Avoid pass in except block",
|
|
122
|
+
enabled: true,
|
|
123
|
+
parameters: []
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "Bandit_B111",
|
|
127
|
+
category: "Security",
|
|
128
|
+
severityLevel: "Warning",
|
|
129
|
+
title: "Detect use of rootwrap running as root",
|
|
130
|
+
enabled: true,
|
|
131
|
+
parameters: []
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "Bandit_B112",
|
|
135
|
+
category: "Security",
|
|
136
|
+
severityLevel: "High",
|
|
137
|
+
title: "Avoid continue in except block",
|
|
138
|
+
enabled: true,
|
|
139
|
+
parameters: []
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "Bandit_B113",
|
|
143
|
+
category: "Security",
|
|
144
|
+
severityLevel: "Warning",
|
|
145
|
+
title: "Enforce timeout parameter in requests calls",
|
|
146
|
+
enabled: true,
|
|
147
|
+
parameters: []
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "Bandit_B201",
|
|
151
|
+
category: "Security",
|
|
152
|
+
severityLevel: "High",
|
|
153
|
+
title: "Avoid Running Flask App with Debug Mode Enabled",
|
|
154
|
+
enabled: true,
|
|
155
|
+
parameters: []
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: "Bandit_B202",
|
|
159
|
+
category: "Security",
|
|
160
|
+
severityLevel: "High",
|
|
161
|
+
title: "Avoid Using tarfile.extractall Without Safe Member Validation",
|
|
162
|
+
enabled: true,
|
|
163
|
+
parameters: []
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: "Bandit_B301",
|
|
167
|
+
category: "Security",
|
|
168
|
+
severityLevel: "Warning",
|
|
169
|
+
title: "Avoid Unsafe Use of Pickle for Deserialization",
|
|
170
|
+
enabled: true,
|
|
171
|
+
parameters: []
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "Bandit_B302",
|
|
175
|
+
category: "Security",
|
|
176
|
+
severityLevel: "Warning",
|
|
177
|
+
title: "Avoid Deserialization with the marshal Module",
|
|
178
|
+
enabled: true,
|
|
179
|
+
parameters: []
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "Bandit_B303",
|
|
183
|
+
category: "Security",
|
|
184
|
+
severityLevel: "Warning",
|
|
185
|
+
title: "Avoid Use of Insecure Hash Functions (MD2, MD4, MD5, SHA1)",
|
|
186
|
+
enabled: true,
|
|
187
|
+
parameters: []
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "Bandit_B304",
|
|
191
|
+
category: "Security",
|
|
192
|
+
severityLevel: "High",
|
|
193
|
+
title: "Avoid Use of Insecure Cipher or Cipher Mode",
|
|
194
|
+
enabled: true,
|
|
195
|
+
parameters: []
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: "Bandit_B305",
|
|
199
|
+
category: "Security",
|
|
200
|
+
severityLevel: "Warning",
|
|
201
|
+
title: "Avoid Use of Insecure Ciphers and Cipher Modes",
|
|
202
|
+
enabled: true,
|
|
203
|
+
parameters: []
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "Bandit_B306",
|
|
207
|
+
category: "Security",
|
|
208
|
+
severityLevel: "Warning",
|
|
209
|
+
title: "Avoid Use of Insecure and Deprecated Function mktemp",
|
|
210
|
+
enabled: true,
|
|
211
|
+
parameters: []
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "Bandit_B307",
|
|
215
|
+
category: "Security",
|
|
216
|
+
severityLevel: "Warning",
|
|
217
|
+
title: "Avoid use of eval function",
|
|
218
|
+
enabled: true,
|
|
219
|
+
parameters: []
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
id: "Bandit_B308",
|
|
223
|
+
category: "Security",
|
|
224
|
+
severityLevel: "Warning",
|
|
225
|
+
title: "Avoid use of mark_safe()",
|
|
226
|
+
enabled: true,
|
|
227
|
+
parameters: []
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: "Bandit_B309",
|
|
231
|
+
category: "Security",
|
|
232
|
+
severityLevel: "Warning",
|
|
233
|
+
title: "Avoid Use of HTTPSConnection on Older Python Versions",
|
|
234
|
+
enabled: true,
|
|
235
|
+
parameters: []
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: "Bandit_B310",
|
|
239
|
+
category: "Security",
|
|
240
|
+
severityLevel: "Warning",
|
|
241
|
+
title: "Audit urllib urlopen for permitted schemes",
|
|
242
|
+
enabled: true,
|
|
243
|
+
parameters: []
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: "Bandit_B311",
|
|
247
|
+
category: "Security",
|
|
248
|
+
severityLevel: "Warning",
|
|
249
|
+
title: "Avoid Using Standard Pseudo-Random Generators for Security Purposes",
|
|
250
|
+
enabled: true,
|
|
251
|
+
parameters: []
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "Bandit_B312",
|
|
255
|
+
category: "Security",
|
|
256
|
+
severityLevel: "High",
|
|
257
|
+
title: "Avoid Using Telnet-related Functions",
|
|
258
|
+
enabled: true,
|
|
259
|
+
parameters: []
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: "Bandit_B313",
|
|
263
|
+
category: "Security",
|
|
264
|
+
severityLevel: "Warning",
|
|
265
|
+
title: "Avoid Using Unsafe XML Parsing Methods",
|
|
266
|
+
enabled: true,
|
|
267
|
+
parameters: []
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "Bandit_B314",
|
|
271
|
+
category: "Security",
|
|
272
|
+
severityLevel: "Warning",
|
|
273
|
+
title: "Avoid Using Vulnerable XML Parsing Methods",
|
|
274
|
+
enabled: true,
|
|
275
|
+
parameters: []
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: "Bandit_B315",
|
|
279
|
+
category: "Security",
|
|
280
|
+
severityLevel: "Warning",
|
|
281
|
+
title: "Avoid Using xml.sax.expatreader.create_parser for Parsing Untrusted XML",
|
|
282
|
+
enabled: true,
|
|
283
|
+
parameters: []
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: "Bandit_B316",
|
|
287
|
+
category: "Security",
|
|
288
|
+
severityLevel: "Warning",
|
|
289
|
+
title: "Avoid parsing untrusted XML data with vulnerable XML methods",
|
|
290
|
+
enabled: true,
|
|
291
|
+
parameters: []
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: "Bandit_B317",
|
|
295
|
+
category: "Security",
|
|
296
|
+
severityLevel: "Warning",
|
|
297
|
+
title: "Avoid Using xml.sax Methods to Parse Untrusted XML",
|
|
298
|
+
enabled: true,
|
|
299
|
+
parameters: []
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: "Bandit_B318",
|
|
303
|
+
category: "Security",
|
|
304
|
+
severityLevel: "Warning",
|
|
305
|
+
title: "Avoid Unsafe XML Parsing with xml.dom.minidom",
|
|
306
|
+
enabled: true,
|
|
307
|
+
parameters: []
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: "Bandit_B319",
|
|
311
|
+
category: "Security",
|
|
312
|
+
severityLevel: "Warning",
|
|
313
|
+
title: "Avoid Parsing Untrusted XML with xml.dom.pulldom Methods",
|
|
314
|
+
enabled: true,
|
|
315
|
+
parameters: []
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "Bandit_B320",
|
|
319
|
+
category: "Security",
|
|
320
|
+
severityLevel: "Warning",
|
|
321
|
+
title: "Avoid Using Vulnerable lxml.etree XML Parsing Methods",
|
|
322
|
+
enabled: true,
|
|
323
|
+
parameters: []
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
id: "Bandit_B321",
|
|
327
|
+
category: "Security",
|
|
328
|
+
severityLevel: "High",
|
|
329
|
+
title: "Avoid Using FTP-Related Functions",
|
|
330
|
+
enabled: true,
|
|
331
|
+
parameters: []
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "Bandit_B322",
|
|
335
|
+
category: "Security",
|
|
336
|
+
severityLevel: "High",
|
|
337
|
+
title: "Avoid using input() in Python 2",
|
|
338
|
+
enabled: false,
|
|
339
|
+
parameters: []
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "Bandit_B323",
|
|
343
|
+
category: "Security",
|
|
344
|
+
severityLevel: "Warning",
|
|
345
|
+
title: "Avoid Using Unverified SSL Context",
|
|
346
|
+
enabled: false,
|
|
347
|
+
parameters: []
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
id: "Bandit_B324",
|
|
351
|
+
category: "Security",
|
|
352
|
+
severityLevel: "High",
|
|
353
|
+
title: "Avoid Use of Insecure Hash Functions in hashlib",
|
|
354
|
+
enabled: false,
|
|
355
|
+
parameters: []
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: "Bandit_B325",
|
|
359
|
+
category: "Security",
|
|
360
|
+
severityLevel: "Warning",
|
|
361
|
+
title: "Avoid use of os.tempnam() and os.tmpnam()",
|
|
362
|
+
enabled: false,
|
|
363
|
+
parameters: []
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: "Bandit_B401",
|
|
367
|
+
category: "Security",
|
|
368
|
+
severityLevel: "High",
|
|
369
|
+
title: "Avoid Importing Telnetlib Module",
|
|
370
|
+
enabled: true,
|
|
371
|
+
parameters: []
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "Bandit_B402",
|
|
375
|
+
category: "Security",
|
|
376
|
+
severityLevel: "High",
|
|
377
|
+
title: "Avoid Importing ftplib Module",
|
|
378
|
+
enabled: true,
|
|
379
|
+
parameters: []
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
id: "Bandit_B403",
|
|
383
|
+
category: "Security",
|
|
384
|
+
severityLevel: "Warning",
|
|
385
|
+
title: "Consider Security Implications of Importing pickle and Related Modules",
|
|
386
|
+
enabled: true,
|
|
387
|
+
parameters: []
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
id: "Bandit_B404",
|
|
391
|
+
category: "Security",
|
|
392
|
+
severityLevel: "Info",
|
|
393
|
+
title: "Consider security implications of importing subprocess module",
|
|
394
|
+
enabled: true,
|
|
395
|
+
parameters: []
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "Bandit_B405",
|
|
399
|
+
category: "Security",
|
|
400
|
+
severityLevel: "Warning",
|
|
401
|
+
title: "Avoid Using Vulnerable xml.etree Imports for Parsing Untrusted XML",
|
|
402
|
+
enabled: true,
|
|
403
|
+
parameters: []
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: "Bandit_B406",
|
|
407
|
+
category: "Security",
|
|
408
|
+
severityLevel: "Warning",
|
|
409
|
+
title: "Avoid Using Vulnerable XML Parsing Methods",
|
|
410
|
+
enabled: true,
|
|
411
|
+
parameters: []
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
id: "Bandit_B407",
|
|
415
|
+
category: "Security",
|
|
416
|
+
severityLevel: "Warning",
|
|
417
|
+
title: "Avoid Using Vulnerable XML Parsing Methods",
|
|
418
|
+
enabled: true,
|
|
419
|
+
parameters: []
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
id: "Bandit_B408",
|
|
423
|
+
category: "Security",
|
|
424
|
+
severityLevel: "Warning",
|
|
425
|
+
title: "Avoid importing xml.dom.minidom for untrusted XML parsing",
|
|
426
|
+
enabled: true,
|
|
427
|
+
parameters: []
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "Bandit_B409",
|
|
431
|
+
category: "Security",
|
|
432
|
+
severityLevel: "Warning",
|
|
433
|
+
title: "Avoid importing xml.dom.pulldom for untrusted XML parsing",
|
|
434
|
+
enabled: true,
|
|
435
|
+
parameters: []
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
id: "Bandit_B410",
|
|
439
|
+
category: "Security",
|
|
440
|
+
severityLevel: "Warning",
|
|
441
|
+
title: "Avoid Using Vulnerable XML Parsing Libraries (lxml)",
|
|
442
|
+
enabled: true,
|
|
443
|
+
parameters: []
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
id: "Bandit_B411",
|
|
447
|
+
category: "Security",
|
|
448
|
+
severityLevel: "High",
|
|
449
|
+
title: "Avoid Importing xmlrpclib",
|
|
450
|
+
enabled: true,
|
|
451
|
+
parameters: []
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
id: "Bandit_B412",
|
|
455
|
+
category: "Security",
|
|
456
|
+
severityLevel: "High",
|
|
457
|
+
title: "Avoid importing CGI handlers vulnerable to httpoxy",
|
|
458
|
+
enabled: false,
|
|
459
|
+
parameters: []
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
id: "Bandit_B413",
|
|
463
|
+
category: "Security",
|
|
464
|
+
severityLevel: "High",
|
|
465
|
+
title: "Avoid Importing PyCrypto Library",
|
|
466
|
+
enabled: false,
|
|
467
|
+
parameters: []
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
id: "Bandit_B414",
|
|
471
|
+
category: "Security",
|
|
472
|
+
severityLevel: "High",
|
|
473
|
+
title: "Avoid Importing PyCryptodome Modules",
|
|
474
|
+
enabled: false,
|
|
475
|
+
parameters: []
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
id: "Bandit_B501",
|
|
479
|
+
category: "Security",
|
|
480
|
+
severityLevel: "High",
|
|
481
|
+
title: "Avoid disabling SSL certificate validation in requests",
|
|
482
|
+
enabled: true,
|
|
483
|
+
parameters: []
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
id: "Bandit_B502",
|
|
487
|
+
category: "Security",
|
|
488
|
+
severityLevel: "High",
|
|
489
|
+
title: "Avoid Use of SSL/TLS with Bad Versions",
|
|
490
|
+
enabled: true,
|
|
491
|
+
parameters: []
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
id: "Bandit_B503",
|
|
495
|
+
category: "Security",
|
|
496
|
+
severityLevel: "Warning",
|
|
497
|
+
title: "Detect SSL Usage with Insecure Default Protocol Versions",
|
|
498
|
+
enabled: true,
|
|
499
|
+
parameters: []
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
id: "Bandit_B504",
|
|
503
|
+
category: "Security",
|
|
504
|
+
severityLevel: "Warning",
|
|
505
|
+
title: "Detect SSL/TLS Usage with No Version Specified",
|
|
506
|
+
enabled: true,
|
|
507
|
+
parameters: []
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
id: "Bandit_B505",
|
|
511
|
+
category: "Security",
|
|
512
|
+
severityLevel: "High",
|
|
513
|
+
title: "Detect Use of Weak Cryptographic Keys",
|
|
514
|
+
enabled: true,
|
|
515
|
+
parameters: []
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
id: "Bandit_B506",
|
|
519
|
+
category: "Security",
|
|
520
|
+
severityLevel: "Warning",
|
|
521
|
+
title: "Avoid Unsafe Use of yaml.load",
|
|
522
|
+
enabled: true,
|
|
523
|
+
parameters: []
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
id: "Bandit_B507",
|
|
527
|
+
category: "Security",
|
|
528
|
+
severityLevel: "High",
|
|
529
|
+
title: "Avoid Disabling SSH Host Key Verification",
|
|
530
|
+
enabled: false,
|
|
531
|
+
parameters: []
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
id: "Bandit_B508",
|
|
535
|
+
category: "Security",
|
|
536
|
+
severityLevel: "Warning",
|
|
537
|
+
title: "Avoid using insecure SNMP versions",
|
|
538
|
+
enabled: false,
|
|
539
|
+
parameters: []
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
id: "Bandit_B509",
|
|
543
|
+
category: "Security",
|
|
544
|
+
severityLevel: "Warning",
|
|
545
|
+
title: "Avoid Weak SNMP Cryptography",
|
|
546
|
+
enabled: false,
|
|
547
|
+
parameters: []
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
id: "Bandit_B601",
|
|
551
|
+
category: "Security",
|
|
552
|
+
severityLevel: "Warning",
|
|
553
|
+
title: "Detect Shell Injection Vulnerabilities in Paramiko exec_command",
|
|
554
|
+
enabled: true,
|
|
555
|
+
parameters: []
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
id: "Bandit_B602",
|
|
559
|
+
category: "Security",
|
|
560
|
+
severityLevel: "High",
|
|
561
|
+
title: "Avoid subprocess.Popen with shell=True",
|
|
562
|
+
enabled: true,
|
|
563
|
+
parameters: []
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
id: "Bandit_B603",
|
|
567
|
+
category: "Security",
|
|
568
|
+
severityLevel: "Warning",
|
|
569
|
+
title: "Check subprocess usage without shell=True",
|
|
570
|
+
enabled: true,
|
|
571
|
+
parameters: []
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
id: "Bandit_B604",
|
|
575
|
+
category: "Security",
|
|
576
|
+
severityLevel: "Warning",
|
|
577
|
+
title: "Detect function calls with shell=True parameter",
|
|
578
|
+
enabled: true,
|
|
579
|
+
parameters: []
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
id: "Bandit_B605",
|
|
583
|
+
category: "Security",
|
|
584
|
+
severityLevel: "Warning",
|
|
585
|
+
title: "Avoid starting a process with a shell",
|
|
586
|
+
enabled: false,
|
|
587
|
+
parameters: []
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
id: "Bandit_B606",
|
|
591
|
+
category: "Security",
|
|
592
|
+
severityLevel: "Info",
|
|
593
|
+
title: "Detect Starting a Process Without a Shell",
|
|
594
|
+
enabled: true,
|
|
595
|
+
parameters: []
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
id: "Bandit_B607",
|
|
599
|
+
category: "Security",
|
|
600
|
+
severityLevel: "Warning",
|
|
601
|
+
title: "Avoid starting a process with a partial executable path",
|
|
602
|
+
enabled: false,
|
|
603
|
+
parameters: []
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
id: "Bandit_B608",
|
|
607
|
+
category: "Security",
|
|
608
|
+
severityLevel: "Warning",
|
|
609
|
+
title: "Detect Hardcoded SQL Expressions to Prevent SQL Injection",
|
|
610
|
+
enabled: true,
|
|
611
|
+
parameters: []
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
id: "Bandit_B609",
|
|
615
|
+
category: "Security",
|
|
616
|
+
severityLevel: "High",
|
|
617
|
+
title: "Detect Wildcard Injection in Linux Command Emulation",
|
|
618
|
+
enabled: true,
|
|
619
|
+
parameters: []
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
id: "Bandit_B610",
|
|
623
|
+
category: "Security",
|
|
624
|
+
severityLevel: "Warning",
|
|
625
|
+
title: "Avoid Potential SQL Injection in Django extra() Function",
|
|
626
|
+
enabled: false,
|
|
627
|
+
parameters: []
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
id: "Bandit_B611",
|
|
631
|
+
category: "Security",
|
|
632
|
+
severityLevel: "Warning",
|
|
633
|
+
title: "Avoid Use of RawSQL Function in Django",
|
|
634
|
+
enabled: false,
|
|
635
|
+
parameters: []
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
id: "Bandit_B612",
|
|
639
|
+
category: "Security",
|
|
640
|
+
severityLevel: "Warning",
|
|
641
|
+
title: "Avoid insecure use of logging.config.listen",
|
|
642
|
+
enabled: false,
|
|
643
|
+
parameters: []
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
id: "Bandit_B701",
|
|
647
|
+
category: "Security",
|
|
648
|
+
severityLevel: "High",
|
|
649
|
+
title: "Avoid disabling autoescaping in Jinja2 templates",
|
|
650
|
+
enabled: true,
|
|
651
|
+
parameters: []
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
id: "Bandit_B702",
|
|
655
|
+
category: "Security",
|
|
656
|
+
severityLevel: "Warning",
|
|
657
|
+
title: "Detect Use of Mako Templates",
|
|
658
|
+
enabled: true,
|
|
659
|
+
parameters: []
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
id: "Bandit_B703",
|
|
663
|
+
category: "Security",
|
|
664
|
+
severityLevel: "Warning",
|
|
665
|
+
title: "Avoid Potential XSS with django mark_safe Function",
|
|
666
|
+
enabled: false,
|
|
667
|
+
parameters: []
|
|
668
|
+
}
|
|
669
|
+
];
|
|
670
|
+
|
|
671
|
+
// src/adapter.ts
|
|
672
|
+
var TOOL_ID = "Bandit";
|
|
673
|
+
var ADAPTER_ID = "bandit-1";
|
|
674
|
+
var PIP_PACKAGES = ["bandit==1.8.3"];
|
|
675
|
+
var PYTHON_VERSION_RANGE = ">=3.8.0 <3.14.0";
|
|
676
|
+
var PATTERN_META = new Map(
|
|
677
|
+
patterns_default.map((p) => [
|
|
678
|
+
p.id,
|
|
679
|
+
{
|
|
680
|
+
category: p.category,
|
|
681
|
+
severity: p.severityLevel ?? "Info"
|
|
682
|
+
}
|
|
683
|
+
])
|
|
684
|
+
);
|
|
685
|
+
function ruleMetadata(patternId) {
|
|
686
|
+
return PATTERN_META.get(patternId) ?? { category: "Security", severity: "Info" };
|
|
687
|
+
}
|
|
688
|
+
function mapConfidence(confidence) {
|
|
689
|
+
switch (confidence) {
|
|
690
|
+
case "HIGH":
|
|
691
|
+
return 3;
|
|
692
|
+
case "MEDIUM":
|
|
693
|
+
return 2;
|
|
694
|
+
default:
|
|
695
|
+
return 1;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
function getLineContent(lines, lineNumber) {
|
|
699
|
+
return lines[lineNumber - 1] ?? "";
|
|
700
|
+
}
|
|
701
|
+
async function readFileLines(filePath) {
|
|
702
|
+
try {
|
|
703
|
+
const content = await import_promises.default.readFile(filePath, "utf-8");
|
|
704
|
+
return content.split("\n");
|
|
705
|
+
} catch {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function buildResult(startedAt, completedAt, issues, errors, filesAnalyzed) {
|
|
710
|
+
const durationMs = completedAt.getTime() - startedAt.getTime();
|
|
711
|
+
return {
|
|
712
|
+
metadata: {
|
|
713
|
+
startedAt: startedAt.toISOString(),
|
|
714
|
+
completedAt: completedAt.toISOString(),
|
|
715
|
+
durationMs
|
|
716
|
+
},
|
|
717
|
+
issues,
|
|
718
|
+
errors,
|
|
719
|
+
summary: {
|
|
720
|
+
toolId: TOOL_ID,
|
|
721
|
+
status: errors.some((e) => e.level === "error") ? "partial" : "success",
|
|
722
|
+
issueCount: issues.length,
|
|
723
|
+
errorCount: errors.length,
|
|
724
|
+
durationMs,
|
|
725
|
+
filesAnalyzed
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
var CONFIG_FILE_CANDIDATES = [".bandit", "bandit.yml", "pyproject.toml"];
|
|
730
|
+
async function resolveBinaryPath(ctx) {
|
|
731
|
+
const venvDir = (0, import_tooling.getVenvDir)(ctx.globalDir, ADAPTER_ID);
|
|
732
|
+
const venvBin = import_path.default.join(venvDir, "bin", "bandit");
|
|
733
|
+
try {
|
|
734
|
+
await import_promises.default.access(venvBin);
|
|
735
|
+
return venvBin;
|
|
736
|
+
} catch {
|
|
737
|
+
return "bandit";
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function buildMessage(result) {
|
|
741
|
+
const base = result.issue_text;
|
|
742
|
+
if (result.issue_cwe && result.issue_cwe.id) {
|
|
743
|
+
return `${base} (CWE-${result.issue_cwe.id})`;
|
|
744
|
+
}
|
|
745
|
+
return base;
|
|
746
|
+
}
|
|
747
|
+
var adapter = {
|
|
748
|
+
id: TOOL_ID,
|
|
749
|
+
displayName: "Bandit",
|
|
750
|
+
preferredVersion: "1.8.3",
|
|
751
|
+
compatibleVersions: ">=1.7.0",
|
|
752
|
+
website: "https://bandit.readthedocs.io/",
|
|
753
|
+
languages: ["Python"],
|
|
754
|
+
filePatterns: ["**/*.py"],
|
|
755
|
+
supportedOS: ["linux", "darwin"],
|
|
756
|
+
supportedArch: ["x64", "arm64"],
|
|
757
|
+
supportedRunners: ["local", "container"],
|
|
758
|
+
prerequisites: [
|
|
759
|
+
{
|
|
760
|
+
name: "Python",
|
|
761
|
+
versionRange: PYTHON_VERSION_RANGE,
|
|
762
|
+
preferredVersion: "3.12.0",
|
|
763
|
+
checkCommand: "python3 --version"
|
|
764
|
+
}
|
|
765
|
+
],
|
|
766
|
+
/**
|
|
767
|
+
* Checks whether bandit is available in a venv or on the global PATH.
|
|
768
|
+
*
|
|
769
|
+
* Checks in order:
|
|
770
|
+
* 1. Codacy-managed venv (`~/.codacy/runtimes/bandit-1/venv/bin/bandit`)
|
|
771
|
+
* 2. Global PATH (`bandit`)
|
|
772
|
+
*
|
|
773
|
+
* Parses the version from `bandit --version` output like `bandit 1.8.3`.
|
|
774
|
+
*/
|
|
775
|
+
async checkAvailability(ctx) {
|
|
776
|
+
ctx.logger.debug("Checking availability", { toolId: TOOL_ID });
|
|
777
|
+
const pythonInfo = await (0, import_tooling.findPython)(PYTHON_VERSION_RANGE, ctx.logger).catch(() => null);
|
|
778
|
+
const dependencies = pythonInfo ? [{ name: "Python", available: true, version: pythonInfo.version, installation: "global" }] : [{ name: "Python", available: false, reason: `Python 3 (${PYTHON_VERSION_RANGE}) not found` }];
|
|
779
|
+
const venvDir = (0, import_tooling.getVenvDir)(ctx.globalDir, ADAPTER_ID);
|
|
780
|
+
try {
|
|
781
|
+
const venvPython = import_path.default.join(venvDir, "bin", "python");
|
|
782
|
+
await import_promises.default.access(venvPython);
|
|
783
|
+
const venv = {
|
|
784
|
+
root: venvDir,
|
|
785
|
+
binDir: import_path.default.join(venvDir, "bin"),
|
|
786
|
+
python: venvPython,
|
|
787
|
+
pip: import_path.default.join(venvDir, "bin", "pip")
|
|
788
|
+
};
|
|
789
|
+
const version = await (0, import_tooling.checkVenvTool)(venv, "bandit", "--version", /bandit\s+(\S+)/, ctx.logger);
|
|
790
|
+
if (version) {
|
|
791
|
+
ctx.logger.debug("Found bandit in codacy venv", { toolId: TOOL_ID, version });
|
|
792
|
+
return {
|
|
793
|
+
available: true,
|
|
794
|
+
version,
|
|
795
|
+
path: (0, import_tooling.resolveVenvBinary)(venv, "bandit"),
|
|
796
|
+
installation: "codacy",
|
|
797
|
+
dependencies
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
} catch {
|
|
801
|
+
}
|
|
802
|
+
try {
|
|
803
|
+
const { stdout, exitCode } = await (0, import_tooling.spawnTool)("bandit", ["--version"], {
|
|
804
|
+
cwd: process.cwd()
|
|
805
|
+
});
|
|
806
|
+
if (exitCode === 0) {
|
|
807
|
+
const match = stdout.match(/bandit\s+(\S+)/);
|
|
808
|
+
if (match) {
|
|
809
|
+
ctx.logger.debug("Found bandit on global PATH", { toolId: TOOL_ID, version: match[1] });
|
|
810
|
+
return {
|
|
811
|
+
available: true,
|
|
812
|
+
version: match[1],
|
|
813
|
+
installation: "global",
|
|
814
|
+
dependencies
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
} catch {
|
|
819
|
+
}
|
|
820
|
+
ctx.logger.debug("bandit not found", { toolId: TOOL_ID });
|
|
821
|
+
return {
|
|
822
|
+
available: false,
|
|
823
|
+
reason: "bandit not found in venv or PATH",
|
|
824
|
+
installHint: "pip install bandit # or\ncodacy-analysis analyze . --install-dependencies # auto-install",
|
|
825
|
+
dependencies
|
|
826
|
+
};
|
|
827
|
+
},
|
|
828
|
+
/**
|
|
829
|
+
* Installs bandit into an isolated Python virtual environment.
|
|
830
|
+
*
|
|
831
|
+
* Uses findPython -> ensureVenv -> pipInstall to create a dedicated venv
|
|
832
|
+
* at `~/.codacy/runtimes/bandit-1/venv/` and install `bandit==1.8.3`.
|
|
833
|
+
*/
|
|
834
|
+
async install(ctx) {
|
|
835
|
+
ctx.logger.info("Starting install", { toolId: TOOL_ID });
|
|
836
|
+
const python = await (0, import_tooling.ensurePython)(ctx.globalDir, PYTHON_VERSION_RANGE, ctx.logger);
|
|
837
|
+
if (!python) {
|
|
838
|
+
return {
|
|
839
|
+
success: false,
|
|
840
|
+
error: {
|
|
841
|
+
toolId: TOOL_ID,
|
|
842
|
+
phase: "requirementCheck",
|
|
843
|
+
kind: "MissingPrerequisite",
|
|
844
|
+
message: `Python (${PYTHON_VERSION_RANGE}) could not be found or downloaded. Bandit 1.8.x is incompatible with Python 3.14+.`,
|
|
845
|
+
level: "error"
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
const venvDir = (0, import_tooling.getVenvDir)(ctx.globalDir, ADAPTER_ID);
|
|
850
|
+
try {
|
|
851
|
+
ctx.logger.info(`Creating venv for bandit at ${venvDir}...`);
|
|
852
|
+
const venv = await (0, import_tooling.ensureVenv)(python.path, venvDir, ctx.logger);
|
|
853
|
+
ctx.logger.info(`Installing ${PIP_PACKAGES.join(", ")} into venv...`);
|
|
854
|
+
await (0, import_tooling.pipInstall)(venv, PIP_PACKAGES, ctx.logger);
|
|
855
|
+
const banditPath = (0, import_tooling.resolveVenvBinary)(venv, "bandit");
|
|
856
|
+
ctx.logger.info(`Installed bandit to ${banditPath}`);
|
|
857
|
+
return { success: true, installedPath: banditPath, installedVersion: adapter.preferredVersion };
|
|
858
|
+
} catch (err) {
|
|
859
|
+
return {
|
|
860
|
+
success: false,
|
|
861
|
+
error: {
|
|
862
|
+
toolId: TOOL_ID,
|
|
863
|
+
phase: "toolInstall",
|
|
864
|
+
kind: "InstallError",
|
|
865
|
+
message: `Failed to install bandit: ${err instanceof Error ? err.message : String(err)}`,
|
|
866
|
+
level: "error"
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
/**
|
|
872
|
+
* Checks for bandit configuration files in the repository root.
|
|
873
|
+
*
|
|
874
|
+
* Looks for `.bandit`, `bandit.yml`, or `pyproject.toml` (in that order).
|
|
875
|
+
* Returns the first found file.
|
|
876
|
+
*/
|
|
877
|
+
async checkLocalConfigurationFile(ctx) {
|
|
878
|
+
ctx.logger.debug("Checking for local config", { toolId: TOOL_ID });
|
|
879
|
+
for (const candidate of CONFIG_FILE_CANDIDATES) {
|
|
880
|
+
const candidatePath = import_path.default.join(ctx.repositoryRoot, candidate);
|
|
881
|
+
try {
|
|
882
|
+
await import_promises.default.access(candidatePath);
|
|
883
|
+
ctx.logger.debug("Found local config", { toolId: TOOL_ID, path: candidatePath });
|
|
884
|
+
return { found: true, path: candidatePath };
|
|
885
|
+
} catch {
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
return { found: false };
|
|
889
|
+
},
|
|
890
|
+
/**
|
|
891
|
+
* Runs `bandit -f json` against the target files and converts the JSON
|
|
892
|
+
* output into Codacy Issue objects.
|
|
893
|
+
*
|
|
894
|
+
* Config resolution:
|
|
895
|
+
* - If `useLocalConfigurationFile` is true and a local config is found,
|
|
896
|
+
* passes `--ini .bandit` for INI files or `-c <path>` for YAML/TOML.
|
|
897
|
+
* - If patterns are specified and no local config is in use, passes
|
|
898
|
+
* `-t B602,B301,...` to filter bandit to only those test IDs.
|
|
899
|
+
* - Otherwise, bandit runs with all default tests.
|
|
900
|
+
*
|
|
901
|
+
* Exit codes: 0 = no issues, 1 = issues found (normal), >1 = error.
|
|
902
|
+
*
|
|
903
|
+
* The `errors` array in bandit's output (file-level errors like syntax
|
|
904
|
+
* errors) is mapped to AnalysisError objects.
|
|
905
|
+
*
|
|
906
|
+
* Never throws — all errors are captured in the returned `errors` array.
|
|
907
|
+
*/
|
|
908
|
+
async analyze(ctx, codacyConfig) {
|
|
909
|
+
const startMs = Date.now();
|
|
910
|
+
ctx.logger.info("Starting analysis", { toolId: TOOL_ID, fileCount: ctx.targetFiles.length });
|
|
911
|
+
const startedAt = /* @__PURE__ */ new Date();
|
|
912
|
+
const issues = [];
|
|
913
|
+
const errors = [];
|
|
914
|
+
if (ctx.targetFiles.length === 0) {
|
|
915
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, 0);
|
|
916
|
+
}
|
|
917
|
+
const binary = await resolveBinaryPath(ctx);
|
|
918
|
+
const absoluteFiles = ctx.targetFiles.map((f) => import_path.default.resolve(ctx.repositoryRoot, f));
|
|
919
|
+
const args = ["-f", "json"];
|
|
920
|
+
const useLocal = codacyConfig.useLocalConfigurationFile && codacyConfig.localConfigurationFile;
|
|
921
|
+
if (useLocal) {
|
|
922
|
+
const configPath = codacyConfig.localConfigurationFile;
|
|
923
|
+
if (configPath.endsWith(".bandit")) {
|
|
924
|
+
args.push("--ini", configPath);
|
|
925
|
+
} else {
|
|
926
|
+
args.push("-c", configPath);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
args.push(...absoluteFiles);
|
|
930
|
+
try {
|
|
931
|
+
const { stdout, stderr, exitCode } = await (0, import_tooling.spawnTool)(binary, args, {
|
|
932
|
+
cwd: ctx.repositoryRoot,
|
|
933
|
+
logger: ctx.logger
|
|
934
|
+
});
|
|
935
|
+
if (exitCode > 1) {
|
|
936
|
+
errors.push({
|
|
937
|
+
toolId: TOOL_ID,
|
|
938
|
+
phase: "toolInvoke",
|
|
939
|
+
kind: "NonZeroExit",
|
|
940
|
+
message: `bandit exited with code ${exitCode}${stderr ? `: ${stderr.trim()}` : ""}`,
|
|
941
|
+
level: "error"
|
|
942
|
+
});
|
|
943
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
944
|
+
}
|
|
945
|
+
if (!stdout.trim()) {
|
|
946
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
947
|
+
}
|
|
948
|
+
let banditOutput;
|
|
949
|
+
try {
|
|
950
|
+
banditOutput = JSON.parse(stdout);
|
|
951
|
+
} catch (err) {
|
|
952
|
+
errors.push({
|
|
953
|
+
toolId: TOOL_ID,
|
|
954
|
+
phase: "outputParse",
|
|
955
|
+
kind: "MalformedOutput",
|
|
956
|
+
message: `Failed to parse bandit JSON output: ${err instanceof Error ? err.message : String(err)}`,
|
|
957
|
+
level: "error"
|
|
958
|
+
});
|
|
959
|
+
return buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
960
|
+
}
|
|
961
|
+
if (banditOutput.errors && banditOutput.errors.length > 0) {
|
|
962
|
+
for (const banditError of banditOutput.errors) {
|
|
963
|
+
errors.push({
|
|
964
|
+
toolId: TOOL_ID,
|
|
965
|
+
phase: "toolInvoke",
|
|
966
|
+
kind: "FileError",
|
|
967
|
+
message: `${banditError.filename}: ${banditError.reason}`,
|
|
968
|
+
level: "warning"
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const enabledPatterns = new Set(codacyConfig.patterns.map((p) => p.patternId));
|
|
973
|
+
const resultsByFile = /* @__PURE__ */ new Map();
|
|
974
|
+
for (const result2 of banditOutput.results) {
|
|
975
|
+
const list = resultsByFile.get(result2.filename) ?? [];
|
|
976
|
+
list.push(result2);
|
|
977
|
+
resultsByFile.set(result2.filename, list);
|
|
978
|
+
}
|
|
979
|
+
for (const [filePath, fileResults] of resultsByFile) {
|
|
980
|
+
const absolutePath = import_path.default.isAbsolute(filePath) ? filePath : import_path.default.resolve(ctx.repositoryRoot, filePath);
|
|
981
|
+
const lines = await readFileLines(absolutePath);
|
|
982
|
+
const relPath = import_path.default.relative(ctx.repositoryRoot, absolutePath);
|
|
983
|
+
for (const result2 of fileResults) {
|
|
984
|
+
const patternId = (0, import_tooling.buildPatternId)(TOOL_ID, result2.test_id);
|
|
985
|
+
if (enabledPatterns.size > 0 && !enabledPatterns.has(patternId)) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
const meta = ruleMetadata(patternId);
|
|
989
|
+
const endLine = result2.line_range && result2.line_range.length > 1 ? result2.line_range[result2.line_range.length - 1] : void 0;
|
|
990
|
+
const issue = {
|
|
991
|
+
filePath: relPath,
|
|
992
|
+
patternId,
|
|
993
|
+
toolId: TOOL_ID,
|
|
994
|
+
line: result2.line_number,
|
|
995
|
+
column: result2.col_offset + 1,
|
|
996
|
+
// 0-based → 1-based
|
|
997
|
+
endLine,
|
|
998
|
+
endColumn: result2.end_col_offset ? result2.end_col_offset + 1 : void 0,
|
|
999
|
+
// 0-based → 1-based
|
|
1000
|
+
message: buildMessage(result2),
|
|
1001
|
+
lineContent: getLineContent(lines, result2.line_number),
|
|
1002
|
+
multilineContent: (0, import_tooling.getMultilineContent)(lines, result2.line_number, endLine),
|
|
1003
|
+
category: meta.category,
|
|
1004
|
+
severity: meta.severity,
|
|
1005
|
+
confidence: mapConfidence(result2.issue_confidence)
|
|
1006
|
+
};
|
|
1007
|
+
issues.push(issue);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
const stderrFiltered = stderr.split("\n").filter((line) => !/^\[.*\]\s+INFO\s+/.test(line.trim())).join("\n").trim();
|
|
1011
|
+
if (stderrFiltered) {
|
|
1012
|
+
errors.push({
|
|
1013
|
+
toolId: TOOL_ID,
|
|
1014
|
+
phase: "outputParse",
|
|
1015
|
+
kind: "ParseWarning",
|
|
1016
|
+
message: `bandit stderr: ${stderrFiltered}`,
|
|
1017
|
+
level: "warning"
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
errors.push({
|
|
1022
|
+
toolId: TOOL_ID,
|
|
1023
|
+
phase: "toolInvoke",
|
|
1024
|
+
kind: "InvocationError",
|
|
1025
|
+
message: `Failed to run bandit: ${err instanceof Error ? err.message : String(err)}`,
|
|
1026
|
+
level: "error"
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
const result = buildResult(startedAt, /* @__PURE__ */ new Date(), issues, errors, ctx.targetFiles.length);
|
|
1030
|
+
ctx.logger.info("Analysis complete", {
|
|
1031
|
+
toolId: TOOL_ID,
|
|
1032
|
+
issueCount: result.summary.issueCount,
|
|
1033
|
+
errorCount: result.summary.errorCount,
|
|
1034
|
+
durationMs: Date.now() - startMs
|
|
1035
|
+
});
|
|
1036
|
+
return result;
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
// src/index.ts
|
|
1041
|
+
var index_default = (0, import_tooling2.defineToolAdapter)(adapter);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codacy/tools-bandit-1",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Bandit adapter — CLI-mode Python security linter",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@codacy/tooling": "0.1.0"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Codacy <support@codacy.com> (https://www.codacy.com)",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/codacy/analysis-cli.git",
|
|
18
|
+
"directory": "packages/tools/bandit-1"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/codacy/analysis-cli",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20.0.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup src/index.ts --format cjs --dts --clean",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"lint": "eslint src/",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"prefetch": "node ../scripts/fetch-patterns.mjs 89b6fc6a-9137-4cf3-8b9f-dcc3bedfa738 src/patterns.json"
|
|
30
|
+
}
|
|
31
|
+
}
|