thailint 0.12.0__py3-none-any.whl → 0.14.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.
Files changed (135) hide show
  1. src/analyzers/__init__.py +4 -3
  2. src/analyzers/ast_utils.py +54 -0
  3. src/analyzers/typescript_base.py +4 -0
  4. src/cli/__init__.py +3 -0
  5. src/cli/config.py +12 -12
  6. src/cli/config_merge.py +241 -0
  7. src/cli/linters/__init__.py +9 -0
  8. src/cli/linters/code_patterns.py +107 -257
  9. src/cli/linters/code_smells.py +48 -165
  10. src/cli/linters/documentation.py +21 -95
  11. src/cli/linters/performance.py +274 -0
  12. src/cli/linters/shared.py +232 -6
  13. src/cli/linters/structure.py +26 -21
  14. src/cli/linters/structure_quality.py +28 -21
  15. src/cli_main.py +3 -0
  16. src/config.py +2 -1
  17. src/core/base.py +3 -2
  18. src/core/cli_utils.py +3 -1
  19. src/core/config_parser.py +5 -2
  20. src/core/constants.py +54 -0
  21. src/core/linter_utils.py +95 -6
  22. src/core/rule_discovery.py +5 -1
  23. src/core/violation_builder.py +3 -0
  24. src/linter_config/directive_markers.py +109 -0
  25. src/linter_config/ignore.py +225 -383
  26. src/linter_config/pattern_utils.py +65 -0
  27. src/linter_config/rule_matcher.py +89 -0
  28. src/linters/collection_pipeline/any_all_analyzer.py +281 -0
  29. src/linters/collection_pipeline/ast_utils.py +40 -0
  30. src/linters/collection_pipeline/config.py +12 -0
  31. src/linters/collection_pipeline/continue_analyzer.py +2 -8
  32. src/linters/collection_pipeline/detector.py +262 -32
  33. src/linters/collection_pipeline/filter_map_analyzer.py +402 -0
  34. src/linters/collection_pipeline/linter.py +18 -35
  35. src/linters/collection_pipeline/suggestion_builder.py +68 -1
  36. src/linters/dry/base_token_analyzer.py +16 -9
  37. src/linters/dry/block_filter.py +7 -4
  38. src/linters/dry/cache.py +7 -2
  39. src/linters/dry/config.py +7 -1
  40. src/linters/dry/constant_matcher.py +34 -25
  41. src/linters/dry/file_analyzer.py +4 -2
  42. src/linters/dry/inline_ignore.py +7 -16
  43. src/linters/dry/linter.py +48 -25
  44. src/linters/dry/python_analyzer.py +18 -10
  45. src/linters/dry/python_constant_extractor.py +51 -52
  46. src/linters/dry/single_statement_detector.py +14 -12
  47. src/linters/dry/token_hasher.py +115 -115
  48. src/linters/dry/typescript_analyzer.py +11 -6
  49. src/linters/dry/typescript_constant_extractor.py +4 -0
  50. src/linters/dry/typescript_statement_detector.py +208 -208
  51. src/linters/dry/typescript_value_extractor.py +3 -0
  52. src/linters/dry/violation_filter.py +1 -4
  53. src/linters/dry/violation_generator.py +1 -4
  54. src/linters/file_header/atemporal_detector.py +58 -40
  55. src/linters/file_header/base_parser.py +4 -0
  56. src/linters/file_header/bash_parser.py +4 -0
  57. src/linters/file_header/config.py +14 -0
  58. src/linters/file_header/field_validator.py +5 -8
  59. src/linters/file_header/linter.py +19 -12
  60. src/linters/file_header/markdown_parser.py +6 -0
  61. src/linters/file_placement/config_loader.py +3 -1
  62. src/linters/file_placement/linter.py +22 -8
  63. src/linters/file_placement/pattern_matcher.py +21 -4
  64. src/linters/file_placement/pattern_validator.py +21 -7
  65. src/linters/file_placement/rule_checker.py +2 -2
  66. src/linters/lazy_ignores/__init__.py +43 -0
  67. src/linters/lazy_ignores/config.py +66 -0
  68. src/linters/lazy_ignores/directive_utils.py +121 -0
  69. src/linters/lazy_ignores/header_parser.py +177 -0
  70. src/linters/lazy_ignores/linter.py +158 -0
  71. src/linters/lazy_ignores/matcher.py +135 -0
  72. src/linters/lazy_ignores/python_analyzer.py +205 -0
  73. src/linters/lazy_ignores/rule_id_utils.py +180 -0
  74. src/linters/lazy_ignores/skip_detector.py +298 -0
  75. src/linters/lazy_ignores/types.py +69 -0
  76. src/linters/lazy_ignores/typescript_analyzer.py +146 -0
  77. src/linters/lazy_ignores/violation_builder.py +131 -0
  78. src/linters/lbyl/__init__.py +29 -0
  79. src/linters/lbyl/config.py +63 -0
  80. src/linters/lbyl/pattern_detectors/__init__.py +25 -0
  81. src/linters/lbyl/pattern_detectors/base.py +46 -0
  82. src/linters/magic_numbers/context_analyzer.py +227 -229
  83. src/linters/magic_numbers/linter.py +20 -15
  84. src/linters/magic_numbers/python_analyzer.py +4 -16
  85. src/linters/magic_numbers/typescript_analyzer.py +9 -16
  86. src/linters/method_property/config.py +4 -1
  87. src/linters/method_property/linter.py +5 -10
  88. src/linters/method_property/python_analyzer.py +5 -4
  89. src/linters/method_property/violation_builder.py +3 -0
  90. src/linters/nesting/linter.py +11 -6
  91. src/linters/nesting/typescript_analyzer.py +6 -12
  92. src/linters/nesting/typescript_function_extractor.py +0 -4
  93. src/linters/nesting/violation_builder.py +1 -0
  94. src/linters/performance/__init__.py +91 -0
  95. src/linters/performance/config.py +43 -0
  96. src/linters/performance/constants.py +49 -0
  97. src/linters/performance/linter.py +149 -0
  98. src/linters/performance/python_analyzer.py +365 -0
  99. src/linters/performance/regex_analyzer.py +312 -0
  100. src/linters/performance/regex_linter.py +139 -0
  101. src/linters/performance/typescript_analyzer.py +236 -0
  102. src/linters/performance/violation_builder.py +160 -0
  103. src/linters/print_statements/linter.py +6 -4
  104. src/linters/print_statements/python_analyzer.py +85 -81
  105. src/linters/print_statements/typescript_analyzer.py +6 -15
  106. src/linters/srp/heuristics.py +4 -4
  107. src/linters/srp/linter.py +12 -12
  108. src/linters/srp/violation_builder.py +0 -4
  109. src/linters/stateless_class/linter.py +30 -36
  110. src/linters/stateless_class/python_analyzer.py +11 -20
  111. src/linters/stringly_typed/config.py +4 -5
  112. src/linters/stringly_typed/context_filter.py +410 -410
  113. src/linters/stringly_typed/function_call_violation_builder.py +93 -95
  114. src/linters/stringly_typed/linter.py +48 -16
  115. src/linters/stringly_typed/python/analyzer.py +5 -1
  116. src/linters/stringly_typed/python/call_tracker.py +8 -5
  117. src/linters/stringly_typed/python/comparison_tracker.py +10 -5
  118. src/linters/stringly_typed/python/condition_extractor.py +3 -0
  119. src/linters/stringly_typed/python/conditional_detector.py +4 -1
  120. src/linters/stringly_typed/python/match_analyzer.py +8 -2
  121. src/linters/stringly_typed/python/validation_detector.py +3 -0
  122. src/linters/stringly_typed/storage.py +14 -14
  123. src/linters/stringly_typed/typescript/call_tracker.py +9 -3
  124. src/linters/stringly_typed/typescript/comparison_tracker.py +9 -3
  125. src/linters/stringly_typed/violation_generator.py +288 -259
  126. src/orchestrator/core.py +13 -4
  127. src/templates/thailint_config_template.yaml +196 -0
  128. src/utils/project_root.py +3 -0
  129. thailint-0.14.0.dist-info/METADATA +185 -0
  130. thailint-0.14.0.dist-info/RECORD +199 -0
  131. thailint-0.12.0.dist-info/METADATA +0 -1667
  132. thailint-0.12.0.dist-info/RECORD +0 -164
  133. {thailint-0.12.0.dist-info → thailint-0.14.0.dist-info}/WHEEL +0 -0
  134. {thailint-0.12.0.dist-info → thailint-0.14.0.dist-info}/entry_points.txt +0 -0
  135. {thailint-0.12.0.dist-info → thailint-0.14.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: FunctionCallFilter class
14
+ Exports: should_include, are_all_values_excluded functions
15
15
 
16
- Interfaces: FunctionCallFilter.should_include(function_name, param_index, string_values) -> bool
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
- class FunctionCallFilter: # thailint: ignore srp - data-heavy class with extensive exclusion pattern lists
26
- """Filters function call violations to reduce false positives.
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
- Uses a blocklist approach to exclude known false positive patterns:
29
- - Dictionary/object access methods (.get, .set, .pop)
30
- - String processing methods (split, replace, strip, strftime)
31
- - Logging and output functions (logger.*, print, echo)
32
- - Framework-specific patterns (Pydantic validators, TypeVar)
33
- - External API functions (boto3, HTTP clients)
34
- - File and path operations
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
- # Function name suffixes that always indicate false positives
38
- _EXCLUDED_FUNCTION_SUFFIXES: tuple[str, ...] = (
39
- # Exception constructors - error messages are inherently unique strings
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
- # Function name patterns to always exclude (case-insensitive suffix/contains match)
46
- _EXCLUDED_FUNCTION_PATTERNS: tuple[str, ...] = (
47
- # Dictionary/object access - these are metadata access, not domain values
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
- # Function names where second parameter (index 1) should be excluded
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
- def should_include(
368
- self,
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
- return True
398
+ Public interface for value-based filtering used by violation generator.
396
399
 
397
- def are_all_values_excluded(self, unique_values: set[str]) -> bool:
398
- """Check if all values match excluded patterns (numeric strings, delimiters, etc.).
400
+ Args:
401
+ unique_values: Set of unique string values to check
399
402
 
400
- Public interface for value-based filtering used by violation generator.
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
- Returns:
406
- True if all values match excluded patterns, False otherwise
407
- """
408
- return self._all_values_excluded(unique_values)
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
- def _matches_suffix(self, function_name: str) -> bool:
418
- """Check if function name ends with an excluded suffix."""
419
- return any(function_name.endswith(s) for s in self._EXCLUDED_FUNCTION_SUFFIXES)
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
- def _is_excluded_param_position(self, function_name: str, param_index: int) -> bool:
430
- """Check if this parameter position should be excluded for this function."""
431
- if param_index != 1:
432
- return False
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
- def _all_values_excluded(self, unique_values: set[str]) -> bool:
441
- """Check if all values in the set match excluded patterns."""
442
- if not unique_values:
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)