pi-lens 3.8.21 → 3.8.23
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/CHANGELOG.md +28 -0
- package/README.md +2 -0
- package/clients/dispatch/dispatcher.ts +75 -91
- package/clients/dispatch/fact-provider-types.ts +22 -0
- package/clients/dispatch/fact-rule-runner.ts +22 -0
- package/clients/dispatch/fact-runner.ts +28 -0
- package/clients/dispatch/fact-scheduler.ts +78 -0
- package/clients/dispatch/fact-store.ts +67 -0
- package/clients/dispatch/facts/comment-facts.ts +59 -0
- package/clients/dispatch/facts/file-content.ts +20 -0
- package/clients/dispatch/facts/function-facts.ts +177 -0
- package/clients/dispatch/facts/try-catch-facts.ts +80 -0
- package/clients/dispatch/integration.ts +130 -24
- package/clients/dispatch/priorities.ts +22 -0
- package/clients/dispatch/rules/async-noise.ts +43 -0
- package/clients/dispatch/rules/error-obscuring.ts +40 -0
- package/clients/dispatch/rules/error-swallowing.ts +35 -0
- package/clients/dispatch/rules/pass-through-wrappers.ts +52 -0
- package/clients/dispatch/rules/placeholder-comments.ts +47 -0
- package/clients/dispatch/runners/architect.ts +2 -1
- package/clients/dispatch/runners/ast-grep-napi.ts +2 -1
- package/clients/dispatch/runners/biome-check.ts +40 -8
- package/clients/dispatch/runners/biome.ts +2 -1
- package/clients/dispatch/runners/eslint.ts +34 -6
- package/clients/dispatch/runners/go-vet.ts +2 -1
- package/clients/dispatch/runners/golangci-lint.ts +2 -1
- package/clients/dispatch/runners/index.ts +29 -27
- package/clients/dispatch/runners/lsp.ts +60 -4
- package/clients/dispatch/runners/oxlint.ts +2 -1
- package/clients/dispatch/runners/pyright.ts +2 -1
- package/clients/dispatch/runners/python-slop.ts +2 -1
- package/clients/dispatch/runners/rubocop.ts +2 -1
- package/clients/dispatch/runners/ruff.ts +2 -1
- package/clients/dispatch/runners/rust-clippy.ts +2 -1
- package/clients/dispatch/runners/shellcheck.ts +2 -1
- package/clients/dispatch/runners/similarity.ts +2 -1
- package/clients/dispatch/runners/spellcheck.ts +2 -1
- package/clients/dispatch/runners/sqlfluff.ts +2 -1
- package/clients/dispatch/runners/tree-sitter.ts +469 -1
- package/clients/dispatch/runners/ts-lsp.ts +2 -1
- package/clients/dispatch/runners/type-safety.ts +2 -1
- package/clients/dispatch/runners/yamllint.ts +2 -1
- package/clients/dispatch/tool-profile.ts +40 -0
- package/clients/dispatch/types.ts +3 -13
- package/clients/lsp/client.ts +366 -12
- package/clients/lsp/index.ts +374 -76
- package/clients/lsp/launch.ts +42 -2
- package/clients/lsp/server.ts +186 -12
- package/clients/pipeline.ts +2 -2
- package/clients/runtime-context.ts +2 -2
- package/clients/runtime-session.ts +43 -5
- package/clients/session-summary.ts +21 -0
- package/clients/tree-sitter-client.ts +162 -0
- package/clients/tree-sitter-logger.ts +47 -0
- package/clients/tree-sitter-query-loader.ts +13 -2
- package/index.ts +67 -17
- package/package.json +3 -1
- package/rules/rule-catalog.json +64 -0
- package/rules/tree-sitter-queries/go/go-bare-error.yml +19 -7
- package/rules/tree-sitter-queries/go/go-command-injection.yml +55 -0
- package/rules/tree-sitter-queries/go/go-direct-panic.yml +45 -0
- package/rules/tree-sitter-queries/go/go-empty-if-err.yml +47 -0
- package/rules/tree-sitter-queries/go/go-goroutine-loop-capture.yml +49 -0
- package/rules/tree-sitter-queries/go/go-ignored-call-result.yml +51 -0
- package/rules/tree-sitter-queries/go/go-insecure-random.yml +51 -0
- package/rules/tree-sitter-queries/go/go-log-fatal.yml +49 -0
- package/rules/tree-sitter-queries/go/go-path-traversal.yml +51 -0
- package/rules/tree-sitter-queries/go/go-shared-map-write-goroutine.yml +54 -0
- package/rules/tree-sitter-queries/go/go-sql-injection.yml +55 -0
- package/rules/tree-sitter-queries/go/go-weak-hash.yml +51 -0
- package/rules/tree-sitter-queries/python/python-command-injection.yml +63 -0
- package/rules/tree-sitter-queries/python/python-insecure-deserialization.yml +48 -0
- package/rules/tree-sitter-queries/python/python-insecure-random.yml +51 -0
- package/rules/tree-sitter-queries/python/python-path-traversal.yml +55 -0
- package/rules/tree-sitter-queries/python/python-sql-injection.yml +47 -0
- package/rules/tree-sitter-queries/python/python-ssrf.yml +50 -0
- package/rules/tree-sitter-queries/python/python-thread-global-write.yml +58 -0
- package/rules/tree-sitter-queries/python/python-weak-hash.yml +51 -0
- package/rules/tree-sitter-queries/ruby/ruby-command-injection.yml +56 -0
- package/rules/tree-sitter-queries/ruby/ruby-insecure-deserialization.yml +47 -0
- package/rules/tree-sitter-queries/ruby/ruby-insecure-random.yml +54 -0
- package/rules/tree-sitter-queries/ruby/ruby-weak-hash.yml +50 -0
- package/rules/tree-sitter-queries/rust/rust-lock-held-across-await.yml +59 -0
- package/rules/tree-sitter-queries/typescript/ts-command-injection.yml +60 -0
- package/rules/tree-sitter-queries/typescript/ts-detached-async-call.yml +56 -0
- package/rules/tree-sitter-queries/typescript/ts-insecure-random.yml +54 -0
- package/rules/tree-sitter-queries/typescript/ts-ssrf.yml +53 -0
- package/rules/tree-sitter-queries/typescript/ts-weak-hash.yml +54 -0
- package/scripts/validate-rule-catalog.mjs +227 -0
- package/skills/lsp-navigation/SKILL.md +15 -3
- package/tools/lsp-navigation.js +466 -79
- package/tools/lsp-navigation.ts +587 -85
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Go Reliability
|
|
2
|
+
# Detects process-terminating log calls in non-boundary code.
|
|
3
|
+
id: go-log-fatal
|
|
4
|
+
name: log.Fatal / log.Panic Call
|
|
5
|
+
severity: warning
|
|
6
|
+
category: reliability
|
|
7
|
+
defect_class: safety
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: go
|
|
10
|
+
|
|
11
|
+
message: "log.Fatal/log.Panic terminates execution — prefer returning errors from core logic"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
`log.Fatal*` exits the process and `log.Panic*` panics. In shared/internal logic,
|
|
15
|
+
this makes control flow hard to test and recover.
|
|
16
|
+
|
|
17
|
+
✅ FIX: return errors upward and let program entrypoints decide termination policy.
|
|
18
|
+
|
|
19
|
+
query: |
|
|
20
|
+
(call_expression
|
|
21
|
+
function: (selector_expression
|
|
22
|
+
operand: (identifier) @PKG
|
|
23
|
+
field: (field_identifier) @METHOD)
|
|
24
|
+
arguments: (argument_list) @ARGS)
|
|
25
|
+
(#eq? @PKG "log")
|
|
26
|
+
(#match? @METHOD "^(Fatal|Fatalf|Fatalln|Panic|Panicf|Panicln)$")
|
|
27
|
+
|
|
28
|
+
metavars:
|
|
29
|
+
- PKG
|
|
30
|
+
- METHOD
|
|
31
|
+
- ARGS
|
|
32
|
+
|
|
33
|
+
has_fix: false
|
|
34
|
+
|
|
35
|
+
tags:
|
|
36
|
+
- go
|
|
37
|
+
- reliability
|
|
38
|
+
- testability
|
|
39
|
+
|
|
40
|
+
examples:
|
|
41
|
+
bad: |
|
|
42
|
+
if err != nil {
|
|
43
|
+
log.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
good: |
|
|
47
|
+
if err != nil {
|
|
48
|
+
return err
|
|
49
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Go Security
|
|
2
|
+
# Detects file-system sink calls with dynamic path expressions.
|
|
3
|
+
id: go-path-traversal
|
|
4
|
+
name: Path Traversal Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: go
|
|
10
|
+
|
|
11
|
+
message: "Potential path traversal sink — sanitize and constrain file paths"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
File operations with user-controlled paths can access unintended files.
|
|
15
|
+
|
|
16
|
+
✅ FIX: clean/canonicalize paths and enforce a fixed base directory.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call_expression
|
|
20
|
+
function: (selector_expression
|
|
21
|
+
operand: (identifier) @PKG
|
|
22
|
+
field: (field_identifier) @FN)
|
|
23
|
+
arguments: (argument_list
|
|
24
|
+
[(identifier) (binary_expression) (call_expression)] @PATH
|
|
25
|
+
(_)*))
|
|
26
|
+
(#match? @PKG "^(os|ioutil)$")
|
|
27
|
+
(#match? @FN "^(Open|OpenFile|ReadFile|WriteFile|Create|Remove|RemoveAll)$")
|
|
28
|
+
|
|
29
|
+
metavars:
|
|
30
|
+
- PKG
|
|
31
|
+
- FN
|
|
32
|
+
- PATH
|
|
33
|
+
|
|
34
|
+
post_filter: go_path_traversal_sink
|
|
35
|
+
|
|
36
|
+
has_fix: false
|
|
37
|
+
|
|
38
|
+
tags:
|
|
39
|
+
- go
|
|
40
|
+
- security
|
|
41
|
+
- path-traversal
|
|
42
|
+
- cwe-22
|
|
43
|
+
- owasp-a01
|
|
44
|
+
|
|
45
|
+
examples:
|
|
46
|
+
bad: |
|
|
47
|
+
os.ReadFile(base + userPath)
|
|
48
|
+
|
|
49
|
+
good: |
|
|
50
|
+
p := filepath.Clean(userPath)
|
|
51
|
+
os.ReadFile(filepath.Join(baseDir, p))
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Go Concurrency
|
|
2
|
+
# Detects map writes inside goroutines, often indicating unsafe shared mutation.
|
|
3
|
+
id: go-shared-map-write-goroutine
|
|
4
|
+
name: Shared Map Write in Goroutine
|
|
5
|
+
severity: warning
|
|
6
|
+
category: concurrency
|
|
7
|
+
defect_class: async-misuse
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: go
|
|
10
|
+
|
|
11
|
+
message: "Map write inside goroutine may race — guard with synchronization or ownership boundaries"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Native Go maps are not safe for concurrent writes without synchronization.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use sync.Mutex/RWMutex, channels, or sync.Map based on access patterns.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(go_statement
|
|
20
|
+
expression: (call_expression
|
|
21
|
+
function: (func_literal
|
|
22
|
+
body: (block
|
|
23
|
+
(assignment_statement
|
|
24
|
+
left: (index_expression) @MAPWRITE
|
|
25
|
+
right: (_) @VALUE)))
|
|
26
|
+
arguments: (argument_list) @ARGS))
|
|
27
|
+
|
|
28
|
+
metavars:
|
|
29
|
+
- MAPWRITE
|
|
30
|
+
- VALUE
|
|
31
|
+
- ARGS
|
|
32
|
+
|
|
33
|
+
cwe:
|
|
34
|
+
- CWE-362
|
|
35
|
+
owasp:
|
|
36
|
+
- A09
|
|
37
|
+
confidence: medium
|
|
38
|
+
|
|
39
|
+
has_fix: false
|
|
40
|
+
|
|
41
|
+
tags:
|
|
42
|
+
- go
|
|
43
|
+
- concurrency
|
|
44
|
+
- map
|
|
45
|
+
- race
|
|
46
|
+
|
|
47
|
+
examples:
|
|
48
|
+
bad: |
|
|
49
|
+
go func() { m[key] = value }()
|
|
50
|
+
|
|
51
|
+
good: |
|
|
52
|
+
mu.Lock()
|
|
53
|
+
m[key] = value
|
|
54
|
+
mu.Unlock()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Go Security
|
|
2
|
+
# Detects SQL query APIs fed by fmt.Sprintf/format-composed strings.
|
|
3
|
+
id: go-sql-injection
|
|
4
|
+
name: SQL Injection Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: go
|
|
10
|
+
|
|
11
|
+
message: "Potential SQL injection sink — use parameterized queries"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Building SQL with string formatting before Query/Exec can enable injection.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use placeholders and bind parameters in Query/Exec methods.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call_expression
|
|
20
|
+
function: (selector_expression
|
|
21
|
+
field: (field_identifier) @DBFN)
|
|
22
|
+
arguments: (argument_list
|
|
23
|
+
(call_expression
|
|
24
|
+
function: (selector_expression
|
|
25
|
+
operand: (identifier) @FMTPKG
|
|
26
|
+
field: (field_identifier) @FMTFN)
|
|
27
|
+
arguments: (argument_list) @FMTARGS)
|
|
28
|
+
(_)*))
|
|
29
|
+
(#match? @DBFN "^(Query|QueryContext|QueryRow|QueryRowContext|Exec|ExecContext)$")
|
|
30
|
+
(#eq? @FMTPKG "fmt")
|
|
31
|
+
(#eq? @FMTFN "Sprintf")
|
|
32
|
+
|
|
33
|
+
metavars:
|
|
34
|
+
- DBFN
|
|
35
|
+
- FMTPKG
|
|
36
|
+
- FMTFN
|
|
37
|
+
- FMTARGS
|
|
38
|
+
|
|
39
|
+
post_filter: go_sql_injection_sink
|
|
40
|
+
|
|
41
|
+
has_fix: false
|
|
42
|
+
|
|
43
|
+
tags:
|
|
44
|
+
- go
|
|
45
|
+
- security
|
|
46
|
+
- sql-injection
|
|
47
|
+
- cwe-89
|
|
48
|
+
- owasp-a03
|
|
49
|
+
|
|
50
|
+
examples:
|
|
51
|
+
bad: |
|
|
52
|
+
db.Query(fmt.Sprintf("SELECT * FROM users WHERE id=%s", userID))
|
|
53
|
+
|
|
54
|
+
good: |
|
|
55
|
+
db.Query("SELECT * FROM users WHERE id=$1", userID)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Go Security
|
|
2
|
+
# Detects weak cryptographic hash package usage.
|
|
3
|
+
id: go-weak-hash
|
|
4
|
+
name: Weak Hash Primitive
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: go
|
|
10
|
+
|
|
11
|
+
message: "Weak hash primitive detected (md5/sha1) — use sha256+ for security-sensitive contexts"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
md5/sha1 are not suitable for security-sensitive hashing.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use crypto/sha256 or stronger alternatives.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call_expression
|
|
20
|
+
function: (selector_expression
|
|
21
|
+
operand: (identifier) @PKG
|
|
22
|
+
field: (field_identifier) @FN)
|
|
23
|
+
arguments: (argument_list) @ARGS)
|
|
24
|
+
(#match? @PKG "^(md5|sha1)$")
|
|
25
|
+
(#match? @FN "^(New|Sum)$")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- PKG
|
|
29
|
+
- FN
|
|
30
|
+
- ARGS
|
|
31
|
+
|
|
32
|
+
cwe:
|
|
33
|
+
- CWE-327
|
|
34
|
+
owasp:
|
|
35
|
+
- A02
|
|
36
|
+
confidence: high
|
|
37
|
+
|
|
38
|
+
has_fix: false
|
|
39
|
+
|
|
40
|
+
tags:
|
|
41
|
+
- go
|
|
42
|
+
- security
|
|
43
|
+
- crypto
|
|
44
|
+
- weak-hash
|
|
45
|
+
|
|
46
|
+
examples:
|
|
47
|
+
bad: |
|
|
48
|
+
sum := md5.Sum(data)
|
|
49
|
+
|
|
50
|
+
good: |
|
|
51
|
+
sum := sha256.Sum256(data)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects shell command execution sinks that commonly lead to command injection.
|
|
3
|
+
id: python-command-injection
|
|
4
|
+
name: Command Injection Sink
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Potential command injection sink — avoid shell execution with dynamic input"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Calling shell execution APIs with user-controlled data can lead to command injection.
|
|
15
|
+
|
|
16
|
+
✅ FIX: avoid shell execution; prefer argument arrays and strict input allowlists.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list) @ARGS)
|
|
24
|
+
(#eq? @MOD "os")
|
|
25
|
+
(#match? @FN "^(system|popen)$")
|
|
26
|
+
|
|
27
|
+
(call
|
|
28
|
+
function: (attribute
|
|
29
|
+
object: (identifier) @MOD
|
|
30
|
+
attribute: (identifier) @FN)
|
|
31
|
+
arguments: (argument_list
|
|
32
|
+
(keyword_argument
|
|
33
|
+
name: (identifier) @KW
|
|
34
|
+
value: (true))))
|
|
35
|
+
(#eq? @MOD "subprocess")
|
|
36
|
+
(#match? @FN "^(run|Popen|call|check_output|check_call)$")
|
|
37
|
+
(#eq? @KW "shell")
|
|
38
|
+
|
|
39
|
+
metavars:
|
|
40
|
+
- MOD
|
|
41
|
+
- FN
|
|
42
|
+
- ARGS
|
|
43
|
+
- KW
|
|
44
|
+
|
|
45
|
+
post_filter: py_command_injection_sink
|
|
46
|
+
|
|
47
|
+
has_fix: false
|
|
48
|
+
|
|
49
|
+
tags:
|
|
50
|
+
- python
|
|
51
|
+
- security
|
|
52
|
+
- command-injection
|
|
53
|
+
- cwe-78
|
|
54
|
+
- owasp-a03
|
|
55
|
+
|
|
56
|
+
examples:
|
|
57
|
+
bad: |
|
|
58
|
+
import os
|
|
59
|
+
os.system(user_input)
|
|
60
|
+
|
|
61
|
+
good: |
|
|
62
|
+
import subprocess
|
|
63
|
+
subprocess.run(["git", "status"], check=True)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects unsafe deserialization APIs.
|
|
3
|
+
id: python-insecure-deserialization
|
|
4
|
+
name: Insecure Deserialization
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Potential insecure deserialization sink — avoid unsafe loaders"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Deserializing untrusted input with pickle/yaml unsafe loaders can lead to code execution.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use safe parsing formats or safe loaders and strict schema validation.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list (_) @DATA))
|
|
24
|
+
(#match? @MOD "^(pickle|yaml)$")
|
|
25
|
+
(#match? @FN "^(load|loads|unsafe_load)$")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- MOD
|
|
29
|
+
- FN
|
|
30
|
+
- DATA
|
|
31
|
+
|
|
32
|
+
post_filter: py_insecure_deserialization_sink
|
|
33
|
+
|
|
34
|
+
has_fix: false
|
|
35
|
+
|
|
36
|
+
tags:
|
|
37
|
+
- python
|
|
38
|
+
- security
|
|
39
|
+
- deserialization
|
|
40
|
+
- cwe-502
|
|
41
|
+
- owasp-a08
|
|
42
|
+
|
|
43
|
+
examples:
|
|
44
|
+
bad: |
|
|
45
|
+
obj = pickle.loads(request_body)
|
|
46
|
+
|
|
47
|
+
good: |
|
|
48
|
+
obj = json.loads(request_body)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects non-cryptographic randomness API usage in security-sensitive code paths.
|
|
3
|
+
id: python-insecure-random
|
|
4
|
+
name: Insecure Randomness
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Insecure randomness source detected — use secrets or os.urandom for security-sensitive values"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
random.* is deterministic and unsuitable for security-sensitive tokens or secrets.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use secrets.token_urlsafe, secrets.randbelow, or os.urandom.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list) @ARGS)
|
|
24
|
+
(#eq? @MOD "random")
|
|
25
|
+
(#match? @FN "^(random|randint|randrange|choice|choices)$")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- MOD
|
|
29
|
+
- FN
|
|
30
|
+
- ARGS
|
|
31
|
+
|
|
32
|
+
cwe:
|
|
33
|
+
- CWE-330
|
|
34
|
+
owasp:
|
|
35
|
+
- A02
|
|
36
|
+
confidence: medium
|
|
37
|
+
|
|
38
|
+
has_fix: false
|
|
39
|
+
|
|
40
|
+
tags:
|
|
41
|
+
- python
|
|
42
|
+
- security
|
|
43
|
+
- randomness
|
|
44
|
+
- weak-prng
|
|
45
|
+
|
|
46
|
+
examples:
|
|
47
|
+
bad: |
|
|
48
|
+
token = random.randint(100000, 999999)
|
|
49
|
+
|
|
50
|
+
good: |
|
|
51
|
+
token = secrets.randbelow(900000) + 100000
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects file access APIs used with dynamic path expressions.
|
|
3
|
+
id: python-path-traversal
|
|
4
|
+
name: Path Traversal Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Potential path traversal sink — sanitize and constrain file paths"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
File operations with user-controlled paths can access unintended files.
|
|
15
|
+
|
|
16
|
+
✅ FIX: normalize paths and enforce a fixed base directory allowlist.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
[
|
|
20
|
+
(call
|
|
21
|
+
function: (identifier) @FN
|
|
22
|
+
arguments: (argument_list
|
|
23
|
+
[(identifier) (binary_operator) (call)] @PATH))
|
|
24
|
+
(call
|
|
25
|
+
function: (attribute
|
|
26
|
+
object: (identifier) @MOD
|
|
27
|
+
attribute: (identifier) @FN)
|
|
28
|
+
arguments: (argument_list
|
|
29
|
+
[(identifier) (binary_operator) (call)] @PATH))
|
|
30
|
+
]
|
|
31
|
+
(#match? @FN "^(open|read_text|read_bytes|write_text|write_bytes|remove|unlink|rmdir)$")
|
|
32
|
+
|
|
33
|
+
metavars:
|
|
34
|
+
- MOD
|
|
35
|
+
- FN
|
|
36
|
+
- PATH
|
|
37
|
+
|
|
38
|
+
post_filter: py_path_traversal_sink
|
|
39
|
+
|
|
40
|
+
has_fix: false
|
|
41
|
+
|
|
42
|
+
tags:
|
|
43
|
+
- python
|
|
44
|
+
- security
|
|
45
|
+
- path-traversal
|
|
46
|
+
- cwe-22
|
|
47
|
+
- owasp-a01
|
|
48
|
+
|
|
49
|
+
examples:
|
|
50
|
+
bad: |
|
|
51
|
+
open(base + user_path)
|
|
52
|
+
|
|
53
|
+
good: |
|
|
54
|
+
safe = os.path.normpath(user_path)
|
|
55
|
+
open(os.path.join(BASE_DIR, safe))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects dynamic SQL passed into execute-like APIs.
|
|
3
|
+
id: python-sql-injection
|
|
4
|
+
name: SQL Injection Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Potential SQL injection sink — use parameterized queries"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Dynamic SQL strings passed to execute-style APIs can enable SQL injection.
|
|
15
|
+
|
|
16
|
+
✅ FIX: use placeholders and parameter binding instead of string composition.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
attribute: (identifier) @FN)
|
|
22
|
+
arguments: (argument_list
|
|
23
|
+
[(binary_operator) (identifier) (call)] @SQL
|
|
24
|
+
(_)*))
|
|
25
|
+
(#match? @FN "^(execute|executemany|query|raw)$")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- FN
|
|
29
|
+
- SQL
|
|
30
|
+
|
|
31
|
+
post_filter: py_sql_injection_sink
|
|
32
|
+
|
|
33
|
+
has_fix: false
|
|
34
|
+
|
|
35
|
+
tags:
|
|
36
|
+
- python
|
|
37
|
+
- security
|
|
38
|
+
- sql-injection
|
|
39
|
+
- cwe-89
|
|
40
|
+
- owasp-a03
|
|
41
|
+
|
|
42
|
+
examples:
|
|
43
|
+
bad: |
|
|
44
|
+
cursor.execute("SELECT * FROM users WHERE id = " + user_id)
|
|
45
|
+
|
|
46
|
+
good: |
|
|
47
|
+
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects user-controlled URL usage in outbound HTTP request sinks.
|
|
3
|
+
id: python-ssrf
|
|
4
|
+
name: SSRF Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Potential SSRF sink — validate/allowlist outbound URLs"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Outbound HTTP calls with untrusted URLs can allow server-side request forgery.
|
|
15
|
+
|
|
16
|
+
✅ FIX: enforce allowlisted hosts/schemes and reject private/internal targets.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list
|
|
24
|
+
[(identifier) (subscript) (call)] @URL))
|
|
25
|
+
(#eq? @MOD "requests")
|
|
26
|
+
(#match? @FN "^(get|post|put|patch|delete|request|head|options)$")
|
|
27
|
+
|
|
28
|
+
metavars:
|
|
29
|
+
- MOD
|
|
30
|
+
- FN
|
|
31
|
+
- URL
|
|
32
|
+
|
|
33
|
+
post_filter: py_ssrf_sink
|
|
34
|
+
|
|
35
|
+
has_fix: false
|
|
36
|
+
|
|
37
|
+
tags:
|
|
38
|
+
- python
|
|
39
|
+
- security
|
|
40
|
+
- ssrf
|
|
41
|
+
- cwe-918
|
|
42
|
+
- owasp-a10
|
|
43
|
+
|
|
44
|
+
examples:
|
|
45
|
+
bad: |
|
|
46
|
+
requests.get(user_url)
|
|
47
|
+
|
|
48
|
+
good: |
|
|
49
|
+
if host in ALLOWLIST:
|
|
50
|
+
requests.get(url)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Python Concurrency
|
|
2
|
+
# Detects thread creation sites where shared state safety should be reviewed.
|
|
3
|
+
id: python-thread-global-write
|
|
4
|
+
name: Threaded Shared State Risk
|
|
5
|
+
severity: warning
|
|
6
|
+
category: concurrency
|
|
7
|
+
defect_class: async-misuse
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Thread creation detected — ensure shared state mutations are synchronized"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
Creating threads in code paths that touch shared mutable state can introduce races.
|
|
15
|
+
|
|
16
|
+
✅ FIX: guard shared state with Lock/RLock or redesign ownership boundaries.
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list) @ARGS)
|
|
24
|
+
(#eq? @MOD "threading")
|
|
25
|
+
(#eq? @FN "Thread")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- MOD
|
|
29
|
+
- FN
|
|
30
|
+
- ARGS
|
|
31
|
+
|
|
32
|
+
cwe:
|
|
33
|
+
- CWE-362
|
|
34
|
+
owasp:
|
|
35
|
+
- A09
|
|
36
|
+
confidence: medium
|
|
37
|
+
|
|
38
|
+
has_fix: false
|
|
39
|
+
|
|
40
|
+
tags:
|
|
41
|
+
- python
|
|
42
|
+
- concurrency
|
|
43
|
+
- shared-state
|
|
44
|
+
- threading
|
|
45
|
+
|
|
46
|
+
examples:
|
|
47
|
+
bad: |
|
|
48
|
+
counter = 0
|
|
49
|
+
def worker():
|
|
50
|
+
global counter
|
|
51
|
+
counter += 1
|
|
52
|
+
threading.Thread(target=worker).start()
|
|
53
|
+
|
|
54
|
+
good: |
|
|
55
|
+
lock = threading.Lock()
|
|
56
|
+
def worker():
|
|
57
|
+
with lock:
|
|
58
|
+
update_state()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python Security
|
|
2
|
+
# Detects weak cryptographic hash primitives.
|
|
3
|
+
id: python-weak-hash
|
|
4
|
+
name: Weak Hash Primitive
|
|
5
|
+
severity: warning
|
|
6
|
+
category: security
|
|
7
|
+
defect_class: injection
|
|
8
|
+
inline_tier: warning
|
|
9
|
+
language: python
|
|
10
|
+
|
|
11
|
+
message: "Weak hash primitive detected (MD5/SHA1) — use SHA-256+ for security-sensitive contexts"
|
|
12
|
+
|
|
13
|
+
description: |
|
|
14
|
+
MD5 and SHA1 are collision-prone and not suitable for security-sensitive hashing.
|
|
15
|
+
|
|
16
|
+
✅ FIX: prefer hashlib.sha256/sha512 (or stronger, context-appropriate algorithms).
|
|
17
|
+
|
|
18
|
+
query: |
|
|
19
|
+
(call
|
|
20
|
+
function: (attribute
|
|
21
|
+
object: (identifier) @MOD
|
|
22
|
+
attribute: (identifier) @FN)
|
|
23
|
+
arguments: (argument_list) @ARGS)
|
|
24
|
+
(#eq? @MOD "hashlib")
|
|
25
|
+
(#match? @FN "^(md5|sha1)$")
|
|
26
|
+
|
|
27
|
+
metavars:
|
|
28
|
+
- MOD
|
|
29
|
+
- FN
|
|
30
|
+
- ARGS
|
|
31
|
+
|
|
32
|
+
cwe:
|
|
33
|
+
- CWE-327
|
|
34
|
+
owasp:
|
|
35
|
+
- A02
|
|
36
|
+
confidence: high
|
|
37
|
+
|
|
38
|
+
has_fix: false
|
|
39
|
+
|
|
40
|
+
tags:
|
|
41
|
+
- python
|
|
42
|
+
- security
|
|
43
|
+
- crypto
|
|
44
|
+
- weak-hash
|
|
45
|
+
|
|
46
|
+
examples:
|
|
47
|
+
bad: |
|
|
48
|
+
digest = hashlib.md5(data).hexdigest()
|
|
49
|
+
|
|
50
|
+
good: |
|
|
51
|
+
digest = hashlib.sha256(data).hexdigest()
|