pi-lens 2.2.9 → 3.0.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/CHANGELOG.md +198 -0
- package/README.md +709 -519
- package/clients/__tests__/file-time.test.js +216 -0
- package/clients/__tests__/file-time.test.ts +276 -0
- package/clients/__tests__/format-service.test.js +245 -0
- package/clients/__tests__/format-service.test.ts +339 -0
- package/clients/__tests__/formatters.test.js +271 -0
- package/clients/__tests__/formatters.test.ts +401 -0
- package/clients/amain-types.js +164 -0
- package/clients/amain-types.ts +165 -0
- package/clients/architect-client.js +56 -12
- package/clients/architect-client.ts +81 -16
- package/clients/ast-grep-client.js +2 -2
- package/clients/ast-grep-client.ts +14 -39
- package/clients/ast-grep-parser.ts +1 -1
- package/clients/ast-grep-rule-manager.js +8 -0
- package/clients/ast-grep-rule-manager.ts +10 -1
- package/clients/ast-grep-types.js +9 -0
- package/clients/ast-grep-types.ts +106 -0
- package/clients/auto-loop.js +10 -0
- package/clients/auto-loop.ts +14 -1
- package/clients/biome-client.js +81 -19
- package/clients/biome-client.ts +103 -22
- package/clients/bus/bus.js +191 -0
- package/clients/bus/bus.ts +251 -0
- package/clients/bus/events.js +214 -0
- package/clients/bus/events.ts +279 -0
- package/clients/bus/index.js +8 -0
- package/clients/bus/index.ts +9 -0
- package/clients/bus/integration.js +158 -0
- package/clients/bus/integration.ts +214 -0
- package/clients/complexity-client.js +13 -7
- package/clients/complexity-client.ts +13 -7
- package/clients/config-validator.js +465 -0
- package/clients/config-validator.ts +558 -0
- package/clients/dependency-checker.js +4 -10
- package/clients/dependency-checker.ts +4 -10
- package/clients/dispatch/__tests__/autofix-integration.test.js +245 -0
- package/clients/dispatch/__tests__/autofix-integration.test.ts +300 -0
- package/clients/dispatch/__tests__/runner-registration.test.js +236 -0
- package/clients/dispatch/__tests__/runner-registration.test.ts +282 -0
- package/clients/dispatch/bus-dispatcher.js +177 -0
- package/clients/dispatch/bus-dispatcher.ts +251 -0
- package/clients/dispatch/dispatcher.edge.test.js +82 -0
- package/clients/dispatch/dispatcher.edge.test.ts +100 -0
- package/clients/dispatch/dispatcher.format.test.js +46 -0
- package/clients/dispatch/dispatcher.format.test.ts +58 -0
- package/clients/dispatch/dispatcher.inline.test.js +74 -0
- package/clients/dispatch/dispatcher.inline.test.ts +93 -0
- package/clients/dispatch/dispatcher.js +19 -53
- package/clients/dispatch/dispatcher.ts +20 -67
- package/clients/dispatch/plan.js +9 -4
- package/clients/dispatch/plan.ts +9 -4
- package/clients/dispatch/runners/architect.js +21 -7
- package/clients/dispatch/runners/architect.test.js +138 -0
- package/clients/dispatch/runners/architect.test.ts +162 -0
- package/clients/dispatch/runners/architect.ts +22 -7
- package/clients/dispatch/runners/ast-grep-napi.js +462 -0
- package/clients/dispatch/runners/ast-grep-napi.test.js +111 -0
- package/clients/dispatch/runners/ast-grep-napi.test.ts +133 -0
- package/clients/dispatch/runners/ast-grep-napi.ts +506 -0
- package/clients/dispatch/runners/ast-grep.js +62 -19
- package/clients/dispatch/runners/ast-grep.ts +70 -18
- package/clients/dispatch/runners/biome.js +29 -53
- package/clients/dispatch/runners/biome.ts +29 -63
- package/clients/dispatch/runners/config-validation.js +67 -0
- package/clients/dispatch/runners/config-validation.ts +82 -0
- package/clients/dispatch/runners/go-vet.js +4 -28
- package/clients/dispatch/runners/go-vet.ts +4 -32
- package/clients/dispatch/runners/index.js +30 -10
- package/clients/dispatch/runners/index.ts +30 -10
- package/clients/dispatch/runners/oxlint.js +141 -0
- package/clients/dispatch/runners/oxlint.test.js +230 -0
- package/clients/dispatch/runners/oxlint.test.ts +303 -0
- package/clients/dispatch/runners/oxlint.ts +175 -0
- package/clients/dispatch/runners/pyright.js +40 -70
- package/clients/dispatch/runners/pyright.test.js +16 -2
- package/clients/dispatch/runners/pyright.test.ts +14 -2
- package/clients/dispatch/runners/pyright.ts +48 -91
- package/clients/dispatch/runners/python-slop.js +97 -0
- package/clients/dispatch/runners/python-slop.test.js +203 -0
- package/clients/dispatch/runners/python-slop.test.ts +298 -0
- package/clients/dispatch/runners/python-slop.ts +124 -0
- package/clients/dispatch/runners/ruff.js +18 -71
- package/clients/dispatch/runners/ruff.ts +19 -79
- package/clients/dispatch/runners/rust-clippy.js +28 -32
- package/clients/dispatch/runners/rust-clippy.ts +29 -31
- package/clients/dispatch/runners/scan_codebase.test.js +89 -0
- package/clients/dispatch/runners/scan_codebase.test.ts +105 -0
- package/clients/dispatch/runners/shellcheck.js +147 -0
- package/clients/dispatch/runners/shellcheck.test.js +98 -0
- package/clients/dispatch/runners/shellcheck.test.ts +129 -0
- package/clients/dispatch/runners/shellcheck.ts +188 -0
- package/clients/dispatch/runners/similarity.js +230 -0
- package/clients/dispatch/runners/similarity.ts +339 -0
- package/clients/dispatch/runners/spellcheck.js +106 -0
- package/clients/dispatch/runners/spellcheck.test.js +158 -0
- package/clients/dispatch/runners/spellcheck.test.ts +214 -0
- package/clients/dispatch/runners/spellcheck.ts +136 -0
- package/clients/dispatch/runners/tree-sitter.js +107 -0
- package/clients/dispatch/runners/tree-sitter.ts +135 -0
- package/clients/dispatch/runners/ts-lsp.js +104 -33
- package/clients/dispatch/runners/ts-lsp.ts +120 -38
- package/clients/dispatch/runners/ts-slop.js +113 -0
- package/clients/dispatch/runners/ts-slop.test.js +180 -0
- package/clients/dispatch/runners/ts-slop.test.ts +230 -0
- package/clients/dispatch/runners/ts-slop.ts +142 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +134 -0
- package/clients/dispatch/runners/utils/diagnostic-parsers.ts +186 -0
- package/clients/dispatch/runners/utils/runner-helpers.js +115 -0
- package/clients/dispatch/runners/utils/runner-helpers.ts +167 -0
- package/clients/dispatch/runners/utils.js +2 -4
- package/clients/dispatch/runners/utils.ts +2 -4
- package/clients/dispatch/types.ts +1 -1
- package/clients/dispatch/utils/format-utils.js +49 -0
- package/clients/dispatch/utils/format-utils.ts +60 -0
- package/clients/dogfood.test.js +201 -0
- package/clients/dogfood.test.ts +269 -0
- package/clients/file-time.js +152 -0
- package/clients/file-time.ts +208 -0
- package/clients/file-utils.js +40 -0
- package/clients/file-utils.ts +44 -0
- package/clients/fix-scanners.js +10 -20
- package/clients/fix-scanners.ts +10 -22
- package/clients/format-service.js +172 -0
- package/clients/format-service.ts +254 -0
- package/clients/formatters.js +435 -0
- package/clients/formatters.ts +508 -0
- package/clients/go-client.js +5 -14
- package/clients/go-client.ts +5 -13
- package/clients/installer/index.js +356 -0
- package/clients/installer/index.ts +426 -0
- package/clients/jscpd-client.js +11 -9
- package/clients/jscpd-client.ts +12 -8
- package/clients/knip-client.js +3 -7
- package/clients/knip-client.ts +3 -6
- package/clients/lsp/__tests__/client.test.js +325 -0
- package/clients/lsp/__tests__/client.test.ts +434 -0
- package/clients/lsp/__tests__/config.test.js +166 -0
- package/clients/lsp/__tests__/config.test.ts +209 -0
- package/clients/lsp/__tests__/error-recovery.test.js +213 -0
- package/clients/lsp/__tests__/error-recovery.test.ts +279 -0
- package/clients/lsp/__tests__/integration.test.js +127 -0
- package/clients/lsp/__tests__/integration.test.ts +160 -0
- package/clients/lsp/__tests__/launch.test.js +260 -0
- package/clients/lsp/__tests__/launch.test.ts +329 -0
- package/clients/lsp/__tests__/server.test.js +259 -0
- package/clients/lsp/__tests__/server.test.ts +332 -0
- package/clients/lsp/__tests__/service.test.js +417 -0
- package/clients/lsp/__tests__/service.test.ts +499 -0
- package/clients/lsp/client.js +235 -0
- package/clients/lsp/client.ts +328 -0
- package/clients/lsp/config.js +115 -0
- package/clients/lsp/config.ts +149 -0
- package/clients/lsp/index.js +222 -0
- package/clients/lsp/index.ts +280 -0
- package/clients/lsp/installer/index.js +391 -0
- package/clients/lsp/interactive-install.js +210 -0
- package/clients/lsp/interactive-install.ts +251 -0
- package/clients/lsp/language.js +170 -0
- package/clients/lsp/language.ts +216 -0
- package/clients/lsp/launch.js +174 -0
- package/clients/lsp/launch.ts +240 -0
- package/clients/lsp/lsp/launch.js +116 -0
- package/clients/lsp/lsp/server.js +532 -0
- package/clients/lsp/lsp-index.js +10 -0
- package/clients/lsp/lsp-index.ts +11 -0
- package/clients/lsp/path-utils.js +48 -0
- package/clients/lsp/path-utils.ts +52 -0
- package/clients/lsp/server.js +615 -0
- package/clients/lsp/server.ts +800 -0
- package/clients/lsp/test-py-spawn/requirements.txt +1 -0
- package/clients/lsp/test-py-spawn/test.py +3 -0
- package/clients/lsp/test-py-svc/requirements.txt +1 -0
- package/clients/lsp/test-py-svc/test.py +3 -0
- package/clients/lsp/test-python-project/requirements.txt +1 -0
- package/clients/lsp/test-python-project/test.py +5 -0
- package/clients/metrics-history.js +2 -2
- package/clients/metrics-history.ts +2 -2
- package/clients/production-readiness.js +522 -0
- package/clients/production-readiness.ts +556 -0
- package/clients/project-index.js +255 -0
- package/clients/project-index.ts +383 -0
- package/clients/project-metadata.js +531 -0
- package/clients/project-metadata.ts +624 -0
- package/clients/ruff-client.js +56 -16
- package/clients/ruff-client.ts +72 -15
- package/clients/runner-tracker.js +152 -0
- package/clients/runner-tracker.ts +213 -0
- package/clients/rust-client.js +4 -11
- package/clients/rust-client.ts +5 -11
- package/clients/safe-spawn.js +96 -0
- package/clients/safe-spawn.ts +128 -0
- package/clients/scan-architectural-debt.js +3 -6
- package/clients/scan-architectural-debt.ts +3 -6
- package/clients/scan-utils.js +5 -20
- package/clients/scan-utils.ts +5 -29
- package/clients/secrets-scanner.js +3 -17
- package/clients/secrets-scanner.ts +4 -20
- package/clients/services/__tests__/effect-integration.test.js +86 -0
- package/clients/services/__tests__/effect-integration.test.ts +111 -0
- package/clients/services/effect-integration.js +194 -0
- package/clients/services/effect-integration.ts +268 -0
- package/clients/services/index.js +7 -0
- package/clients/services/index.ts +8 -0
- package/clients/services/runner-service.js +105 -0
- package/clients/services/runner-service.ts +179 -0
- package/clients/sg-runner.js +87 -13
- package/clients/sg-runner.ts +97 -13
- package/clients/state-matrix.js +160 -0
- package/clients/state-matrix.ts +202 -0
- package/clients/subprocess-client.js +10 -9
- package/clients/subprocess-client.ts +10 -8
- package/clients/test-runner-client.js +3 -7
- package/clients/test-runner-client.ts +3 -6
- package/clients/tool-availability.js +4 -10
- package/clients/tool-availability.ts +4 -9
- package/clients/tree-sitter-client.js +564 -0
- package/clients/tree-sitter-client.ts +797 -0
- package/clients/tree-sitter-query-loader.js +355 -0
- package/clients/tree-sitter-query-loader.ts +425 -0
- package/clients/type-coverage-client.js +3 -7
- package/clients/type-coverage-client.ts +3 -6
- package/clients/typescript-client.codefix.test.js +157 -0
- package/clients/typescript-client.codefix.test.ts +186 -0
- package/clients/typescript-client.js +43 -0
- package/clients/typescript-client.ts +98 -0
- package/commands/booboo.js +799 -219
- package/commands/booboo.ts +1004 -225
- package/commands/clients/ast-grep-client.js +250 -0
- package/commands/clients/ast-grep-parser.js +86 -0
- package/commands/clients/ast-grep-rule-manager.js +91 -0
- package/commands/clients/ast-grep-types.js +9 -0
- package/commands/clients/biome-client.js +380 -0
- package/commands/clients/complexity-client.js +667 -0
- package/commands/clients/file-kinds.js +177 -0
- package/commands/clients/file-utils.js +40 -0
- package/commands/clients/jscpd-client.js +169 -0
- package/commands/clients/knip-client.js +211 -0
- package/commands/clients/ruff-client.js +297 -0
- package/commands/clients/safe-spawn.js +88 -0
- package/commands/clients/scan-utils.js +83 -0
- package/commands/clients/sg-runner.js +190 -0
- package/commands/clients/types.js +11 -0
- package/commands/clients/typescript-client.js +505 -0
- package/commands/fix-from-booboo.js +398 -0
- package/commands/fix-from-booboo.ts +485 -0
- package/commands/fix-simplified.js +618 -0
- package/commands/fix-simplified.ts +768 -0
- package/commands/rate.js +10 -14
- package/commands/rate.ts +9 -16
- package/default-architect.yaml +59 -15
- package/index.ts +342 -429
- package/package.json +16 -3
- package/rules/ast-grep-rules/rules/empty-catch.yml +38 -13
- package/rules/ast-grep-rules/rules/no-array-constructor.yml +1 -0
- package/rules/ast-grep-rules/rules/no-debugger.yml +2 -0
- package/rules/python-slop-rules/.sgconfig.yml +4 -0
- package/rules/python-slop-rules/rules/slop-rules.yml +647 -0
- package/rules/tree-sitter-queries/python/bare-except.yml +54 -0
- package/rules/tree-sitter-queries/python/eval-exec.yml +50 -0
- package/rules/tree-sitter-queries/python/is-vs-equals.yml +60 -0
- package/rules/tree-sitter-queries/python/mutable-default-arg.yml +57 -0
- package/rules/tree-sitter-queries/python/unreachable-except.yml +60 -0
- package/rules/tree-sitter-queries/python/wildcard-import.yml +46 -0
- package/rules/tree-sitter-queries/tsx/dangerously-set-inner-html.yml +63 -0
- package/rules/tree-sitter-queries/typescript/await-in-loop.yml +56 -0
- package/rules/tree-sitter-queries/typescript/console-statement.yml +47 -0
- package/rules/tree-sitter-queries/typescript/debugger.yml +47 -0
- package/rules/tree-sitter-queries/typescript/deep-nesting.yml +117 -0
- package/rules/tree-sitter-queries/typescript/deep-promise-chain.yml +73 -0
- package/rules/tree-sitter-queries/typescript/empty-catch.yml +64 -0
- package/rules/tree-sitter-queries/typescript/eval.yml +48 -0
- package/rules/tree-sitter-queries/typescript/hardcoded-secrets.yml +78 -0
- package/rules/tree-sitter-queries/typescript/long-parameter-list.yml +62 -0
- package/rules/tree-sitter-queries/typescript/mixed-async-styles.yml +49 -0
- package/rules/tree-sitter-queries/typescript/nested-ternary.yml +45 -0
- package/rules/ts-slop-rules/.sgconfig.yml +4 -0
- package/rules/ts-slop-rules/rules/in-correct-optional-input-type.yml +10 -0
- package/rules/ts-slop-rules/rules/jwt-no-verify.yml +13 -0
- package/rules/ts-slop-rules/rules/no-architecture-violation.yml +10 -0
- package/rules/ts-slop-rules/rules/no-case-declarations.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dangerously-set-inner-html.yml +10 -0
- package/rules/ts-slop-rules/rules/no-debugger.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-args.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-class-members.yml +10 -0
- package/rules/ts-slop-rules/rules/no-dupe-keys.yml +10 -0
- package/rules/ts-slop-rules/rules/no-eval.yml +13 -0
- package/rules/ts-slop-rules/rules/no-hardcoded-secrets.yml +12 -0
- package/rules/ts-slop-rules/rules/no-implied-eval.yml +12 -0
- package/rules/ts-slop-rules/rules/no-inner-html.yml +13 -0
- package/rules/ts-slop-rules/rules/no-javascript-url.yml +10 -0
- package/rules/ts-slop-rules/rules/no-mutable-default.yml +10 -0
- package/rules/ts-slop-rules/rules/no-nested-links.yml +12 -0
- package/rules/ts-slop-rules/rules/no-new-symbol.yml +10 -0
- package/rules/ts-slop-rules/rules/no-new-wrappers.yml +13 -0
- package/rules/ts-slop-rules/rules/no-open-redirect.yml +16 -0
- package/rules/ts-slop-rules/rules/slop-rules.yml +455 -0
- package/rules/ts-slop-rules/rules/weak-rsa-key.yml +12 -0
- package/skills/ast-grep/SKILL.md +182 -0
- package/clients/dispatch/runners/secrets.js +0 -109
- package/commands/fix.js +0 -244
- package/commands/fix.ts +0 -373
- package/rules/ast-grep-rules/rules/no-lonely-if.yml +0 -13
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Eval/Exec Usage
|
|
2
|
+
# Detects eval() and exec() which are security risks
|
|
3
|
+
id: eval-exec
|
|
4
|
+
name: Eval/Exec Usage
|
|
5
|
+
severity: error
|
|
6
|
+
category: security
|
|
7
|
+
language: python
|
|
8
|
+
|
|
9
|
+
message: "{{FUNC}}() detected — security risk, code injection vulnerability"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
eval() and exec() execute arbitrary Python code and are major
|
|
13
|
+
security vulnerabilities when used with untrusted input.
|
|
14
|
+
|
|
15
|
+
✅ ALTERNATIVES:
|
|
16
|
+
- ast.literal_eval() for parsing literals
|
|
17
|
+
- json.loads() for JSON
|
|
18
|
+
- Proper parsing libraries for complex needs
|
|
19
|
+
|
|
20
|
+
query: |
|
|
21
|
+
(call
|
|
22
|
+
function: (identifier) @FUNC
|
|
23
|
+
(#match? @FUNC "^(eval|exec)$")
|
|
24
|
+
arguments: (argument_list) @ARGS)
|
|
25
|
+
|
|
26
|
+
metavars:
|
|
27
|
+
- FUNC
|
|
28
|
+
- ARGS
|
|
29
|
+
|
|
30
|
+
tags:
|
|
31
|
+
- security
|
|
32
|
+
- xss
|
|
33
|
+
- injection
|
|
34
|
+
|
|
35
|
+
examples:
|
|
36
|
+
bad: |
|
|
37
|
+
user_input = "os.system('rm -rf /')"
|
|
38
|
+
eval(user_input) # DANGEROUS!
|
|
39
|
+
|
|
40
|
+
good: |
|
|
41
|
+
import ast
|
|
42
|
+
import json
|
|
43
|
+
|
|
44
|
+
# For literals: use ast.literal_eval
|
|
45
|
+
data = ast.literal_eval("[1, 2, 3]")
|
|
46
|
+
|
|
47
|
+
# For JSON: use json.loads
|
|
48
|
+
data = json.loads(json_string)
|
|
49
|
+
|
|
50
|
+
has_fix: false
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Is vs Equals for Literals
|
|
2
|
+
# Detects 'is' used with string/int literals (should use '==')
|
|
3
|
+
id: is-vs-equals
|
|
4
|
+
name: Is vs Equals for Literals
|
|
5
|
+
severity: warning
|
|
6
|
+
category: reliability
|
|
7
|
+
language: python
|
|
8
|
+
|
|
9
|
+
message: "Using 'is' with literal — use '==' for value comparison"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
'is' checks identity (same object in memory), not equality.
|
|
13
|
+
For strings and small ints, Python may intern them, making
|
|
14
|
+
'is' appear to work, but it's unreliable and wrong.
|
|
15
|
+
|
|
16
|
+
✅ FIX: Use '==' for value comparison
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
if name == "admin": # GOOD - value comparison
|
|
20
|
+
if count == 0: # GOOD - value comparison
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
query: |
|
|
24
|
+
(comparison_operator
|
|
25
|
+
(identifier)
|
|
26
|
+
("is")
|
|
27
|
+
(string) @LITERAL)
|
|
28
|
+
(comparison_operator
|
|
29
|
+
(identifier)
|
|
30
|
+
("is not")
|
|
31
|
+
(string) @LITERAL)
|
|
32
|
+
(comparison_operator
|
|
33
|
+
(identifier)
|
|
34
|
+
("is")
|
|
35
|
+
(integer) @LITERAL)
|
|
36
|
+
(comparison_operator
|
|
37
|
+
(identifier)
|
|
38
|
+
("is not")
|
|
39
|
+
(integer) @LITERAL)
|
|
40
|
+
|
|
41
|
+
metavars:
|
|
42
|
+
- LITERAL
|
|
43
|
+
|
|
44
|
+
tags:
|
|
45
|
+
- reliability
|
|
46
|
+
- common-mistake
|
|
47
|
+
- python-specific
|
|
48
|
+
|
|
49
|
+
examples:
|
|
50
|
+
bad: |
|
|
51
|
+
if name is "admin": # BAD - identity check
|
|
52
|
+
if count is 0: # BAD - may work by accident
|
|
53
|
+
|
|
54
|
+
good: |
|
|
55
|
+
if name == "admin": # GOOD
|
|
56
|
+
if count == 0: # GOOD
|
|
57
|
+
if obj is None: # OK - None is a singleton
|
|
58
|
+
|
|
59
|
+
has_fix: true
|
|
60
|
+
fix_action: replace_is_with_equals
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Mutable Default Argument
|
|
2
|
+
# Detects mutable defaults in function arguments (common Python pitfall)
|
|
3
|
+
id: mutable-default-arg
|
|
4
|
+
name: Mutable Default Argument
|
|
5
|
+
severity: error
|
|
6
|
+
category: reliability
|
|
7
|
+
language: python
|
|
8
|
+
|
|
9
|
+
message: "Mutable default argument — list/dict/set as default value"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Mutable default arguments are evaluated once at function definition time,
|
|
13
|
+
not each call. This causes unexpected shared state between calls.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Use None as default, create mutable inside function
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
def process(items=None): # GOOD
|
|
19
|
+
if items is None:
|
|
20
|
+
items = []
|
|
21
|
+
items.append(item)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
query: |
|
|
25
|
+
(function_definition
|
|
26
|
+
(parameters
|
|
27
|
+
(default_parameter
|
|
28
|
+
(identifier) @PARAM
|
|
29
|
+
[(list) (dictionary) (set)] @MUTABLE)))
|
|
30
|
+
|
|
31
|
+
metavars:
|
|
32
|
+
- PARAM
|
|
33
|
+
- MUTABLE
|
|
34
|
+
|
|
35
|
+
tags:
|
|
36
|
+
- reliability
|
|
37
|
+
- common-mistake
|
|
38
|
+
- python-specific
|
|
39
|
+
|
|
40
|
+
examples:
|
|
41
|
+
bad: |
|
|
42
|
+
def add_item(item, items=[]): # BAD - shared list!
|
|
43
|
+
items.append(item)
|
|
44
|
+
return items
|
|
45
|
+
|
|
46
|
+
print(add_item(1)) # [1]
|
|
47
|
+
print(add_item(2)) # [1, 2] - unexpected!
|
|
48
|
+
|
|
49
|
+
good: |
|
|
50
|
+
def add_item(item, items=None): # GOOD
|
|
51
|
+
if items is None:
|
|
52
|
+
items = []
|
|
53
|
+
items.append(item)
|
|
54
|
+
return items
|
|
55
|
+
|
|
56
|
+
has_fix: true
|
|
57
|
+
fix_action: convert_to_none_default
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Unreachable Except Clause
|
|
2
|
+
# Detects except clauses that can never be reached
|
|
3
|
+
id: unreachable-except
|
|
4
|
+
name: Unreachable Except Clause
|
|
5
|
+
severity: error
|
|
6
|
+
category: reliability
|
|
7
|
+
language: python
|
|
8
|
+
|
|
9
|
+
message: "Unreachable except clause — earlier except catches all"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
When a bare 'except' or 'except Exception' comes before specific
|
|
13
|
+
exception handlers, those specific handlers are never reached.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Order from specific to general
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
try:
|
|
19
|
+
process()
|
|
20
|
+
except ValueError: # Specific first
|
|
21
|
+
handle_value_error()
|
|
22
|
+
except Exception: # General last
|
|
23
|
+
handle_general()
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
query: |
|
|
27
|
+
(try_statement
|
|
28
|
+
(except_clause
|
|
29
|
+
"except") @GENERAL
|
|
30
|
+
(except_clause
|
|
31
|
+
"except"
|
|
32
|
+
(identifier) @SPECIFIC))
|
|
33
|
+
|
|
34
|
+
metavars:
|
|
35
|
+
- GENERAL
|
|
36
|
+
- SPECIFIC
|
|
37
|
+
|
|
38
|
+
tags:
|
|
39
|
+
- reliability
|
|
40
|
+
- dead-code
|
|
41
|
+
- exceptions
|
|
42
|
+
|
|
43
|
+
examples:
|
|
44
|
+
bad: |
|
|
45
|
+
try:
|
|
46
|
+
process()
|
|
47
|
+
except: # BAD - catches everything
|
|
48
|
+
handle_all()
|
|
49
|
+
except ValueError: # UNREACHABLE!
|
|
50
|
+
handle_value()
|
|
51
|
+
|
|
52
|
+
good: |
|
|
53
|
+
try:
|
|
54
|
+
process()
|
|
55
|
+
except ValueError: # GOOD - specific first
|
|
56
|
+
handle_value()
|
|
57
|
+
except Exception: # GOOD - general last
|
|
58
|
+
handle_general()
|
|
59
|
+
|
|
60
|
+
has_fix: false
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Wildcard Import
|
|
2
|
+
# Detects 'from module import *' which pollutes namespace
|
|
3
|
+
id: wildcard-import
|
|
4
|
+
name: Wildcard Import
|
|
5
|
+
severity: warning
|
|
6
|
+
category: readability
|
|
7
|
+
language: python
|
|
8
|
+
|
|
9
|
+
message: "Wildcard import — pollutes namespace, hard to track origin"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
'from module import *' makes it unclear where names come from
|
|
13
|
+
and can cause name collisions. Explicit is better than implicit.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Import specific names or use module prefix
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from os import path, getenv # GOOD - explicit
|
|
19
|
+
import os # GOOD - namespace preserved
|
|
20
|
+
os.path.join(...) # Clear where this comes from
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
query: |
|
|
24
|
+
(import_from_statement
|
|
25
|
+
module_name: (dotted_name) @MODULE
|
|
26
|
+
(wildcard_import) @WILDCARD)
|
|
27
|
+
|
|
28
|
+
metavars:
|
|
29
|
+
- MODULE
|
|
30
|
+
- WILDCARD
|
|
31
|
+
|
|
32
|
+
tags:
|
|
33
|
+
- readability
|
|
34
|
+
- best-practice
|
|
35
|
+
- imports
|
|
36
|
+
|
|
37
|
+
examples:
|
|
38
|
+
bad: |
|
|
39
|
+
from os import * # BAD - where does path come from?
|
|
40
|
+
from numpy import * # BAD - pollutes namespace
|
|
41
|
+
|
|
42
|
+
good: |
|
|
43
|
+
from os import path, getenv # GOOD - explicit
|
|
44
|
+
import numpy as np # GOOD - namespace preserved
|
|
45
|
+
|
|
46
|
+
has_fix: false
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Dangerously Set Inner HTML
|
|
2
|
+
# Detects dangerouslySetInnerHTML usage (XSS risk)
|
|
3
|
+
id: dangerously-set-inner-html
|
|
4
|
+
name: Dangerously Set Inner HTML
|
|
5
|
+
severity: error
|
|
6
|
+
category: security
|
|
7
|
+
language: tsx
|
|
8
|
+
|
|
9
|
+
message: "dangerouslySetInnerHTML — XSS risk, sanitize user input"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
dangerouslySetInnerHTML allows arbitrary HTML injection.
|
|
13
|
+
This is a major XSS vulnerability if user input is used.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Sanitize HTML with a library like DOMPurify
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import DOMPurify from 'dompurify';
|
|
19
|
+
|
|
20
|
+
<div dangerouslySetInnerHTML={{
|
|
21
|
+
__html: DOMPurify.sanitize(userInput)
|
|
22
|
+
}} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or better, avoid using HTML altogether and use JSX.
|
|
26
|
+
|
|
27
|
+
query: |
|
|
28
|
+
(jsx_attribute
|
|
29
|
+
(property_identifier) @ATTR
|
|
30
|
+
(#match? @ATTR "dangerouslySetInnerHTML"))
|
|
31
|
+
|
|
32
|
+
metavars:
|
|
33
|
+
- ATTR
|
|
34
|
+
|
|
35
|
+
tags:
|
|
36
|
+
- security
|
|
37
|
+
- xss
|
|
38
|
+
- react
|
|
39
|
+
- jsx
|
|
40
|
+
|
|
41
|
+
examples:
|
|
42
|
+
bad: |
|
|
43
|
+
function Component({ userInput }) {
|
|
44
|
+
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
good: |
|
|
48
|
+
import DOMPurify from 'dompurify';
|
|
49
|
+
|
|
50
|
+
function Component({ userInput }) {
|
|
51
|
+
return (
|
|
52
|
+
<div dangerouslySetInnerHTML={{
|
|
53
|
+
__html: DOMPurify.sanitize(userInput)
|
|
54
|
+
}} />
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Or better:
|
|
59
|
+
function Component({ content }) {
|
|
60
|
+
return <div>{content}</div>; // Just use JSX
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
has_fix: false
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Await in Loop
|
|
2
|
+
# Detects sequential await calls inside loops (performance anti-pattern)
|
|
3
|
+
id: await-in-loop
|
|
4
|
+
name: Await in Loop
|
|
5
|
+
severity: warning
|
|
6
|
+
category: performance
|
|
7
|
+
language: typescript
|
|
8
|
+
|
|
9
|
+
message: "Await in loop — sequential execution is slow, use Promise.all()"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Using await inside a loop forces sequential execution, making it O(n) time.
|
|
13
|
+
With Promise.all(), operations run in parallel, making it O(1) time.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Use Promise.all() for parallel execution
|
|
16
|
+
|
|
17
|
+
⚠️ EXCEPTION: If order matters or you need to throttle requests, sequential
|
|
18
|
+
may be intentional. In that case, add a comment explaining why.
|
|
19
|
+
|
|
20
|
+
query: |
|
|
21
|
+
(for_in_statement
|
|
22
|
+
body: (statement_block
|
|
23
|
+
(expression_statement
|
|
24
|
+
(await_expression) @AWAIT)))
|
|
25
|
+
(for_statement
|
|
26
|
+
body: (statement_block
|
|
27
|
+
(expression_statement
|
|
28
|
+
(await_expression) @AWAIT)))
|
|
29
|
+
(while_statement
|
|
30
|
+
body: (statement_block
|
|
31
|
+
(expression_statement
|
|
32
|
+
(await_expression) @AWAIT)))
|
|
33
|
+
|
|
34
|
+
metavars:
|
|
35
|
+
- AWAIT
|
|
36
|
+
|
|
37
|
+
tags:
|
|
38
|
+
- performance
|
|
39
|
+
- async
|
|
40
|
+
- optimization
|
|
41
|
+
|
|
42
|
+
examples:
|
|
43
|
+
bad: |
|
|
44
|
+
// Slow: sequential execution
|
|
45
|
+
for (const id of ids) {
|
|
46
|
+
await fetchUser(id); // Wait, wait, wait...
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
good: |
|
|
50
|
+
// Fast: parallel execution
|
|
51
|
+
await Promise.all(
|
|
52
|
+
ids.map(id => fetchUser(id)) // All at once!
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
has_fix: true
|
|
56
|
+
fix_action: convert_to_promise_all
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Console Statement
|
|
2
|
+
# Detects console.log and friends (debug leftovers)
|
|
3
|
+
id: console-statement
|
|
4
|
+
name: Console Statement
|
|
5
|
+
severity: warning
|
|
6
|
+
category: debugging
|
|
7
|
+
language: typescript
|
|
8
|
+
|
|
9
|
+
message: "{{METHOD}} — remove debug statements before committing"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Console statements are for debugging and should not be in production code.
|
|
13
|
+
|
|
14
|
+
✅ FIX: Remove or use a proper logging library.
|
|
15
|
+
|
|
16
|
+
query: |
|
|
17
|
+
(call_expression
|
|
18
|
+
function: (member_expression
|
|
19
|
+
object: (identifier) @OBJ (#eq? @OBJ "console")
|
|
20
|
+
property: (property_identifier) @METHOD)
|
|
21
|
+
arguments: (arguments) @ARGS)
|
|
22
|
+
|
|
23
|
+
# Post-filter: Exclude debug function names like "dbg"
|
|
24
|
+
post_filter: not_dbg_method
|
|
25
|
+
|
|
26
|
+
metavars:
|
|
27
|
+
- OBJ
|
|
28
|
+
- METHOD
|
|
29
|
+
- ARGS
|
|
30
|
+
|
|
31
|
+
tags:
|
|
32
|
+
- debugging
|
|
33
|
+
- code-quality
|
|
34
|
+
|
|
35
|
+
examples:
|
|
36
|
+
bad: |
|
|
37
|
+
console.log("debug info");
|
|
38
|
+
console.error("error");
|
|
39
|
+
|
|
40
|
+
good: |
|
|
41
|
+
// Use a logging library
|
|
42
|
+
import { logger } from './logger';
|
|
43
|
+
logger.info("info");
|
|
44
|
+
logger.error("error");
|
|
45
|
+
|
|
46
|
+
has_fix: true
|
|
47
|
+
fix_action: remove
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Debugger Statement
|
|
2
|
+
# Detects debugger statements left in code
|
|
3
|
+
id: debugger-statement
|
|
4
|
+
name: Debugger Statement
|
|
5
|
+
severity: warning
|
|
6
|
+
category: debugging
|
|
7
|
+
language: typescript
|
|
8
|
+
|
|
9
|
+
message: "Debugger statement — remove before committing"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Debugger statements pause execution and should never be committed to production.
|
|
13
|
+
|
|
14
|
+
✅ FIX: Remove the debugger statement before committing.
|
|
15
|
+
|
|
16
|
+
If you need to debug, use:
|
|
17
|
+
- console.log() for quick checks
|
|
18
|
+
- IDE debugger for stepping through
|
|
19
|
+
- Add breakpoints in dev tools
|
|
20
|
+
|
|
21
|
+
query: |
|
|
22
|
+
(debugger_statement) @DEBUGGER
|
|
23
|
+
|
|
24
|
+
metavars:
|
|
25
|
+
- DEBUGGER
|
|
26
|
+
|
|
27
|
+
tags:
|
|
28
|
+
- debugging
|
|
29
|
+
- code-quality
|
|
30
|
+
|
|
31
|
+
examples:
|
|
32
|
+
bad: |
|
|
33
|
+
function process() {
|
|
34
|
+
const x = calculate();
|
|
35
|
+
debugger; // ← Remove this!
|
|
36
|
+
return x;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
good: |
|
|
40
|
+
function process() {
|
|
41
|
+
const x = calculate();
|
|
42
|
+
console.log('Debug:', x); // Or just remove
|
|
43
|
+
return x;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
has_fix: true
|
|
47
|
+
fix_action: remove
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Deep Nesting
|
|
2
|
+
# Detects functions with 3+ levels of control structure nesting (if/for/while/try)
|
|
3
|
+
id: deep-nesting
|
|
4
|
+
name: Deep Nesting
|
|
5
|
+
severity: warning
|
|
6
|
+
category: complexity
|
|
7
|
+
language: typescript
|
|
8
|
+
|
|
9
|
+
message: "Deep nesting (3+ levels) — consider early returns or extract functions"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Deeply nested code (if/for/while/try blocks 3+ levels deep) is hard to read and test.
|
|
13
|
+
Flatten by using early returns (guard clauses) or extracting nested logic.
|
|
14
|
+
|
|
15
|
+
query: |
|
|
16
|
+
[
|
|
17
|
+
;; Pattern 1: if inside if inside if
|
|
18
|
+
(statement_block
|
|
19
|
+
(if_statement
|
|
20
|
+
consequence: (statement_block
|
|
21
|
+
(if_statement
|
|
22
|
+
consequence: (statement_block
|
|
23
|
+
(if_statement) @IF_NESTED)))))
|
|
24
|
+
|
|
25
|
+
;; Pattern 2: for inside if inside if
|
|
26
|
+
(statement_block
|
|
27
|
+
(if_statement
|
|
28
|
+
consequence: (statement_block
|
|
29
|
+
(if_statement
|
|
30
|
+
consequence: (statement_block
|
|
31
|
+
(for_statement) @FOR_NESTED)))))
|
|
32
|
+
|
|
33
|
+
;; Pattern 3: while inside if inside if
|
|
34
|
+
(statement_block
|
|
35
|
+
(if_statement
|
|
36
|
+
consequence: (statement_block
|
|
37
|
+
(if_statement
|
|
38
|
+
consequence: (statement_block
|
|
39
|
+
(while_statement) @WHILE_NESTED)))))
|
|
40
|
+
|
|
41
|
+
;; Pattern 4: try inside if inside if
|
|
42
|
+
(statement_block
|
|
43
|
+
(if_statement
|
|
44
|
+
consequence: (statement_block
|
|
45
|
+
(if_statement
|
|
46
|
+
consequence: (statement_block
|
|
47
|
+
(try_statement) @TRY_NESTED)))))
|
|
48
|
+
|
|
49
|
+
;; Pattern 5: if inside for inside if
|
|
50
|
+
(statement_block
|
|
51
|
+
(if_statement
|
|
52
|
+
consequence: (statement_block
|
|
53
|
+
(for_statement
|
|
54
|
+
body: (statement_block
|
|
55
|
+
(if_statement) @IF_IN_FOR)))))
|
|
56
|
+
|
|
57
|
+
;; Pattern 6: if inside while inside if
|
|
58
|
+
(statement_block
|
|
59
|
+
(if_statement
|
|
60
|
+
consequence: (statement_block
|
|
61
|
+
(while_statement
|
|
62
|
+
body: (statement_block
|
|
63
|
+
(if_statement) @IF_IN_WHILE)))))
|
|
64
|
+
|
|
65
|
+
;; Pattern 7: for inside for inside for
|
|
66
|
+
(statement_block
|
|
67
|
+
(for_statement
|
|
68
|
+
body: (statement_block
|
|
69
|
+
(for_statement
|
|
70
|
+
body: (statement_block
|
|
71
|
+
(for_statement) @FOR_NESTED)))))
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
metavars:
|
|
75
|
+
- IF_NESTED
|
|
76
|
+
- FOR_NESTED
|
|
77
|
+
- WHILE_NESTED
|
|
78
|
+
- TRY_NESTED
|
|
79
|
+
- IF_IN_FOR
|
|
80
|
+
- IF_IN_WHILE
|
|
81
|
+
|
|
82
|
+
tags:
|
|
83
|
+
- complexity
|
|
84
|
+
- readability
|
|
85
|
+
- best-practice
|
|
86
|
+
|
|
87
|
+
examples:
|
|
88
|
+
bad: |
|
|
89
|
+
function process(user) {
|
|
90
|
+
if (user) {
|
|
91
|
+
if (user.active) {
|
|
92
|
+
if (user.permissions) { // 3 levels deep!
|
|
93
|
+
return doSomething();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function loop(items) {
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
if (item.active) {
|
|
102
|
+
for (const sub of item.subs) { // 3 levels
|
|
103
|
+
process(sub);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
good: |
|
|
110
|
+
function process(user) {
|
|
111
|
+
if (!user) return null;
|
|
112
|
+
if (!user.active) return null;
|
|
113
|
+
if (!user.permissions) return null;
|
|
114
|
+
return doSomething();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
has_fix: false
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Deep Promise Chain
|
|
2
|
+
# Detects promise chains 4+ levels deep
|
|
3
|
+
id: deep-promise-chain
|
|
4
|
+
name: Deep Promise Chain (4+ levels)
|
|
5
|
+
severity: warning
|
|
6
|
+
category: complexity
|
|
7
|
+
language: typescript
|
|
8
|
+
|
|
9
|
+
message: "Promise chain {{M1}} → {{M2}} → {{M3}} → {{M4}} — consider async/await"
|
|
10
|
+
|
|
11
|
+
description: |
|
|
12
|
+
Deep promise chains (4+ levels) are hard to read and debug.
|
|
13
|
+
Async/await provides clearer control flow.
|
|
14
|
+
|
|
15
|
+
✅ FIX: Convert to async/await
|
|
16
|
+
|
|
17
|
+
query: |
|
|
18
|
+
(call_expression
|
|
19
|
+
function: (member_expression
|
|
20
|
+
object: (call_expression
|
|
21
|
+
function: (member_expression
|
|
22
|
+
object: (call_expression
|
|
23
|
+
function: (member_expression
|
|
24
|
+
object: (call_expression
|
|
25
|
+
function: (member_expression
|
|
26
|
+
property: (property_identifier) @M1)
|
|
27
|
+
arguments: (arguments))
|
|
28
|
+
property: (property_identifier) @M2)
|
|
29
|
+
arguments: (arguments))
|
|
30
|
+
property: (property_identifier) @M3)
|
|
31
|
+
arguments: (arguments))
|
|
32
|
+
property: (property_identifier) @M4)
|
|
33
|
+
arguments: (arguments))
|
|
34
|
+
(#match? @M1 "^(then|catch|finally)$")
|
|
35
|
+
(#match? @M2 "^(then|catch|finally)$")
|
|
36
|
+
(#match? @M3 "^(then|catch|finally)$")
|
|
37
|
+
(#match? @M4 "^(then|catch|finally)$")
|
|
38
|
+
|
|
39
|
+
metavars:
|
|
40
|
+
- M1
|
|
41
|
+
- M2
|
|
42
|
+
- M3
|
|
43
|
+
- M4
|
|
44
|
+
|
|
45
|
+
tags:
|
|
46
|
+
- complexity
|
|
47
|
+
- async
|
|
48
|
+
- readability
|
|
49
|
+
|
|
50
|
+
examples:
|
|
51
|
+
bad: |
|
|
52
|
+
fetch('/api')
|
|
53
|
+
.then(r => r.json())
|
|
54
|
+
.catch(e => console.log(e))
|
|
55
|
+
.then(data => data.items)
|
|
56
|
+
.then(items => items[0])
|
|
57
|
+
.then(first => process(first));
|
|
58
|
+
|
|
59
|
+
good: |
|
|
60
|
+
async function fetchFirstItem() {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch('/api');
|
|
63
|
+
const data = await response.json();
|
|
64
|
+
const items = data.items;
|
|
65
|
+
const first = items[0];
|
|
66
|
+
return process(first);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.log(e);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
has_fix: true
|
|
73
|
+
fix_action: convert_to_async_await
|