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,423 @@
1
+ """
2
+ NestJS framework plugin.
3
+
4
+ Extracts HTTP routes from NestJS @Controller + @Get/@Post/@Put/@Patch/@Delete
5
+ decorator patterns. Supports:
6
+ - @Controller('prefix') and @Controller({ path: 'prefix' })
7
+ - Verb decorators: @Get, @Post, @Put, @Patch, @Delete, @Options, @Head, @All
8
+ - Path params: :id → {id}
9
+ - Auth: @UseGuards, @ApiBearerAuth, etc.
10
+ - Versioned controllers
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import logging
16
+ import re
17
+ from typing import TYPE_CHECKING, Any
18
+
19
+ from ...core.types import (
20
+ AuthDependencyType,
21
+ AuthSchemeType,
22
+ CodeLocation,
23
+ Confidence,
24
+ Framework,
25
+ HttpMethod,
26
+ Language,
27
+ ParameterLocation,
28
+ QualifiedName,
29
+ )
30
+ from ...parsing.base import ParsedDecorator, ParsedFile
31
+ from ..base import (
32
+ BaseFrameworkPlugin,
33
+ ExtractedAuthDependency,
34
+ ExtractedAuthScheme,
35
+ ExtractedDependency,
36
+ ExtractedMiddleware,
37
+ ExtractedParameter,
38
+ ExtractedRoute,
39
+ FrameworkPluginRegistry,
40
+ )
41
+
42
+ if TYPE_CHECKING:
43
+ from ...parsing.services import AnalysisContext
44
+
45
+ logger = logging.getLogger(__name__)
46
+
47
+ # NestJS HTTP verb decorators → HttpMethod
48
+ _VERB_DECORATORS: dict[str, HttpMethod] = {
49
+ "Get": HttpMethod.GET,
50
+ "Post": HttpMethod.POST,
51
+ "Put": HttpMethod.PUT,
52
+ "Patch": HttpMethod.PATCH,
53
+ "Delete": HttpMethod.DELETE,
54
+ "Options": HttpMethod.OPTIONS,
55
+ "Head": HttpMethod.HEAD,
56
+ "All": HttpMethod.GET,
57
+ }
58
+
59
+ # Auth-related decorators
60
+ _AUTH_DECORATORS: frozenset[str] = frozenset(
61
+ {
62
+ "UseGuards",
63
+ "Roles",
64
+ "Public",
65
+ "ApiBearerAuth",
66
+ "ApiSecurity",
67
+ "Auth",
68
+ "Authorize",
69
+ }
70
+ )
71
+
72
+ # Regex to convert NestJS :param to {param}
73
+ _PARAM_RE = re.compile(r":([A-Za-z_]\w*)")
74
+ # Regex to extract 'path' from object literal { path: 'xxx' }
75
+ _OBJ_PATH_RE = re.compile(r"path\s*:\s*['\"]([^'\"]+)['\"]")
76
+
77
+
78
+ def _colon_to_curly(path: str) -> str:
79
+ return _PARAM_RE.sub(r"{\1}", path)
80
+
81
+
82
+ def _extract_path_params(path: str) -> list[ExtractedParameter]:
83
+ return [
84
+ ExtractedParameter(name=m.group(1), location=ParameterLocation.PATH)
85
+ for m in _PARAM_RE.finditer(path)
86
+ ]
87
+
88
+
89
+ def _get_decorator_path(dec: ParsedDecorator) -> str:
90
+ """Extract the path string from a decorator (string arg or { path: '...' } object)."""
91
+ if not dec.positional_args:
92
+ return ""
93
+
94
+ value = dec.positional_args[0]
95
+ if not isinstance(value, str):
96
+ return ""
97
+
98
+ # Check if it looks like an object literal
99
+ if value.strip().startswith("{"):
100
+ m = _OBJ_PATH_RE.search(value)
101
+ if m:
102
+ return m.group(1)
103
+ return ""
104
+
105
+ return value
106
+
107
+
108
+ class NestJSPlugin(BaseFrameworkPlugin):
109
+ """
110
+ Framework plugin for NestJS.
111
+
112
+ Detects NestJS usage via @nestjs/* imports and extracts routes
113
+ from @Controller + @Get/@Post etc. decorator patterns.
114
+ """
115
+
116
+ FRAMEWORK = Framework.NESTJS
117
+ LANGUAGE = Language.JAVASCRIPT
118
+ DETECTION_IMPORTS: frozenset[str] = frozenset(
119
+ {
120
+ "@nestjs/common",
121
+ "@nestjs/core",
122
+ "@nestjs/swagger",
123
+ }
124
+ )
125
+
126
+ def detect(self, parsed_file: ParsedFile) -> bool:
127
+ """Detect NestJS by looking for @nestjs/* imports."""
128
+ return any(imp.module.startswith("@nestjs/") for imp in parsed_file.imports)
129
+
130
+ def extract_routes(
131
+ self,
132
+ parsed_file: ParsedFile,
133
+ context: AnalysisContext | None = None,
134
+ ) -> list[ExtractedRoute]:
135
+ """Extract routes from @Controller + @Get/@Post etc. patterns."""
136
+ routes: list[ExtractedRoute] = []
137
+
138
+ for cls in parsed_file.classes:
139
+ # Find @Controller decorator
140
+ controller_dec = _find_decorator(cls.decorators, "Controller")
141
+ if controller_dec is None:
142
+ continue
143
+
144
+ # Extract controller prefix
145
+ prefix = _get_decorator_path(controller_dec)
146
+
147
+ # Process each method
148
+ for method in cls.methods:
149
+ # Find a verb decorator
150
+ verb_dec = _find_verb_decorator(method.decorators)
151
+ if verb_dec is None:
152
+ continue
153
+
154
+ http_method = _VERB_DECORATORS[verb_dec.name]
155
+
156
+ # Get route path from decorator
157
+ method_path = _get_decorator_path(verb_dec)
158
+
159
+ # Build full path
160
+ if prefix and method_path:
161
+ full_path = prefix.rstrip("/") + "/" + method_path.lstrip("/")
162
+ elif prefix:
163
+ full_path = "/" + prefix.lstrip("/")
164
+ elif method_path:
165
+ full_path = "/" + method_path.lstrip("/")
166
+ else:
167
+ full_path = "/"
168
+
169
+ # Convert :param to {param}
170
+ full_path = _colon_to_curly(full_path)
171
+ if not full_path.startswith("/"):
172
+ full_path = "/" + full_path
173
+ full_path = re.sub(r"/+", "/", full_path)
174
+
175
+ # Extract path params from combined path
176
+ path_params = _extract_path_params(prefix + "/" + method_path)
177
+
178
+ # Auth: check for @UseGuards or similar
179
+ auth_guard: str | None = None
180
+ for auth_dec in method.decorators + cls.decorators:
181
+ if auth_dec.name in _AUTH_DECORATORS:
182
+ guard_val = (
183
+ auth_dec.positional_args[0]
184
+ if auth_dec.positional_args
185
+ else auth_dec.name
186
+ )
187
+ auth_guard = str(guard_val)
188
+ break
189
+
190
+ # Handler name: ClassName.methodName
191
+ handler_name = f"{cls.name}.{method.name}"
192
+
193
+ # Use decorator's location (line number) for benchmark accuracy
194
+ handler_location = verb_dec.location or method.location
195
+
196
+ routes.append(
197
+ ExtractedRoute(
198
+ method=http_method,
199
+ path=full_path,
200
+ handler_function=QualifiedName(
201
+ module=parsed_file.path.stem,
202
+ name=handler_name,
203
+ ),
204
+ handler_location=handler_location,
205
+ path_params=path_params,
206
+ router_name=auth_guard,
207
+ )
208
+ )
209
+
210
+ return routes
211
+
212
+ def extract_dependencies(self, parsed_file: ParsedFile) -> list[ExtractedDependency]:
213
+ return []
214
+
215
+ def extract_auth_schemes(self, parsed_file: ParsedFile) -> list[ExtractedAuthScheme]:
216
+ """
217
+ Detect NestJS auth scheme definitions.
218
+
219
+ Covers:
220
+ - @ApiBearerAuth() import/usage → JWT_BEARER scheme
221
+ - AuthGuard('strategy') references → named Passport strategies
222
+ - Passport strategy class definitions (extends PassportStrategy)
223
+ """
224
+ schemes: list[ExtractedAuthScheme] = []
225
+ seen_names: set[str] = set()
226
+
227
+ def _add(name: str, scheme_type: AuthSchemeType, line: int) -> None:
228
+ if name not in seen_names:
229
+ seen_names.add(name)
230
+ schemes.append(
231
+ ExtractedAuthScheme(
232
+ scheme_type=scheme_type,
233
+ name=name,
234
+ location=CodeLocation(file=parsed_file.path, line=line),
235
+ confidence=Confidence.HIGH,
236
+ )
237
+ )
238
+
239
+ # ── Import-based: @ApiBearerAuth / AuthGuard ─────────────────────────
240
+ for imp in parsed_file.imports:
241
+ for name in imp.names:
242
+ if name == "ApiBearerAuth":
243
+ _add("BearerAuth", AuthSchemeType.JWT_BEARER, 1)
244
+ elif name == "AuthGuard":
245
+ _add("AuthGuard", AuthSchemeType.JWT_BEARER, 1)
246
+ elif name in ("JwtAuthGuard", "JwtGuard"):
247
+ _add(name, AuthSchemeType.JWT_BEARER, 1)
248
+
249
+ # ── Class-based: Passport strategy definitions ───────────────────────
250
+ for cls in parsed_file.classes:
251
+ base_names_lower = {b.lower() for b in cls.base_classes}
252
+ if "passportstrategy" in base_names_lower or any(
253
+ "strategy" in b for b in base_names_lower
254
+ ):
255
+ hint = cls.name.lower()
256
+ scheme_type = AuthSchemeType.JWT_BEARER if "jwt" in hint else AuthSchemeType.CUSTOM
257
+ line = cls.location.line if cls.location else 1
258
+ _add(cls.name, scheme_type, line)
259
+
260
+ # ── Usage-based: AuthGuard('strategy-name') call sites ───────────────
261
+ for call in parsed_file.call_sites:
262
+ if call.callee_name != "AuthGuard":
263
+ continue
264
+ if call.arguments and call.arguments[0].is_literal:
265
+ strategy = str(call.arguments[0].literal_value)
266
+ scheme_type = (
267
+ AuthSchemeType.JWT_BEARER
268
+ if "jwt" in strategy.lower()
269
+ else AuthSchemeType.CUSTOM
270
+ )
271
+ line = call.location.line if call.location else 1
272
+ _add(f"AuthGuard({strategy})", scheme_type, line)
273
+
274
+ return schemes
275
+
276
+ def extract_auth_dependencies(
277
+ self,
278
+ parsed_file: ParsedFile,
279
+ known_scheme_names: set[str] | None = None,
280
+ **kwargs: Any,
281
+ ) -> list[ExtractedAuthDependency]:
282
+ """
283
+ Detect NestJS auth requirements from decorator patterns.
284
+
285
+ Covers:
286
+ - @UseGuards(AuthGuard('jwt'), RolesGuard) — class or method level
287
+ - @Roles(RoleEnum.admin) / @Roles('admin') — role requirements
288
+ - @ApiBearerAuth() — marks route as requiring Bearer token
289
+ - Class-level decorators apply to all methods in the controller
290
+
291
+ For each controller class with auth decorators, emits one dependency
292
+ capturing the guards and roles. Method-level overrides are also captured.
293
+ """
294
+ deps: list[ExtractedAuthDependency] = []
295
+
296
+ for cls in parsed_file.classes:
297
+ # Class-level guards and roles (apply to all methods)
298
+ class_guards = _extract_guards(cls.decorators)
299
+ class_roles = _extract_roles(cls.decorators)
300
+ class_bearer = any(d.name == "ApiBearerAuth" for d in cls.decorators)
301
+
302
+ if class_guards or class_roles or class_bearer:
303
+ uses_schemes = _guards_to_scheme_names(class_guards)
304
+ if class_bearer and "BearerAuth" not in uses_schemes:
305
+ uses_schemes.append("BearerAuth")
306
+ line = cls.location.line if cls.location else 1
307
+ deps.append(
308
+ ExtractedAuthDependency(
309
+ name=cls.name,
310
+ qualified_name=QualifiedName(
311
+ module=parsed_file.path.stem,
312
+ name=cls.name,
313
+ ),
314
+ location=CodeLocation(file=parsed_file.path, line=line),
315
+ dependency_type=AuthDependencyType.DECORATOR,
316
+ uses_schemes=uses_schemes,
317
+ requires_roles=class_roles,
318
+ confidence=Confidence.HIGH,
319
+ )
320
+ )
321
+
322
+ # Method-level guards/roles that differ from class-level
323
+ for method in cls.methods:
324
+ method_guards = _extract_guards(method.decorators)
325
+ method_roles = _extract_roles(method.decorators)
326
+ method_bearer = any(d.name == "ApiBearerAuth" for d in method.decorators)
327
+
328
+ if not method_guards and not method_roles and not method_bearer:
329
+ continue
330
+ # Only emit if method has guards/roles not already covered by class
331
+ if method_guards == class_guards and method_roles == class_roles:
332
+ continue
333
+
334
+ uses_schemes = _guards_to_scheme_names(method_guards)
335
+ if method_bearer and "BearerAuth" not in uses_schemes:
336
+ uses_schemes.append("BearerAuth")
337
+ line = method.location.line if method.location else 1
338
+ handler_name = f"{cls.name}.{method.name}"
339
+ deps.append(
340
+ ExtractedAuthDependency(
341
+ name=handler_name,
342
+ qualified_name=QualifiedName(
343
+ module=parsed_file.path.stem,
344
+ name=handler_name,
345
+ ),
346
+ location=CodeLocation(file=parsed_file.path, line=line),
347
+ dependency_type=AuthDependencyType.DECORATOR,
348
+ uses_schemes=uses_schemes,
349
+ requires_roles=method_roles,
350
+ confidence=Confidence.HIGH,
351
+ )
352
+ )
353
+
354
+ return deps
355
+
356
+ def extract_middleware(self, parsed_file: ParsedFile) -> list[ExtractedMiddleware]:
357
+ return []
358
+
359
+
360
+ def _find_decorator(
361
+ decorators: list[ParsedDecorator],
362
+ name: str,
363
+ ) -> ParsedDecorator | None:
364
+ """Find a decorator by name in a list."""
365
+ for dec in decorators:
366
+ if dec.name == name:
367
+ return dec
368
+ return None
369
+
370
+
371
+ def _find_verb_decorator(
372
+ decorators: list[ParsedDecorator],
373
+ ) -> ParsedDecorator | None:
374
+ """Find the first HTTP verb decorator in a list."""
375
+ for dec in decorators:
376
+ if dec.name in _VERB_DECORATORS:
377
+ return dec
378
+ return None
379
+
380
+
381
+ def _extract_guards(decorators: list[ParsedDecorator]) -> list[str]:
382
+ """Extract guard names from @UseGuards(...) decorators."""
383
+ guards: list[str] = []
384
+ for dec in decorators:
385
+ if dec.name != "UseGuards":
386
+ continue
387
+ for arg in dec.positional_args:
388
+ arg_str = str(arg).strip()
389
+ if arg_str:
390
+ guards.append(arg_str)
391
+ return guards
392
+
393
+
394
+ def _extract_roles(decorators: list[ParsedDecorator]) -> list[str]:
395
+ """Extract role names from @Roles(...) decorators."""
396
+ roles: list[str] = []
397
+ for dec in decorators:
398
+ if dec.name not in ("Roles", "Role"):
399
+ continue
400
+ for arg in dec.positional_args:
401
+ arg_str = str(arg).strip().strip("'\"")
402
+ if arg_str:
403
+ # Strip enum prefix: RoleEnum.admin → admin
404
+ if "." in arg_str:
405
+ arg_str = arg_str.split(".")[-1]
406
+ roles.append(arg_str)
407
+ return roles
408
+
409
+
410
+ def _guards_to_scheme_names(guards: list[str]) -> list[str]:
411
+ """Map guard names to auth scheme names."""
412
+ schemes: list[str] = []
413
+ for guard in guards:
414
+ low = guard.lower()
415
+ if "jwt" in low or "bearer" in low or "auth" in low:
416
+ if "BearerAuth" not in schemes:
417
+ schemes.append("BearerAuth")
418
+ elif guard not in schemes:
419
+ schemes.append(guard)
420
+ return schemes
421
+
422
+
423
+ FrameworkPluginRegistry.register(NestJSPlugin())
@@ -0,0 +1,19 @@
1
+ """Python framework plugins."""
2
+
3
+ from .celery_plugin import CeleryPlugin
4
+ from .click_plugin import ClickPlugin
5
+ from .django_plugin import DjangoPlugin
6
+ from .fastapi import FastAPIPlugin
7
+ from .graphql_plugin import GraphQLPythonPlugin
8
+ from .prefect_plugin import PrefectPlugin
9
+ from .webhook_plugin import WebhookEventPlugin
10
+
11
+ __all__ = [
12
+ "FastAPIPlugin",
13
+ "ClickPlugin",
14
+ "CeleryPlugin",
15
+ "DjangoPlugin",
16
+ "GraphQLPythonPlugin",
17
+ "PrefectPlugin",
18
+ "WebhookEventPlugin",
19
+ ]