thailint 0.12.0__py3-none-any.whl → 0.13.0__py3-none-any.whl
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.
- src/analyzers/__init__.py +4 -3
- src/analyzers/ast_utils.py +54 -0
- src/analyzers/typescript_base.py +4 -0
- src/cli/__init__.py +3 -0
- src/cli/config.py +12 -12
- src/cli/config_merge.py +241 -0
- src/cli/linters/__init__.py +3 -0
- src/cli/linters/code_patterns.py +113 -5
- src/cli/linters/code_smells.py +4 -0
- src/cli/linters/documentation.py +3 -0
- src/cli/linters/structure.py +3 -0
- src/cli/linters/structure_quality.py +3 -0
- src/cli_main.py +3 -0
- src/config.py +2 -1
- src/core/base.py +3 -2
- src/core/cli_utils.py +3 -1
- src/core/config_parser.py +5 -2
- src/core/constants.py +54 -0
- src/core/linter_utils.py +4 -0
- src/core/rule_discovery.py +5 -1
- src/core/violation_builder.py +3 -0
- src/linter_config/directive_markers.py +109 -0
- src/linter_config/ignore.py +225 -383
- src/linter_config/pattern_utils.py +65 -0
- src/linter_config/rule_matcher.py +89 -0
- src/linters/collection_pipeline/any_all_analyzer.py +281 -0
- src/linters/collection_pipeline/ast_utils.py +40 -0
- src/linters/collection_pipeline/config.py +12 -0
- src/linters/collection_pipeline/continue_analyzer.py +2 -8
- src/linters/collection_pipeline/detector.py +262 -32
- src/linters/collection_pipeline/filter_map_analyzer.py +402 -0
- src/linters/collection_pipeline/linter.py +18 -35
- src/linters/collection_pipeline/suggestion_builder.py +68 -1
- src/linters/dry/base_token_analyzer.py +16 -9
- src/linters/dry/block_filter.py +7 -4
- src/linters/dry/cache.py +7 -2
- src/linters/dry/config.py +7 -1
- src/linters/dry/constant_matcher.py +34 -25
- src/linters/dry/file_analyzer.py +4 -2
- src/linters/dry/inline_ignore.py +7 -16
- src/linters/dry/linter.py +48 -25
- src/linters/dry/python_analyzer.py +18 -10
- src/linters/dry/python_constant_extractor.py +51 -52
- src/linters/dry/single_statement_detector.py +14 -12
- src/linters/dry/token_hasher.py +115 -115
- src/linters/dry/typescript_analyzer.py +11 -6
- src/linters/dry/typescript_constant_extractor.py +4 -0
- src/linters/dry/typescript_statement_detector.py +208 -208
- src/linters/dry/typescript_value_extractor.py +3 -0
- src/linters/dry/violation_filter.py +1 -4
- src/linters/dry/violation_generator.py +1 -4
- src/linters/file_header/atemporal_detector.py +4 -0
- src/linters/file_header/base_parser.py +4 -0
- src/linters/file_header/bash_parser.py +4 -0
- src/linters/file_header/field_validator.py +5 -8
- src/linters/file_header/linter.py +19 -12
- src/linters/file_header/markdown_parser.py +6 -0
- src/linters/file_placement/config_loader.py +3 -1
- src/linters/file_placement/linter.py +22 -8
- src/linters/file_placement/pattern_matcher.py +21 -4
- src/linters/file_placement/pattern_validator.py +21 -7
- src/linters/file_placement/rule_checker.py +2 -2
- src/linters/lazy_ignores/__init__.py +43 -0
- src/linters/lazy_ignores/config.py +66 -0
- src/linters/lazy_ignores/directive_utils.py +121 -0
- src/linters/lazy_ignores/header_parser.py +177 -0
- src/linters/lazy_ignores/linter.py +158 -0
- src/linters/lazy_ignores/matcher.py +135 -0
- src/linters/lazy_ignores/python_analyzer.py +201 -0
- src/linters/lazy_ignores/rule_id_utils.py +180 -0
- src/linters/lazy_ignores/skip_detector.py +298 -0
- src/linters/lazy_ignores/types.py +67 -0
- src/linters/lazy_ignores/typescript_analyzer.py +146 -0
- src/linters/lazy_ignores/violation_builder.py +131 -0
- src/linters/lbyl/__init__.py +29 -0
- src/linters/lbyl/config.py +63 -0
- src/linters/lbyl/pattern_detectors/__init__.py +25 -0
- src/linters/lbyl/pattern_detectors/base.py +46 -0
- src/linters/magic_numbers/context_analyzer.py +227 -229
- src/linters/magic_numbers/linter.py +20 -15
- src/linters/magic_numbers/python_analyzer.py +4 -16
- src/linters/magic_numbers/typescript_analyzer.py +9 -16
- src/linters/method_property/config.py +4 -0
- src/linters/method_property/linter.py +5 -4
- src/linters/method_property/python_analyzer.py +5 -4
- src/linters/method_property/violation_builder.py +3 -0
- src/linters/nesting/typescript_analyzer.py +6 -12
- src/linters/nesting/typescript_function_extractor.py +0 -4
- src/linters/print_statements/linter.py +6 -4
- src/linters/print_statements/python_analyzer.py +85 -81
- src/linters/print_statements/typescript_analyzer.py +6 -15
- src/linters/srp/heuristics.py +4 -4
- src/linters/srp/linter.py +12 -12
- src/linters/srp/violation_builder.py +0 -4
- src/linters/stateless_class/linter.py +30 -36
- src/linters/stateless_class/python_analyzer.py +11 -20
- src/linters/stringly_typed/config.py +4 -5
- src/linters/stringly_typed/context_filter.py +410 -410
- src/linters/stringly_typed/function_call_violation_builder.py +93 -95
- src/linters/stringly_typed/linter.py +48 -16
- src/linters/stringly_typed/python/analyzer.py +5 -1
- src/linters/stringly_typed/python/call_tracker.py +8 -5
- src/linters/stringly_typed/python/comparison_tracker.py +10 -5
- src/linters/stringly_typed/python/condition_extractor.py +3 -0
- src/linters/stringly_typed/python/conditional_detector.py +4 -1
- src/linters/stringly_typed/python/match_analyzer.py +8 -2
- src/linters/stringly_typed/python/validation_detector.py +3 -0
- src/linters/stringly_typed/storage.py +14 -14
- src/linters/stringly_typed/typescript/call_tracker.py +9 -3
- src/linters/stringly_typed/typescript/comparison_tracker.py +9 -3
- src/linters/stringly_typed/violation_generator.py +288 -259
- src/orchestrator/core.py +13 -4
- src/templates/thailint_config_template.yaml +166 -0
- src/utils/project_root.py +3 -0
- thailint-0.13.0.dist-info/METADATA +184 -0
- thailint-0.13.0.dist-info/RECORD +189 -0
- thailint-0.12.0.dist-info/METADATA +0 -1667
- thailint-0.12.0.dist-info/RECORD +0 -164
- {thailint-0.12.0.dist-info → thailint-0.13.0.dist-info}/WHEEL +0 -0
- {thailint-0.12.0.dist-info → thailint-0.13.0.dist-info}/entry_points.txt +0 -0
- {thailint-0.12.0.dist-info → thailint-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,9 +11,10 @@ Overview: Implements a blocklist-based filtering approach to reduce false positi
|
|
|
11
11
|
|
|
12
12
|
Dependencies: re module for pattern matching
|
|
13
13
|
|
|
14
|
-
Exports:
|
|
14
|
+
Exports: should_include, are_all_values_excluded functions
|
|
15
15
|
|
|
16
|
-
Interfaces:
|
|
16
|
+
Interfaces: should_include(function_name, param_index, string_values) -> bool,
|
|
17
|
+
are_all_values_excluded(unique_values) -> bool
|
|
17
18
|
|
|
18
19
|
Implementation: Blocklist-based filtering with function name patterns, parameter position rules,
|
|
19
20
|
and string value pattern detection
|
|
@@ -21,431 +22,430 @@ Implementation: Blocklist-based filtering with function name patterns, parameter
|
|
|
21
22
|
|
|
22
23
|
import re
|
|
23
24
|
|
|
25
|
+
# Function name suffixes that always indicate false positives
|
|
26
|
+
_EXCLUDED_FUNCTION_SUFFIXES: tuple[str, ...] = (
|
|
27
|
+
# Exception constructors - error messages are inherently unique strings
|
|
28
|
+
"Error",
|
|
29
|
+
"Exception",
|
|
30
|
+
"Warning",
|
|
31
|
+
)
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
# Function name patterns to always exclude (case-insensitive suffix/contains match)
|
|
34
|
+
_EXCLUDED_FUNCTION_PATTERNS: tuple[str, ...] = (
|
|
35
|
+
# Dictionary/object access - these are metadata access, not domain values
|
|
36
|
+
".get",
|
|
37
|
+
".set",
|
|
38
|
+
".pop",
|
|
39
|
+
".setdefault",
|
|
40
|
+
".update",
|
|
41
|
+
# List/collection operations
|
|
42
|
+
".append",
|
|
43
|
+
".extend",
|
|
44
|
+
".insert",
|
|
45
|
+
".add",
|
|
46
|
+
".remove",
|
|
47
|
+
".push",
|
|
48
|
+
".set",
|
|
49
|
+
".has",
|
|
50
|
+
"hasItem",
|
|
51
|
+
"push",
|
|
52
|
+
# String processing - delimiters and format strings
|
|
53
|
+
# Note: both .method and method forms to catch both method calls and standalone functions
|
|
54
|
+
".split",
|
|
55
|
+
"split",
|
|
56
|
+
".rsplit",
|
|
57
|
+
".replace",
|
|
58
|
+
"replace",
|
|
59
|
+
".strip",
|
|
60
|
+
".rstrip",
|
|
61
|
+
".lstrip",
|
|
62
|
+
".startswith",
|
|
63
|
+
"startswith",
|
|
64
|
+
".startsWith",
|
|
65
|
+
"startsWith",
|
|
66
|
+
".endswith",
|
|
67
|
+
"endswith",
|
|
68
|
+
".endsWith",
|
|
69
|
+
"endsWith",
|
|
70
|
+
".includes",
|
|
71
|
+
"includes",
|
|
72
|
+
".indexOf",
|
|
73
|
+
"indexOf",
|
|
74
|
+
".lastIndexOf",
|
|
75
|
+
".match",
|
|
76
|
+
".search",
|
|
77
|
+
".format",
|
|
78
|
+
".join",
|
|
79
|
+
"join",
|
|
80
|
+
".encode",
|
|
81
|
+
".decode",
|
|
82
|
+
".lower",
|
|
83
|
+
".upper",
|
|
84
|
+
".trim",
|
|
85
|
+
".trimStart",
|
|
86
|
+
".trimEnd",
|
|
87
|
+
".padStart",
|
|
88
|
+
".padEnd",
|
|
89
|
+
"strftime",
|
|
90
|
+
"strptime",
|
|
91
|
+
# Logging and output - human-readable messages
|
|
92
|
+
"logger.debug",
|
|
93
|
+
"logger.info",
|
|
94
|
+
"logger.warning",
|
|
95
|
+
"logger.error",
|
|
96
|
+
"logger.critical",
|
|
97
|
+
"logger.exception",
|
|
98
|
+
"logging.debug",
|
|
99
|
+
"logging.info",
|
|
100
|
+
"logging.warning",
|
|
101
|
+
"logging.error",
|
|
102
|
+
"print",
|
|
103
|
+
"echo",
|
|
104
|
+
"console.print",
|
|
105
|
+
"console.log",
|
|
106
|
+
"typer.echo",
|
|
107
|
+
"click.echo",
|
|
108
|
+
# Regex - pattern strings
|
|
109
|
+
"re.sub",
|
|
110
|
+
"re.match",
|
|
111
|
+
"re.search",
|
|
112
|
+
"re.compile",
|
|
113
|
+
"re.findall",
|
|
114
|
+
"re.split",
|
|
115
|
+
# Environment variables
|
|
116
|
+
"os.environ.get",
|
|
117
|
+
"os.getenv",
|
|
118
|
+
"environ.get",
|
|
119
|
+
"getenv",
|
|
120
|
+
# File operations
|
|
121
|
+
"open",
|
|
122
|
+
"Path",
|
|
123
|
+
# Framework validators - must be strings matching field names
|
|
124
|
+
"field_validator",
|
|
125
|
+
"validator",
|
|
126
|
+
"computed_field",
|
|
127
|
+
# Type system - required Python syntax
|
|
128
|
+
"TypeVar",
|
|
129
|
+
"Generic",
|
|
130
|
+
"cast",
|
|
131
|
+
# Numeric - string representations of numbers
|
|
132
|
+
"Decimal",
|
|
133
|
+
"int",
|
|
134
|
+
"float",
|
|
135
|
+
# Exception constructors - error messages
|
|
136
|
+
"ValueError",
|
|
137
|
+
"TypeError",
|
|
138
|
+
"KeyError",
|
|
139
|
+
"AttributeError",
|
|
140
|
+
"RuntimeError",
|
|
141
|
+
"Exception",
|
|
142
|
+
"raise",
|
|
143
|
+
"APIException",
|
|
144
|
+
"HTTPException",
|
|
145
|
+
"ValidationError",
|
|
146
|
+
# CLI frameworks - short flags, option names, prompts
|
|
147
|
+
"typer.Option",
|
|
148
|
+
"typer.Argument",
|
|
149
|
+
"typer.confirm",
|
|
150
|
+
"typer.prompt",
|
|
151
|
+
"click.option",
|
|
152
|
+
"click.argument",
|
|
153
|
+
"click.confirm",
|
|
154
|
+
"click.prompt",
|
|
155
|
+
".command",
|
|
156
|
+
# HTTP/API clients - external protocol strings
|
|
157
|
+
"requests.get",
|
|
158
|
+
"requests.post",
|
|
159
|
+
"requests.put",
|
|
160
|
+
"requests.delete",
|
|
161
|
+
"requests.patch",
|
|
162
|
+
"httpx.get",
|
|
163
|
+
"httpx.post",
|
|
164
|
+
"axios.get",
|
|
165
|
+
"axios.post",
|
|
166
|
+
"axios.put",
|
|
167
|
+
"axios.delete",
|
|
168
|
+
"axios.patch",
|
|
169
|
+
"client.get",
|
|
170
|
+
"client.post",
|
|
171
|
+
"client.put",
|
|
172
|
+
"client.delete",
|
|
173
|
+
"session.client",
|
|
174
|
+
"session.resource",
|
|
175
|
+
"_request",
|
|
176
|
+
# Browser/DOM APIs - CSS selectors and data URLs
|
|
177
|
+
"document.querySelector",
|
|
178
|
+
"document.querySelectorAll",
|
|
179
|
+
"document.getElementById",
|
|
180
|
+
"document.getElementsByClassName",
|
|
181
|
+
"canvas.toDataURL",
|
|
182
|
+
"canvas.toBlob",
|
|
183
|
+
"createElement",
|
|
184
|
+
"getAttribute",
|
|
185
|
+
"setAttribute",
|
|
186
|
+
"addEventListener",
|
|
187
|
+
"removeEventListener",
|
|
188
|
+
"localStorage.getItem",
|
|
189
|
+
"localStorage.setItem",
|
|
190
|
+
"sessionStorage.getItem",
|
|
191
|
+
"sessionStorage.setItem",
|
|
192
|
+
"window.confirm",
|
|
193
|
+
"window.alert",
|
|
194
|
+
"window.prompt",
|
|
195
|
+
"confirm",
|
|
196
|
+
"alert",
|
|
197
|
+
"prompt",
|
|
198
|
+
# React hooks - internal state identifiers
|
|
199
|
+
"useRef",
|
|
200
|
+
"useState",
|
|
201
|
+
"useCallback",
|
|
202
|
+
"useMemo",
|
|
203
|
+
# AWS SDK - service names and API parameters
|
|
204
|
+
"boto3.client",
|
|
205
|
+
"boto3.resource",
|
|
206
|
+
"generate_presigned_url",
|
|
207
|
+
# AWS CDK - infrastructure as code (broad patterns)
|
|
208
|
+
"s3.",
|
|
209
|
+
"ec2.",
|
|
210
|
+
"logs.",
|
|
211
|
+
"route53.",
|
|
212
|
+
"lambda_.",
|
|
213
|
+
"_lambda.",
|
|
214
|
+
"tasks.",
|
|
215
|
+
"iam.",
|
|
216
|
+
"dynamodb.",
|
|
217
|
+
"sqs.",
|
|
218
|
+
"sns.",
|
|
219
|
+
"apigateway.",
|
|
220
|
+
"cloudfront.",
|
|
221
|
+
"cdk.",
|
|
222
|
+
"sfn.",
|
|
223
|
+
"acm.",
|
|
224
|
+
"cloudwatch.",
|
|
225
|
+
"secretsmanager.",
|
|
226
|
+
"cr.",
|
|
227
|
+
"pipes.",
|
|
228
|
+
"rds.",
|
|
229
|
+
"elasticache.",
|
|
230
|
+
"from_lookup",
|
|
231
|
+
"generate_resource_name",
|
|
232
|
+
"CfnPipe",
|
|
233
|
+
"CfnOutput",
|
|
234
|
+
# FastAPI/Starlette routing
|
|
235
|
+
"router.get",
|
|
236
|
+
"router.post",
|
|
237
|
+
"router.put",
|
|
238
|
+
"router.delete",
|
|
239
|
+
"router.patch",
|
|
240
|
+
"app.get",
|
|
241
|
+
"app.post",
|
|
242
|
+
"app.put",
|
|
243
|
+
"app.delete",
|
|
244
|
+
"@app.",
|
|
245
|
+
"@router.",
|
|
246
|
+
# DynamoDB attribute access
|
|
247
|
+
"Key",
|
|
248
|
+
"Attr",
|
|
249
|
+
"ConditionExpression",
|
|
250
|
+
# Azure CLI - external tool invocation
|
|
251
|
+
"az",
|
|
252
|
+
# Database/ORM - schema definitions
|
|
253
|
+
"op.add_column",
|
|
254
|
+
"op.drop_column",
|
|
255
|
+
"op.create_table",
|
|
256
|
+
"op.alter_column",
|
|
257
|
+
"sa.Column",
|
|
258
|
+
"sa.PrimaryKeyConstraint",
|
|
259
|
+
"sa.ForeignKeyConstraint",
|
|
260
|
+
"Column",
|
|
261
|
+
"relationship",
|
|
262
|
+
"postgresql.ENUM",
|
|
263
|
+
"ENUM",
|
|
264
|
+
# Python built-ins
|
|
265
|
+
"getattr",
|
|
266
|
+
"setattr",
|
|
267
|
+
"hasattr",
|
|
268
|
+
"delattr",
|
|
269
|
+
"isinstance",
|
|
270
|
+
"issubclass",
|
|
271
|
+
# Pydantic/dataclass fields
|
|
272
|
+
"Field",
|
|
273
|
+
"PrivateAttr",
|
|
274
|
+
# UI frameworks - display text
|
|
275
|
+
"QLabel",
|
|
276
|
+
"QPushButton",
|
|
277
|
+
"QMessageBox",
|
|
278
|
+
"QCheckBox",
|
|
279
|
+
"setWindowTitle",
|
|
280
|
+
"setText",
|
|
281
|
+
"setToolTip",
|
|
282
|
+
"setPlaceholderText",
|
|
283
|
+
"setStatusTip",
|
|
284
|
+
"Static",
|
|
285
|
+
"Label",
|
|
286
|
+
"Button",
|
|
287
|
+
# Table/grid display - formatting
|
|
288
|
+
"table.add_row",
|
|
289
|
+
"add_row",
|
|
290
|
+
"add_column",
|
|
291
|
+
"Table",
|
|
292
|
+
"Panel",
|
|
293
|
+
"Console",
|
|
294
|
+
# Testing - mocks and fixtures
|
|
295
|
+
"monkeypatch.setattr",
|
|
296
|
+
"patch",
|
|
297
|
+
"Mock",
|
|
298
|
+
"MagicMock",
|
|
299
|
+
"PropertyMock",
|
|
300
|
+
# String parsing methods - internal message processing
|
|
301
|
+
".index",
|
|
302
|
+
".find",
|
|
303
|
+
".rfind",
|
|
304
|
+
".rindex",
|
|
305
|
+
# Storybook - action handlers
|
|
306
|
+
"action",
|
|
307
|
+
"fn",
|
|
308
|
+
# React state setters - UI state names
|
|
309
|
+
"setMessage",
|
|
310
|
+
"setError",
|
|
311
|
+
"setLoading",
|
|
312
|
+
"setStatus",
|
|
313
|
+
"setText",
|
|
314
|
+
# API clients - external endpoints
|
|
315
|
+
"API.",
|
|
316
|
+
"api.",
|
|
317
|
+
# CSS/styling
|
|
318
|
+
"setStyleSheet",
|
|
319
|
+
"add_class",
|
|
320
|
+
"remove_class",
|
|
321
|
+
# JSON/serialization - output identifiers
|
|
322
|
+
"_output",
|
|
323
|
+
"json.dumps",
|
|
324
|
+
"json.loads",
|
|
325
|
+
# Health checks - framework pattern
|
|
326
|
+
"register_health_check",
|
|
327
|
+
# Config management - dynamic config keys
|
|
328
|
+
"ensure_config_section",
|
|
329
|
+
"set_config_value",
|
|
330
|
+
"get_config_value",
|
|
331
|
+
)
|
|
27
332
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
333
|
+
# Function names where second parameter (index 1) should be excluded
|
|
334
|
+
# These are typically default values, not keys
|
|
335
|
+
_EXCLUDE_PARAM_INDEX_1: tuple[str, ...] = (
|
|
336
|
+
".get",
|
|
337
|
+
"os.environ.get",
|
|
338
|
+
"environ.get",
|
|
339
|
+
"getattr",
|
|
340
|
+
"os.getenv",
|
|
341
|
+
"getenv",
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# String value patterns that indicate false positives
|
|
345
|
+
_EXCLUDED_VALUE_PATTERNS: tuple[re.Pattern[str], ...] = (
|
|
346
|
+
# strftime format strings
|
|
347
|
+
re.compile(r"^%[A-Za-z%-]+$"),
|
|
348
|
+
# Single character delimiters
|
|
349
|
+
re.compile(r"^[\n\t\r,;:|/\\.\-_]$"),
|
|
350
|
+
# Empty string or whitespace only
|
|
351
|
+
re.compile(r"^\s*$"),
|
|
352
|
+
# HTTP methods (external protocol)
|
|
353
|
+
re.compile(r"^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$"),
|
|
354
|
+
# Numeric strings (should use Decimal or int)
|
|
355
|
+
re.compile(r"^-?\d+\.?\d*$"),
|
|
356
|
+
# Short CLI flags
|
|
357
|
+
re.compile(r"^-[a-zA-Z]$"),
|
|
358
|
+
# CSS/Rich markup
|
|
359
|
+
re.compile(r"^\[/?[a-z]+\]"),
|
|
360
|
+
# File modes (only multi-char modes to avoid false positives on single letters)
|
|
361
|
+
re.compile(r"^[rwa][bt]\+?$|^[rwa]\+$"),
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def should_include(
|
|
366
|
+
function_name: str,
|
|
367
|
+
param_index: int,
|
|
368
|
+
unique_values: set[str],
|
|
369
|
+
) -> bool:
|
|
370
|
+
"""Determine if a function call pattern should be included in violations.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
function_name: Name of the function being called
|
|
374
|
+
param_index: Index of the parameter (0-based)
|
|
375
|
+
unique_values: Set of unique string values passed to this parameter
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
True if this pattern should generate a violation, False to filter it out
|
|
35
379
|
"""
|
|
380
|
+
# Check function name patterns
|
|
381
|
+
if _is_excluded_function(function_name):
|
|
382
|
+
return False
|
|
36
383
|
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"Error",
|
|
41
|
-
"Exception",
|
|
42
|
-
"Warning",
|
|
43
|
-
)
|
|
384
|
+
# Check parameter position for specific functions
|
|
385
|
+
if _is_excluded_param_position(function_name, param_index):
|
|
386
|
+
return False
|
|
44
387
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
".get",
|
|
49
|
-
".set",
|
|
50
|
-
".pop",
|
|
51
|
-
".setdefault",
|
|
52
|
-
".update",
|
|
53
|
-
# List/collection operations
|
|
54
|
-
".append",
|
|
55
|
-
".extend",
|
|
56
|
-
".insert",
|
|
57
|
-
".add",
|
|
58
|
-
".remove",
|
|
59
|
-
".push",
|
|
60
|
-
".set",
|
|
61
|
-
".has",
|
|
62
|
-
"hasItem",
|
|
63
|
-
"push",
|
|
64
|
-
# String processing - delimiters and format strings
|
|
65
|
-
# Note: both .method and method forms to catch both method calls and standalone functions
|
|
66
|
-
".split",
|
|
67
|
-
"split",
|
|
68
|
-
".rsplit",
|
|
69
|
-
".replace",
|
|
70
|
-
"replace",
|
|
71
|
-
".strip",
|
|
72
|
-
".rstrip",
|
|
73
|
-
".lstrip",
|
|
74
|
-
".startswith",
|
|
75
|
-
"startswith",
|
|
76
|
-
".startsWith",
|
|
77
|
-
"startsWith",
|
|
78
|
-
".endswith",
|
|
79
|
-
"endswith",
|
|
80
|
-
".endsWith",
|
|
81
|
-
"endsWith",
|
|
82
|
-
".includes",
|
|
83
|
-
"includes",
|
|
84
|
-
".indexOf",
|
|
85
|
-
"indexOf",
|
|
86
|
-
".lastIndexOf",
|
|
87
|
-
".match",
|
|
88
|
-
".search",
|
|
89
|
-
".format",
|
|
90
|
-
".join",
|
|
91
|
-
"join",
|
|
92
|
-
".encode",
|
|
93
|
-
".decode",
|
|
94
|
-
".lower",
|
|
95
|
-
".upper",
|
|
96
|
-
".trim",
|
|
97
|
-
".trimStart",
|
|
98
|
-
".trimEnd",
|
|
99
|
-
".padStart",
|
|
100
|
-
".padEnd",
|
|
101
|
-
"strftime",
|
|
102
|
-
"strptime",
|
|
103
|
-
# Logging and output - human-readable messages
|
|
104
|
-
"logger.debug",
|
|
105
|
-
"logger.info",
|
|
106
|
-
"logger.warning",
|
|
107
|
-
"logger.error",
|
|
108
|
-
"logger.critical",
|
|
109
|
-
"logger.exception",
|
|
110
|
-
"logging.debug",
|
|
111
|
-
"logging.info",
|
|
112
|
-
"logging.warning",
|
|
113
|
-
"logging.error",
|
|
114
|
-
"print",
|
|
115
|
-
"echo",
|
|
116
|
-
"console.print",
|
|
117
|
-
"console.log",
|
|
118
|
-
"typer.echo",
|
|
119
|
-
"click.echo",
|
|
120
|
-
# Regex - pattern strings
|
|
121
|
-
"re.sub",
|
|
122
|
-
"re.match",
|
|
123
|
-
"re.search",
|
|
124
|
-
"re.compile",
|
|
125
|
-
"re.findall",
|
|
126
|
-
"re.split",
|
|
127
|
-
# Environment variables
|
|
128
|
-
"os.environ.get",
|
|
129
|
-
"os.getenv",
|
|
130
|
-
"environ.get",
|
|
131
|
-
"getenv",
|
|
132
|
-
# File operations
|
|
133
|
-
"open",
|
|
134
|
-
"Path",
|
|
135
|
-
# Framework validators - must be strings matching field names
|
|
136
|
-
"field_validator",
|
|
137
|
-
"validator",
|
|
138
|
-
"computed_field",
|
|
139
|
-
# Type system - required Python syntax
|
|
140
|
-
"TypeVar",
|
|
141
|
-
"Generic",
|
|
142
|
-
"cast",
|
|
143
|
-
# Numeric - string representations of numbers
|
|
144
|
-
"Decimal",
|
|
145
|
-
"int",
|
|
146
|
-
"float",
|
|
147
|
-
# Exception constructors - error messages
|
|
148
|
-
"ValueError",
|
|
149
|
-
"TypeError",
|
|
150
|
-
"KeyError",
|
|
151
|
-
"AttributeError",
|
|
152
|
-
"RuntimeError",
|
|
153
|
-
"Exception",
|
|
154
|
-
"raise",
|
|
155
|
-
"APIException",
|
|
156
|
-
"HTTPException",
|
|
157
|
-
"ValidationError",
|
|
158
|
-
# CLI frameworks - short flags, option names, prompts
|
|
159
|
-
"typer.Option",
|
|
160
|
-
"typer.Argument",
|
|
161
|
-
"typer.confirm",
|
|
162
|
-
"typer.prompt",
|
|
163
|
-
"click.option",
|
|
164
|
-
"click.argument",
|
|
165
|
-
"click.confirm",
|
|
166
|
-
"click.prompt",
|
|
167
|
-
".command",
|
|
168
|
-
# HTTP/API clients - external protocol strings
|
|
169
|
-
"requests.get",
|
|
170
|
-
"requests.post",
|
|
171
|
-
"requests.put",
|
|
172
|
-
"requests.delete",
|
|
173
|
-
"requests.patch",
|
|
174
|
-
"httpx.get",
|
|
175
|
-
"httpx.post",
|
|
176
|
-
"axios.get",
|
|
177
|
-
"axios.post",
|
|
178
|
-
"axios.put",
|
|
179
|
-
"axios.delete",
|
|
180
|
-
"axios.patch",
|
|
181
|
-
"client.get",
|
|
182
|
-
"client.post",
|
|
183
|
-
"client.put",
|
|
184
|
-
"client.delete",
|
|
185
|
-
"session.client",
|
|
186
|
-
"session.resource",
|
|
187
|
-
"_request",
|
|
188
|
-
# Browser/DOM APIs - CSS selectors and data URLs
|
|
189
|
-
"document.querySelector",
|
|
190
|
-
"document.querySelectorAll",
|
|
191
|
-
"document.getElementById",
|
|
192
|
-
"document.getElementsByClassName",
|
|
193
|
-
"canvas.toDataURL",
|
|
194
|
-
"canvas.toBlob",
|
|
195
|
-
"createElement",
|
|
196
|
-
"getAttribute",
|
|
197
|
-
"setAttribute",
|
|
198
|
-
"addEventListener",
|
|
199
|
-
"removeEventListener",
|
|
200
|
-
"localStorage.getItem",
|
|
201
|
-
"localStorage.setItem",
|
|
202
|
-
"sessionStorage.getItem",
|
|
203
|
-
"sessionStorage.setItem",
|
|
204
|
-
"window.confirm",
|
|
205
|
-
"window.alert",
|
|
206
|
-
"window.prompt",
|
|
207
|
-
"confirm",
|
|
208
|
-
"alert",
|
|
209
|
-
"prompt",
|
|
210
|
-
# React hooks - internal state identifiers
|
|
211
|
-
"useRef",
|
|
212
|
-
"useState",
|
|
213
|
-
"useCallback",
|
|
214
|
-
"useMemo",
|
|
215
|
-
# AWS SDK - service names and API parameters
|
|
216
|
-
"boto3.client",
|
|
217
|
-
"boto3.resource",
|
|
218
|
-
"generate_presigned_url",
|
|
219
|
-
# AWS CDK - infrastructure as code (broad patterns)
|
|
220
|
-
"s3.",
|
|
221
|
-
"ec2.",
|
|
222
|
-
"logs.",
|
|
223
|
-
"route53.",
|
|
224
|
-
"lambda_.",
|
|
225
|
-
"_lambda.",
|
|
226
|
-
"tasks.",
|
|
227
|
-
"iam.",
|
|
228
|
-
"dynamodb.",
|
|
229
|
-
"sqs.",
|
|
230
|
-
"sns.",
|
|
231
|
-
"apigateway.",
|
|
232
|
-
"cloudfront.",
|
|
233
|
-
"cdk.",
|
|
234
|
-
"sfn.",
|
|
235
|
-
"acm.",
|
|
236
|
-
"cloudwatch.",
|
|
237
|
-
"secretsmanager.",
|
|
238
|
-
"cr.",
|
|
239
|
-
"pipes.",
|
|
240
|
-
"rds.",
|
|
241
|
-
"elasticache.",
|
|
242
|
-
"from_lookup",
|
|
243
|
-
"generate_resource_name",
|
|
244
|
-
"CfnPipe",
|
|
245
|
-
"CfnOutput",
|
|
246
|
-
# FastAPI/Starlette routing
|
|
247
|
-
"router.get",
|
|
248
|
-
"router.post",
|
|
249
|
-
"router.put",
|
|
250
|
-
"router.delete",
|
|
251
|
-
"router.patch",
|
|
252
|
-
"app.get",
|
|
253
|
-
"app.post",
|
|
254
|
-
"app.put",
|
|
255
|
-
"app.delete",
|
|
256
|
-
"@app.",
|
|
257
|
-
"@router.",
|
|
258
|
-
# DynamoDB attribute access
|
|
259
|
-
"Key",
|
|
260
|
-
"Attr",
|
|
261
|
-
"ConditionExpression",
|
|
262
|
-
# Azure CLI - external tool invocation
|
|
263
|
-
"az",
|
|
264
|
-
# Database/ORM - schema definitions
|
|
265
|
-
"op.add_column",
|
|
266
|
-
"op.drop_column",
|
|
267
|
-
"op.create_table",
|
|
268
|
-
"op.alter_column",
|
|
269
|
-
"sa.Column",
|
|
270
|
-
"sa.PrimaryKeyConstraint",
|
|
271
|
-
"sa.ForeignKeyConstraint",
|
|
272
|
-
"Column",
|
|
273
|
-
"relationship",
|
|
274
|
-
"postgresql.ENUM",
|
|
275
|
-
"ENUM",
|
|
276
|
-
# Python built-ins
|
|
277
|
-
"getattr",
|
|
278
|
-
"setattr",
|
|
279
|
-
"hasattr",
|
|
280
|
-
"delattr",
|
|
281
|
-
"isinstance",
|
|
282
|
-
"issubclass",
|
|
283
|
-
# Pydantic/dataclass fields
|
|
284
|
-
"Field",
|
|
285
|
-
"PrivateAttr",
|
|
286
|
-
# UI frameworks - display text
|
|
287
|
-
"QLabel",
|
|
288
|
-
"QPushButton",
|
|
289
|
-
"QMessageBox",
|
|
290
|
-
"QCheckBox",
|
|
291
|
-
"setWindowTitle",
|
|
292
|
-
"setText",
|
|
293
|
-
"setToolTip",
|
|
294
|
-
"setPlaceholderText",
|
|
295
|
-
"setStatusTip",
|
|
296
|
-
"Static",
|
|
297
|
-
"Label",
|
|
298
|
-
"Button",
|
|
299
|
-
# Table/grid display - formatting
|
|
300
|
-
"table.add_row",
|
|
301
|
-
"add_row",
|
|
302
|
-
"add_column",
|
|
303
|
-
"Table",
|
|
304
|
-
"Panel",
|
|
305
|
-
"Console",
|
|
306
|
-
# Testing - mocks and fixtures
|
|
307
|
-
"monkeypatch.setattr",
|
|
308
|
-
"patch",
|
|
309
|
-
"Mock",
|
|
310
|
-
"MagicMock",
|
|
311
|
-
"PropertyMock",
|
|
312
|
-
# Storybook - action handlers
|
|
313
|
-
"action",
|
|
314
|
-
"fn",
|
|
315
|
-
# React state setters - UI state names
|
|
316
|
-
"setMessage",
|
|
317
|
-
"setError",
|
|
318
|
-
"setLoading",
|
|
319
|
-
"setStatus",
|
|
320
|
-
"setText",
|
|
321
|
-
# API clients - external endpoints
|
|
322
|
-
"API.",
|
|
323
|
-
"api.",
|
|
324
|
-
# CSS/styling
|
|
325
|
-
"setStyleSheet",
|
|
326
|
-
"add_class",
|
|
327
|
-
"remove_class",
|
|
328
|
-
# JSON/serialization - output identifiers
|
|
329
|
-
"_output",
|
|
330
|
-
"json.dumps",
|
|
331
|
-
"json.loads",
|
|
332
|
-
# Health checks - framework pattern
|
|
333
|
-
"register_health_check",
|
|
334
|
-
)
|
|
388
|
+
# Check if all values match excluded patterns
|
|
389
|
+
if _all_values_excluded(unique_values):
|
|
390
|
+
return False
|
|
335
391
|
|
|
336
|
-
|
|
337
|
-
# These are typically default values, not keys
|
|
338
|
-
_EXCLUDE_PARAM_INDEX_1: tuple[str, ...] = (
|
|
339
|
-
".get",
|
|
340
|
-
"os.environ.get",
|
|
341
|
-
"environ.get",
|
|
342
|
-
"getattr",
|
|
343
|
-
"os.getenv",
|
|
344
|
-
"getenv",
|
|
345
|
-
)
|
|
392
|
+
return True
|
|
346
393
|
|
|
347
|
-
# String value patterns that indicate false positives
|
|
348
|
-
_EXCLUDED_VALUE_PATTERNS: tuple[re.Pattern[str], ...] = (
|
|
349
|
-
# strftime format strings
|
|
350
|
-
re.compile(r"^%[A-Za-z%-]+$"),
|
|
351
|
-
# Single character delimiters
|
|
352
|
-
re.compile(r"^[\n\t\r,;:|/\\.\-_]$"),
|
|
353
|
-
# Empty string or whitespace only
|
|
354
|
-
re.compile(r"^\s*$"),
|
|
355
|
-
# HTTP methods (external protocol)
|
|
356
|
-
re.compile(r"^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$"),
|
|
357
|
-
# Numeric strings (should use Decimal or int)
|
|
358
|
-
re.compile(r"^-?\d+\.?\d*$"),
|
|
359
|
-
# Short CLI flags
|
|
360
|
-
re.compile(r"^-[a-zA-Z]$"),
|
|
361
|
-
# CSS/Rich markup
|
|
362
|
-
re.compile(r"^\[/?[a-z]+\]"),
|
|
363
|
-
# File modes (only multi-char modes to avoid false positives on single letters)
|
|
364
|
-
re.compile(r"^[rwa][bt]\+?$|^[rwa]\+$"),
|
|
365
|
-
)
|
|
366
394
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
function_name: str,
|
|
370
|
-
param_index: int,
|
|
371
|
-
unique_values: set[str],
|
|
372
|
-
) -> bool:
|
|
373
|
-
"""Determine if a function call pattern should be included in violations.
|
|
374
|
-
|
|
375
|
-
Args:
|
|
376
|
-
function_name: Name of the function being called
|
|
377
|
-
param_index: Index of the parameter (0-based)
|
|
378
|
-
unique_values: Set of unique string values passed to this parameter
|
|
379
|
-
|
|
380
|
-
Returns:
|
|
381
|
-
True if this pattern should generate a violation, False to filter it out
|
|
382
|
-
"""
|
|
383
|
-
# Check function name patterns
|
|
384
|
-
if self._is_excluded_function(function_name):
|
|
385
|
-
return False
|
|
386
|
-
|
|
387
|
-
# Check parameter position for specific functions
|
|
388
|
-
if self._is_excluded_param_position(function_name, param_index):
|
|
389
|
-
return False
|
|
390
|
-
|
|
391
|
-
# Check if all values match excluded patterns
|
|
392
|
-
if self._all_values_excluded(unique_values):
|
|
393
|
-
return False
|
|
395
|
+
def are_all_values_excluded(unique_values: set[str]) -> bool:
|
|
396
|
+
"""Check if all values match excluded patterns (numeric strings, delimiters, etc.).
|
|
394
397
|
|
|
395
|
-
|
|
398
|
+
Public interface for value-based filtering used by violation generator.
|
|
396
399
|
|
|
397
|
-
|
|
398
|
-
|
|
400
|
+
Args:
|
|
401
|
+
unique_values: Set of unique string values to check
|
|
399
402
|
|
|
400
|
-
|
|
403
|
+
Returns:
|
|
404
|
+
True if all values match excluded patterns, False otherwise
|
|
405
|
+
"""
|
|
406
|
+
return _all_values_excluded(unique_values)
|
|
401
407
|
|
|
402
|
-
Args:
|
|
403
|
-
unique_values: Set of unique string values to check
|
|
404
408
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
+
def _is_excluded_function(function_name: str) -> bool:
|
|
410
|
+
"""Check if function name matches any excluded pattern."""
|
|
411
|
+
# Check suffix patterns (e.g., *Error, *Exception)
|
|
412
|
+
if _matches_suffix(function_name):
|
|
413
|
+
return True
|
|
414
|
+
return _matches_pattern(function_name.lower())
|
|
409
415
|
|
|
410
|
-
def _is_excluded_function(self, function_name: str) -> bool:
|
|
411
|
-
"""Check if function name matches any excluded pattern."""
|
|
412
|
-
# Check suffix patterns (e.g., *Error, *Exception)
|
|
413
|
-
if self._matches_suffix(function_name):
|
|
414
|
-
return True
|
|
415
|
-
return self._matches_pattern(function_name.lower())
|
|
416
416
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
417
|
+
def _matches_suffix(function_name: str) -> bool:
|
|
418
|
+
"""Check if function name ends with an excluded suffix."""
|
|
419
|
+
return any(function_name.endswith(s) for s in _EXCLUDED_FUNCTION_SUFFIXES)
|
|
420
420
|
|
|
421
|
-
def _matches_pattern(self, func_lower: str) -> bool:
|
|
422
|
-
"""Check if function name matches any excluded pattern."""
|
|
423
|
-
for pattern in self._EXCLUDED_FUNCTION_PATTERNS:
|
|
424
|
-
pattern_lower = pattern.lower()
|
|
425
|
-
if pattern_lower in func_lower or func_lower.endswith(pattern_lower):
|
|
426
|
-
return True
|
|
427
|
-
return False
|
|
428
421
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
422
|
+
def _matches_pattern(func_lower: str) -> bool:
|
|
423
|
+
"""Check if function name matches any excluded pattern."""
|
|
424
|
+
return any(
|
|
425
|
+
pattern.lower() in func_lower or func_lower.endswith(pattern.lower())
|
|
426
|
+
for pattern in _EXCLUDED_FUNCTION_PATTERNS
|
|
427
|
+
)
|
|
433
428
|
|
|
434
|
-
func_lower = function_name.lower()
|
|
435
|
-
for pattern in self._EXCLUDE_PARAM_INDEX_1:
|
|
436
|
-
if pattern.lower() in func_lower or func_lower.endswith(pattern.lower()):
|
|
437
|
-
return True
|
|
438
|
-
return False
|
|
439
429
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return True
|
|
444
|
-
return all(self._is_excluded_value(value) for value in unique_values)
|
|
445
|
-
|
|
446
|
-
def _is_excluded_value(self, value: str) -> bool:
|
|
447
|
-
"""Check if a single value matches any excluded pattern."""
|
|
448
|
-
for pattern in self._EXCLUDED_VALUE_PATTERNS:
|
|
449
|
-
if pattern.match(value):
|
|
450
|
-
return True
|
|
430
|
+
def _is_excluded_param_position(function_name: str, param_index: int) -> bool:
|
|
431
|
+
"""Check if this parameter position should be excluded for this function."""
|
|
432
|
+
if param_index != 1:
|
|
451
433
|
return False
|
|
434
|
+
|
|
435
|
+
func_lower = function_name.lower()
|
|
436
|
+
return any(
|
|
437
|
+
pattern.lower() in func_lower or func_lower.endswith(pattern.lower())
|
|
438
|
+
for pattern in _EXCLUDE_PARAM_INDEX_1
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _all_values_excluded(unique_values: set[str]) -> bool:
|
|
443
|
+
"""Check if all values in the set match excluded patterns."""
|
|
444
|
+
if not unique_values:
|
|
445
|
+
return True
|
|
446
|
+
return all(_is_excluded_value(value) for value in unique_values)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def _is_excluded_value(value: str) -> bool:
|
|
450
|
+
"""Check if a single value matches any excluded pattern."""
|
|
451
|
+
return any(pattern.match(value) for pattern in _EXCLUDED_VALUE_PATTERNS)
|