apisec-code-bolt 0.1.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 (111) hide show
  1. apisec_code_bolt/__init__.py +42 -0
  2. apisec_code_bolt/__main__.py +11 -0
  3. apisec_code_bolt/analysis/__init__.py +96 -0
  4. apisec_code_bolt/analysis/analyzer.py +2309 -0
  5. apisec_code_bolt/analysis/binding_tracker.py +341 -0
  6. apisec_code_bolt/analysis/call_graph.py +1197 -0
  7. apisec_code_bolt/analysis/call_graph_types.py +332 -0
  8. apisec_code_bolt/analysis/call_resolver.py +988 -0
  9. apisec_code_bolt/analysis/capability_tagger.py +322 -0
  10. apisec_code_bolt/analysis/config_scanner.py +197 -0
  11. apisec_code_bolt/analysis/data_flow.py +1883 -0
  12. apisec_code_bolt/analysis/dependency_extractor.py +959 -0
  13. apisec_code_bolt/analysis/flow_analysis.py +1406 -0
  14. apisec_code_bolt/analysis/hof_catalog.py +61 -0
  15. apisec_code_bolt/analysis/integration_detector.py +1399 -0
  16. apisec_code_bolt/analysis/literal_scanner.py +300 -0
  17. apisec_code_bolt/analysis/path_normalizer.py +55 -0
  18. apisec_code_bolt/analysis/read_site_detector.py +310 -0
  19. apisec_code_bolt/analysis/request_patterns.py +162 -0
  20. apisec_code_bolt/analysis/sensitivity_classifier.py +224 -0
  21. apisec_code_bolt/analysis/sink_evidence.py +333 -0
  22. apisec_code_bolt/analysis/url_prefix_resolver.py +338 -0
  23. apisec_code_bolt/cli/__init__.py +5 -0
  24. apisec_code_bolt/cli/exit_codes.py +17 -0
  25. apisec_code_bolt/cli/main.py +1069 -0
  26. apisec_code_bolt/cloud/__init__.py +1 -0
  27. apisec_code_bolt/cloud/apisec_client.py +118 -0
  28. apisec_code_bolt/cloud/client.py +255 -0
  29. apisec_code_bolt/core/__init__.py +75 -0
  30. apisec_code_bolt/core/config.py +528 -0
  31. apisec_code_bolt/core/credentials.py +65 -0
  32. apisec_code_bolt/core/discovery.py +433 -0
  33. apisec_code_bolt/core/log_format.py +115 -0
  34. apisec_code_bolt/core/manifest.py +1009 -0
  35. apisec_code_bolt/core/repo.py +280 -0
  36. apisec_code_bolt/core/state.py +59 -0
  37. apisec_code_bolt/core/telemetry.py +451 -0
  38. apisec_code_bolt/core/types.py +587 -0
  39. apisec_code_bolt/fingerprinting/__init__.py +1 -0
  40. apisec_code_bolt/frameworks/__init__.py +29 -0
  41. apisec_code_bolt/frameworks/_jwt_common.py +50 -0
  42. apisec_code_bolt/frameworks/auth_helpers.py +437 -0
  43. apisec_code_bolt/frameworks/base.py +608 -0
  44. apisec_code_bolt/frameworks/dotnet/__init__.py +17 -0
  45. apisec_code_bolt/frameworks/dotnet/_path_helpers.py +43 -0
  46. apisec_code_bolt/frameworks/dotnet/aspnet_plugin.py +2546 -0
  47. apisec_code_bolt/frameworks/dotnet/grpc_plugin.py +559 -0
  48. apisec_code_bolt/frameworks/dotnet/jwt_config_extractor.py +545 -0
  49. apisec_code_bolt/frameworks/dotnet/legacy_aspnet_plugin.py +732 -0
  50. apisec_code_bolt/frameworks/dotnet/refit_plugin.py +374 -0
  51. apisec_code_bolt/frameworks/dotnet/wcf_plugin.py +1239 -0
  52. apisec_code_bolt/frameworks/java/__init__.py +6 -0
  53. apisec_code_bolt/frameworks/java/_annotations.py +167 -0
  54. apisec_code_bolt/frameworks/java/_constraints.py +128 -0
  55. apisec_code_bolt/frameworks/java/graphql_plugin.py +287 -0
  56. apisec_code_bolt/frameworks/java/jaxrs_plugin.py +748 -0
  57. apisec_code_bolt/frameworks/java/jwt_config_extractor.py +361 -0
  58. apisec_code_bolt/frameworks/java/micronaut_plugin.py +1059 -0
  59. apisec_code_bolt/frameworks/java/spring_plugin.py +1293 -0
  60. apisec_code_bolt/frameworks/js/__init__.py +8 -0
  61. apisec_code_bolt/frameworks/js/express_plugin.py +391 -0
  62. apisec_code_bolt/frameworks/js/fastify_plugin.py +381 -0
  63. apisec_code_bolt/frameworks/js/graphql_plugin.py +198 -0
  64. apisec_code_bolt/frameworks/js/nestjs_plugin.py +423 -0
  65. apisec_code_bolt/frameworks/python/__init__.py +19 -0
  66. apisec_code_bolt/frameworks/python/celery_plugin.py +393 -0
  67. apisec_code_bolt/frameworks/python/click_plugin.py +427 -0
  68. apisec_code_bolt/frameworks/python/django_plugin.py +867 -0
  69. apisec_code_bolt/frameworks/python/fastapi/__init__.py +28 -0
  70. apisec_code_bolt/frameworks/python/fastapi/plugin.py +1390 -0
  71. apisec_code_bolt/frameworks/python/flask_plugin.py +205 -0
  72. apisec_code_bolt/frameworks/python/graphql_plugin.py +274 -0
  73. apisec_code_bolt/frameworks/python/prefect_plugin.py +251 -0
  74. apisec_code_bolt/frameworks/python/webhook_plugin.py +255 -0
  75. apisec_code_bolt/parsing/__init__.py +62 -0
  76. apisec_code_bolt/parsing/base.py +554 -0
  77. apisec_code_bolt/parsing/csharp/__init__.py +5 -0
  78. apisec_code_bolt/parsing/csharp/language_services.py +203 -0
  79. apisec_code_bolt/parsing/csharp/literals.py +72 -0
  80. apisec_code_bolt/parsing/csharp/parser.py +1158 -0
  81. apisec_code_bolt/parsing/csharp/type_resolver.py +568 -0
  82. apisec_code_bolt/parsing/js/__init__.py +5 -0
  83. apisec_code_bolt/parsing/js/language_services.py +118 -0
  84. apisec_code_bolt/parsing/js/parser.py +622 -0
  85. apisec_code_bolt/parsing/jvm/__init__.py +7 -0
  86. apisec_code_bolt/parsing/jvm/language_services.py +270 -0
  87. apisec_code_bolt/parsing/jvm/parser.py +774 -0
  88. apisec_code_bolt/parsing/jvm/type_resolver.py +422 -0
  89. apisec_code_bolt/parsing/python/__init__.py +150 -0
  90. apisec_code_bolt/parsing/python/cbv_extractor.py +606 -0
  91. apisec_code_bolt/parsing/python/constant_resolver.py +500 -0
  92. apisec_code_bolt/parsing/python/cross_file_resolver.py +1054 -0
  93. apisec_code_bolt/parsing/python/dynamic_route_detector.py +532 -0
  94. apisec_code_bolt/parsing/python/expression_utils.py +221 -0
  95. apisec_code_bolt/parsing/python/extraction_types.py +271 -0
  96. apisec_code_bolt/parsing/python/language_services.py +487 -0
  97. apisec_code_bolt/parsing/python/parameter_analyzer.py +789 -0
  98. apisec_code_bolt/parsing/python/parser.py +719 -0
  99. apisec_code_bolt/parsing/python/path_resolver.py +576 -0
  100. apisec_code_bolt/parsing/python/router_registry.py +806 -0
  101. apisec_code_bolt/parsing/python/type_resolver.py +730 -0
  102. apisec_code_bolt/parsing/python/visitors.py +1544 -0
  103. apisec_code_bolt/parsing/services.py +544 -0
  104. apisec_code_bolt/query/__init__.py +1 -0
  105. apisec_code_bolt/query/ast_cache.py +182 -0
  106. apisec_code_bolt/query/executor.py +283 -0
  107. apisec_code_bolt/query/handlers.py +832 -0
  108. apisec_code_bolt-0.1.0.dist-info/METADATA +230 -0
  109. apisec_code_bolt-0.1.0.dist-info/RECORD +111 -0
  110. apisec_code_bolt-0.1.0.dist-info/WHEEL +4 -0
  111. apisec_code_bolt-0.1.0.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,587 @@
1
+ """
2
+ Core type definitions for apisec-code-bolt.
3
+
4
+ This module defines all enums, protocols, and base types used throughout the codebase.
5
+ We use strong typing everywhere - no magic strings where enums should be used.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from enum import Enum, auto
12
+ from pathlib import Path
13
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
14
+
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Iterator, Sequence
17
+
18
+
19
+ # =============================================================================
20
+ # Language and Framework Enums
21
+ # =============================================================================
22
+
23
+
24
+ class Language(Enum):
25
+ """
26
+ Programming languages supported by the analyzer.
27
+
28
+ Only add languages here when parser support is implemented.
29
+ This enum drives parser selection and framework detection.
30
+ """
31
+
32
+ PYTHON = auto()
33
+ JAVA = auto()
34
+ CSHARP = auto()
35
+ JAVASCRIPT = auto()
36
+
37
+
38
+ class Framework(Enum):
39
+ """
40
+ Frameworks we detect and provide specialized analysis for.
41
+
42
+ Only add frameworks here when extraction logic is implemented.
43
+ Includes web frameworks, CLI frameworks, and task/worker frameworks.
44
+ """
45
+
46
+ # Python web frameworks
47
+ FASTAPI = auto()
48
+ FLASK = auto()
49
+ DJANGO = auto()
50
+
51
+ # Python CLI frameworks
52
+ CLICK = auto()
53
+ TYPER = auto()
54
+
55
+ # Python task / worker frameworks
56
+ CELERY = auto()
57
+ DRAMATIQ = auto()
58
+ PREFECT = auto()
59
+
60
+ # Java frameworks
61
+ SPRING_BOOT = auto()
62
+ MICRONAUT = auto()
63
+ JAXRS = auto() # Jersey, Quarkus REST, RESTEasy — javax.ws.rs / jakarta.ws.rs
64
+
65
+ # .NET / C# frameworks
66
+ ASPNET_CORE = auto()
67
+ ASPNET_LEGACY = auto() # System.Web.Mvc / System.Web.Http (pre-.NET Core)
68
+ REFIT = auto() # Refit declarative HTTP client interfaces
69
+ GRPC = auto() # Grpc.Core / Grpc.AspNetCore.Server (server-side)
70
+ WCF = auto() # Windows Communication Foundation / CoreWCF
71
+
72
+ # JavaScript / TypeScript frameworks
73
+ EXPRESS = auto()
74
+ NESTJS = auto()
75
+ FASTIFY = auto()
76
+
77
+ # GraphQL (cross-language)
78
+ GRAPHQL = auto()
79
+
80
+ # Generic fallback
81
+ GENERIC = auto()
82
+
83
+
84
+ # =============================================================================
85
+ # Data Origin Types (Generic - not vulnerability-specific)
86
+ # =============================================================================
87
+
88
+
89
+ class OriginType(Enum):
90
+ """
91
+ Types of data origins - where data enters the application.
92
+
93
+ These are OBSERVABLE FACTS about data sources, not security judgments.
94
+ The Cloud applies vulnerability rules to interpret these.
95
+
96
+ Member names are the canonical serialization strings written into
97
+ manifests and matched by the reasoning engine's _ORIGIN_CATEGORY_MAP.
98
+ Do NOT rename members without updating the engine map.
99
+ """
100
+
101
+ # HTTP request data
102
+ HTTP_PATH_PARAM = auto() # /users/{user_id}
103
+ HTTP_QUERY_PARAM = auto() # ?filter=value
104
+ HTTP_BODY = auto() # POST/PUT body
105
+ HTTP_HEADER = auto() # Request headers
106
+ HTTP_COOKIE = auto() # Cookie values
107
+ HTTP_FORM = auto() # Form data
108
+ HTTP_FILE = auto() # Uploaded files
109
+
110
+ # Environment and configuration
111
+ ENVIRONMENT_VAR = auto() # os.environ, System.getenv
112
+ CONFIG_VALUE = auto() # Loaded from config files
113
+
114
+ # External data sources
115
+ FILE_CONTENT = auto() # Read from filesystem
116
+ DATABASE_RESULT = auto() # Query result from DB
117
+ EXTERNAL_API = auto() # Response from HTTP call
118
+ MESSAGE_QUEUE = auto() # Message from queue/broker
119
+
120
+ # Code-level origins
121
+ FUNCTION_PARAMETER = auto() # Parameter passed to function
122
+ FUNCTION_RETURN = auto() # Return value from function
123
+ CLASS_ATTRIBUTE = auto() # Instance/class attribute
124
+ LITERAL = auto() # Hardcoded literal value
125
+
126
+ # Generic
127
+ USER_INPUT = auto() # Generic user input
128
+
129
+ # Unknown/unresolved
130
+ UNKNOWN = auto()
131
+
132
+
133
+ class TransformationType(Enum):
134
+ """
135
+ Types of transformations applied to data as it flows.
136
+
137
+ This helps the Cloud understand what happened to data.
138
+ """
139
+
140
+ # String operations
141
+ STRING_CONCATENATION = auto() # "prefix" + value
142
+ STRING_FORMAT = auto() # f"...{value}..." or .format()
143
+ STRING_INTERPOLATION = auto() # Template strings
144
+
145
+ # Encoding/decoding
146
+ URL_ENCODE = auto()
147
+ URL_DECODE = auto()
148
+ BASE64_ENCODE = auto()
149
+ BASE64_DECODE = auto()
150
+ JSON_SERIALIZE = auto()
151
+ JSON_DESERIALIZE = auto()
152
+
153
+ # Type conversions
154
+ TYPE_CAST = auto() # int(value), str(value)
155
+ TYPE_COERCION = auto() # Implicit conversion
156
+
157
+ # Collection operations
158
+ LIST_INDEX = auto() # value[0]
159
+ DICT_ACCESS = auto() # value["key"]
160
+ ATTRIBUTE_ACCESS = auto() # value.attr
161
+
162
+ # Function calls
163
+ FUNCTION_CALL = auto() # some_function(value)
164
+ METHOD_CALL = auto() # obj.method(value)
165
+
166
+ # Assignments
167
+ VARIABLE_ASSIGNMENT = auto() # x = value
168
+ DESTRUCTURING = auto() # a, b = value
169
+
170
+ # Unknown
171
+ UNKNOWN = auto()
172
+
173
+
174
+ # =============================================================================
175
+ # Authentication Types
176
+ # =============================================================================
177
+
178
+
179
+ class AuthSchemeType(Enum):
180
+ """
181
+ Types of authentication schemes we detect.
182
+
183
+ These represent observable authentication patterns, not security judgments.
184
+ """
185
+
186
+ # OAuth2 variants
187
+ OAUTH2_PASSWORD = auto() # Resource Owner Password Grant
188
+ OAUTH2_AUTHORIZATION_CODE = auto() # Authorization Code Grant
189
+ OAUTH2_CLIENT_CREDENTIALS = auto() # Client Credentials Grant
190
+ OAUTH2_IMPLICIT = auto() # Implicit Grant (deprecated but exists)
191
+
192
+ # Token-based
193
+ JWT_BEARER = auto() # JWT in Authorization: Bearer header
194
+ API_KEY_HEADER = auto() # API key in custom header
195
+ API_KEY_QUERY = auto() # API key in query string
196
+ API_KEY_COOKIE = auto() # API key in cookie
197
+
198
+ # Session-based
199
+ SESSION_COOKIE = auto() # Server-side session with cookie
200
+
201
+ # Basic authentication
202
+ HTTP_BASIC = auto() # Basic auth header
203
+ HTTP_DIGEST = auto() # Digest auth header
204
+
205
+ # Framework-specific
206
+ SPRING_SECURITY = auto() # Spring Security configuration
207
+
208
+ # Unknown/custom
209
+ CUSTOM = auto()
210
+ UNKNOWN = auto()
211
+
212
+
213
+ class AuthDependencyType(Enum):
214
+ """Types of authentication dependencies/guards."""
215
+
216
+ FUNCTION = auto() # Dependency is a function
217
+ CLASS = auto() # Dependency is a class
218
+ DECORATOR = auto() # Dependency is a decorator
219
+ ANNOTATION = auto() # Dependency is an annotation (Java)
220
+ MIDDLEWARE = auto() # Authentication in middleware
221
+ FILTER = auto() # Authentication in filter (Java)
222
+ INTERCEPTOR = auto() # Authentication in interceptor (Java)
223
+
224
+
225
+ # =============================================================================
226
+ # HTTP Types
227
+ # =============================================================================
228
+
229
+
230
+ class HttpMethod(Enum):
231
+ """HTTP methods for route definitions."""
232
+
233
+ GET = auto()
234
+ POST = auto()
235
+ PUT = auto()
236
+ PATCH = auto()
237
+ DELETE = auto()
238
+ HEAD = auto()
239
+ OPTIONS = auto()
240
+ TRACE = auto()
241
+
242
+ # WebSocket (special case)
243
+ WEBSOCKET = auto()
244
+
245
+
246
+ class ParameterLocation(Enum):
247
+ """Where a parameter comes from in a request or entry point invocation."""
248
+
249
+ # HTTP parameter locations
250
+ PATH = auto()
251
+ QUERY = auto()
252
+ HEADER = auto()
253
+ COOKIE = auto()
254
+ BODY = auto()
255
+ FORM = auto()
256
+
257
+ # CLI parameter locations
258
+ CLI_ARGUMENT = auto()
259
+ CLI_OPTION = auto()
260
+
261
+ # Task / message consumer parameter locations
262
+ TASK_ARGUMENT = auto()
263
+
264
+
265
+ # =============================================================================
266
+ # Code Location Types
267
+ # =============================================================================
268
+
269
+
270
+ @dataclass(frozen=True, slots=True)
271
+ class CodeLocation:
272
+ """
273
+ Precise location in source code.
274
+
275
+ Immutable and hashable for use as dict keys and in sets.
276
+ """
277
+
278
+ file: Path
279
+ line: int
280
+ column: int | None = None
281
+ end_line: int | None = None
282
+ end_column: int | None = None
283
+
284
+ def __str__(self) -> str:
285
+ if self.column is not None:
286
+ return f"{self.file}:{self.line}:{self.column}"
287
+ return f"{self.file}:{self.line}"
288
+
289
+
290
+ @dataclass(frozen=True, slots=True)
291
+ class QualifiedName:
292
+ """
293
+ Fully qualified name for a symbol (function, class, method).
294
+
295
+ Examples:
296
+ - "app.routes.users.get_user"
297
+ - "com.example.UserController.getUser"
298
+ """
299
+
300
+ module: str # "app.routes.users" or "com.example"
301
+ name: str # "get_user" or "UserController.getUser"
302
+
303
+ @property
304
+ def full(self) -> str:
305
+ """Full qualified name."""
306
+ if self.module:
307
+ return f"{self.module}.{self.name}"
308
+ return self.name
309
+
310
+ def __str__(self) -> str:
311
+ return self.full
312
+
313
+
314
+ # =============================================================================
315
+ # Analysis Context Types
316
+ # =============================================================================
317
+
318
+
319
+ @dataclass(slots=True)
320
+ class CallContext:
321
+ """
322
+ Context about where a function call occurs.
323
+
324
+ This provides valuable information for security analysis:
325
+ - Is the call inside a try block? (affects error handling)
326
+ - Is it inside a conditional? (affects reachability)
327
+ """
328
+
329
+ in_try_block: bool = False
330
+ exception_handlers: list[str] = field(default_factory=list)
331
+ exception_swallowed: bool = False # except: pass
332
+
333
+ in_conditional: bool = False
334
+ conditional_expressions: list[str] = field(default_factory=list)
335
+ guards_this_call: bool = False # Is the conditional a guard for this call?
336
+
337
+ in_loop: bool = False
338
+ loop_type: str | None = None # "for", "while"
339
+
340
+ in_async_context: bool = False
341
+
342
+
343
+ @dataclass(slots=True)
344
+ class FlowContext:
345
+ """
346
+ Context about a data flow's execution environment.
347
+ """
348
+
349
+ entry_point_id: str | None = None
350
+ in_try_block: bool = False
351
+ exception_handlers: list[str] = field(default_factory=list)
352
+ guarded_by_conditionals: list[str] = field(default_factory=list)
353
+ in_loop: bool = False
354
+
355
+
356
+ # =============================================================================
357
+ # Confidence and Analysis Metadata
358
+ # =============================================================================
359
+
360
+
361
+ class Confidence(Enum):
362
+ """
363
+ Confidence level in an analysis result.
364
+
365
+ Used to indicate how certain we are about detected patterns.
366
+ """
367
+
368
+ HIGH = auto() # Definitely correct
369
+ MEDIUM = auto() # Likely correct, may need verification
370
+ LOW = auto() # Uncertain, needs Cloud verification
371
+ UNKNOWN = auto() # Could not determine
372
+
373
+
374
+ @dataclass(slots=True)
375
+ class AnalysisNote:
376
+ """
377
+ Note or warning about analysis quality for a specific item.
378
+ """
379
+
380
+ level: str # "info", "warning", "error"
381
+ message: str
382
+ location: CodeLocation | None = None
383
+ details: dict[str, Any] = field(default_factory=dict)
384
+
385
+
386
+ # =============================================================================
387
+ # Protocol Definitions (Interfaces)
388
+ # =============================================================================
389
+
390
+
391
+ @runtime_checkable
392
+ class Visitable(Protocol):
393
+ """Protocol for AST nodes that can be visited."""
394
+
395
+ def accept(self, visitor: Any) -> Any:
396
+ """Accept a visitor."""
397
+ ...
398
+
399
+
400
+ @runtime_checkable
401
+ class Parser(Protocol):
402
+ """
403
+ Protocol for language-specific parsers.
404
+
405
+ Each language (Python, Java, etc.) implements this protocol
406
+ to provide parsing capabilities.
407
+ """
408
+
409
+ @property
410
+ def language(self) -> Language:
411
+ """The language this parser handles."""
412
+ ...
413
+
414
+ @property
415
+ def supported_extensions(self) -> frozenset[str]:
416
+ """File extensions this parser handles (e.g., {'.py'})."""
417
+ ...
418
+
419
+ def can_parse(self, file_path: Path) -> bool:
420
+ """Check if this parser can handle the given file."""
421
+ ...
422
+
423
+ def parse_file(self, file_path: Path) -> ParsedFile:
424
+ """
425
+ Parse a single file and return structured data.
426
+
427
+ Raises:
428
+ ParseError: If the file cannot be parsed.
429
+ """
430
+ ...
431
+
432
+ def parse_files(self, file_paths: Sequence[Path]) -> Iterator[ParsedFile]:
433
+ """
434
+ Parse multiple files, yielding results.
435
+
436
+ This allows for streaming/incremental processing.
437
+ """
438
+ ...
439
+
440
+
441
+ @runtime_checkable
442
+ class FrameworkPlugin(Protocol):
443
+ """
444
+ Protocol for framework-specific extraction plugins.
445
+
446
+ Each framework (FastAPI, Spring Boot, etc.) implements this
447
+ to provide specialized route and dependency extraction.
448
+ """
449
+
450
+ @property
451
+ def framework(self) -> Framework:
452
+ """The framework this plugin handles."""
453
+ ...
454
+
455
+ @property
456
+ def language(self) -> Language:
457
+ """The language this framework is for."""
458
+ ...
459
+
460
+ def detect(self, parsed_file: ParsedFile) -> bool:
461
+ """
462
+ Detect if this framework is used in the parsed file.
463
+
464
+ Returns True if framework-specific patterns are found.
465
+ """
466
+ ...
467
+
468
+ def extract_routes(self, parsed_file: ParsedFile) -> list[RouteDefinition]:
469
+ """Extract HTTP route definitions from the file."""
470
+ ...
471
+
472
+ def extract_dependencies(self, parsed_file: ParsedFile) -> list[DependencyDefinition]:
473
+ """Extract dependency injection definitions."""
474
+ ...
475
+
476
+ def extract_auth(self, parsed_file: ParsedFile) -> list[AuthDefinition]:
477
+ """Extract authentication-related definitions."""
478
+ ...
479
+
480
+ def extract_middleware(self, parsed_file: ParsedFile) -> list[MiddlewareDefinition]:
481
+ """Extract middleware/interceptor definitions."""
482
+ ...
483
+
484
+
485
+ # =============================================================================
486
+ # Forward Declarations (to be fully defined in manifest.py)
487
+ # =============================================================================
488
+
489
+ # These are declared here so they can be referenced in protocols above.
490
+ # Full definitions with all fields are in manifest.py
491
+
492
+
493
+ @dataclass
494
+ class ParsedFile:
495
+ """Represents a parsed source file."""
496
+
497
+ path: Path
498
+ language: Language
499
+ # Full definition in manifest.py
500
+
501
+
502
+ @dataclass
503
+ class RouteDefinition:
504
+ """Represents an HTTP route definition."""
505
+
506
+ id: str
507
+ method: HttpMethod
508
+ path: str
509
+ # Full definition in manifest.py
510
+
511
+
512
+ @dataclass
513
+ class DependencyDefinition:
514
+ """Represents a dependency injection definition."""
515
+
516
+ id: str
517
+ name: str
518
+ # Full definition in manifest.py
519
+
520
+
521
+ @dataclass
522
+ class AuthDefinition:
523
+ """Represents an authentication definition."""
524
+
525
+ id: str
526
+ scheme_type: AuthSchemeType
527
+ # Full definition in manifest.py
528
+
529
+
530
+ @dataclass
531
+ class MiddlewareDefinition:
532
+ """Represents a middleware/interceptor definition."""
533
+
534
+ id: str
535
+ name: str
536
+ # Full definition in manifest.py
537
+
538
+
539
+ # =============================================================================
540
+ # Exception Types
541
+ # =============================================================================
542
+
543
+
544
+ class CodeBoltError(Exception):
545
+ """Base exception for all apisec-code-bolt errors."""
546
+
547
+ pass
548
+
549
+
550
+ class ParseError(CodeBoltError):
551
+ """Error during parsing."""
552
+
553
+ def __init__(
554
+ self,
555
+ message: str,
556
+ file_path: Path | None = None,
557
+ line: int | None = None,
558
+ column: int | None = None,
559
+ ):
560
+ super().__init__(message)
561
+ self.file_path = file_path
562
+ self.line = line
563
+ self.column = column
564
+
565
+
566
+ class ConfigurationError(CodeBoltError):
567
+ """Error in configuration."""
568
+
569
+ pass
570
+
571
+
572
+ class AnalysisError(CodeBoltError):
573
+ """Error during analysis."""
574
+
575
+ pass
576
+
577
+
578
+ class CloudCommunicationError(CodeBoltError):
579
+ """Error communicating with cloud services."""
580
+
581
+ pass
582
+
583
+
584
+ class TimeoutError(CodeBoltError):
585
+ """Analysis timeout."""
586
+
587
+ pass
@@ -0,0 +1 @@
1
+ """Integration detection and capability fingerprinting."""
@@ -0,0 +1,29 @@
1
+ """Framework-specific extraction plugins."""
2
+
3
+ from .base import (
4
+ BaseFrameworkPlugin,
5
+ ExtractedAuthDependency,
6
+ ExtractedAuthScheme,
7
+ ExtractedBody,
8
+ ExtractedDependency,
9
+ ExtractedJwtConfig,
10
+ ExtractedMiddleware,
11
+ ExtractedParameter,
12
+ ExtractedResponse,
13
+ ExtractedRoute,
14
+ FrameworkPluginRegistry,
15
+ )
16
+
17
+ __all__ = [
18
+ "BaseFrameworkPlugin",
19
+ "FrameworkPluginRegistry",
20
+ "ExtractedRoute",
21
+ "ExtractedParameter",
22
+ "ExtractedBody",
23
+ "ExtractedResponse",
24
+ "ExtractedDependency",
25
+ "ExtractedAuthScheme",
26
+ "ExtractedAuthDependency",
27
+ "ExtractedJwtConfig",
28
+ "ExtractedMiddleware",
29
+ ]
@@ -0,0 +1,50 @@
1
+ """
2
+ Shared JWT algorithm helpers.
3
+
4
+ The canonical JWA algorithm set is identical across every JWT library
5
+ (HS256/RS256/ES256/PS256 families). The normalization and append-to-config
6
+ helpers used by `JavaJwtConfigExtractor` and `DotNetJwtConfigExtractor`
7
+ were near-byte-identical — promoted here so each extractor only carries
8
+ the framework-specific aliases.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from .base import ExtractedJwtConfig
14
+
15
+ # Canonical JWA algorithm names (RFC 7518). Both Java and .NET extractors
16
+ # share this set; framework-specific aliases live alongside each extractor.
17
+ ALGORITHM_NAMES: frozenset[str] = frozenset(
18
+ {
19
+ "HS256",
20
+ "HS384",
21
+ "HS512",
22
+ "RS256",
23
+ "RS384",
24
+ "RS512",
25
+ "ES256",
26
+ "ES384",
27
+ "ES512",
28
+ "PS256",
29
+ "PS384",
30
+ "PS512",
31
+ }
32
+ )
33
+
34
+
35
+ def normalize_algorithm(raw: str, aliases: dict[str, str]) -> str | None:
36
+ """Return a canonical JWA algorithm name, or None if unrecognised.
37
+
38
+ Upper-cases and strips `-` / `_` so framework-specific forms like
39
+ `HMAC-SHA256` or `RSA_SHA256` collapse to the alias-lookup key.
40
+ """
41
+ upper = raw.upper().replace("-", "").replace("_", "")
42
+ if upper in ALGORITHM_NAMES:
43
+ return upper
44
+ return aliases.get(upper)
45
+
46
+
47
+ def add_algorithm(algo: str | None, jwt_config: ExtractedJwtConfig) -> None:
48
+ """Append an algorithm to the config if non-empty and not already present."""
49
+ if algo and algo not in jwt_config.algorithms:
50
+ jwt_config.algorithms.append(algo)