apisec-code-bolt 0.1.6__tar.gz → 0.1.7__tar.gz

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 (248) hide show
  1. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/PKG-INFO +1 -1
  2. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/pyproject.toml +1 -1
  3. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/dependency_extractor.py +114 -72
  4. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_dependency_parsing.py +55 -0
  5. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/.gitignore +0 -0
  6. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/README.md +0 -0
  7. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/__init__.py +0 -0
  8. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/__main__.py +0 -0
  9. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/__init__.py +0 -0
  10. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/analyzer.py +0 -0
  11. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/binding_tracker.py +0 -0
  12. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/call_graph.py +0 -0
  13. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/call_graph_types.py +0 -0
  14. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/call_resolver.py +0 -0
  15. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/capability_tagger.py +0 -0
  16. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/config_scanner.py +0 -0
  17. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/data_flow.py +0 -0
  18. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/flow_analysis.py +0 -0
  19. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/hof_catalog.py +0 -0
  20. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/integration_detector.py +0 -0
  21. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/literal_scanner.py +0 -0
  22. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/path_normalizer.py +0 -0
  23. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/read_site_detector.py +0 -0
  24. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/request_patterns.py +0 -0
  25. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/sensitivity_classifier.py +0 -0
  26. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/sink_evidence.py +0 -0
  27. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/surface_enrichment.py +0 -0
  28. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/analysis/url_prefix_resolver.py +0 -0
  29. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cli/__init__.py +0 -0
  30. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cli/exit_codes.py +0 -0
  31. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cli/main.py +0 -0
  32. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cloud/__init__.py +0 -0
  33. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cloud/apisec_client.py +0 -0
  34. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/cloud/client.py +0 -0
  35. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/__init__.py +0 -0
  36. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/config.py +0 -0
  37. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/credentials.py +0 -0
  38. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/discovery.py +0 -0
  39. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/log_format.py +0 -0
  40. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/manifest.py +0 -0
  41. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/repo.py +0 -0
  42. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/state.py +0 -0
  43. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/telemetry.py +0 -0
  44. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/core/types.py +0 -0
  45. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/fingerprinting/__init__.py +0 -0
  46. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/__init__.py +0 -0
  47. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/_graphql_common.py +0 -0
  48. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/_jwt_common.py +0 -0
  49. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/auth_helpers.py +0 -0
  50. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/base.py +0 -0
  51. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/__init__.py +0 -0
  52. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/_path_helpers.py +0 -0
  53. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/aspnet_plugin.py +0 -0
  54. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/grpc_plugin.py +0 -0
  55. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/jwt_config_extractor.py +0 -0
  56. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/legacy_aspnet_plugin.py +0 -0
  57. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/refit_plugin.py +0 -0
  58. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/wcf_plugin.py +0 -0
  59. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/dotnet/webforms_plugin.py +0 -0
  60. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/__init__.py +0 -0
  61. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/_annotations.py +0 -0
  62. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/_constraints.py +0 -0
  63. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/graphql_plugin.py +0 -0
  64. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/jaxrs_plugin.py +0 -0
  65. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/jwt_config_extractor.py +0 -0
  66. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/micronaut_plugin.py +0 -0
  67. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/java/spring_plugin.py +0 -0
  68. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/__init__.py +0 -0
  69. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/_route_helpers.py +0 -0
  70. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/express_plugin.py +0 -0
  71. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/fastify_plugin.py +0 -0
  72. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/graphql_plugin.py +0 -0
  73. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/js/nestjs_plugin.py +0 -0
  74. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/__init__.py +0 -0
  75. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/celery_plugin.py +0 -0
  76. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/click_plugin.py +0 -0
  77. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/django_plugin.py +0 -0
  78. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/fastapi/__init__.py +0 -0
  79. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/fastapi/plugin.py +0 -0
  80. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/flask_plugin.py +0 -0
  81. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/graphql_plugin.py +0 -0
  82. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/prefect_plugin.py +0 -0
  83. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/python/webhook_plugin.py +0 -0
  84. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/__init__.py +0 -0
  85. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/_ts_helpers.py +0 -0
  86. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/grape_plugin.py +0 -0
  87. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/graphql_plugin.py +0 -0
  88. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/rails_plugin.py +0 -0
  89. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/sinatra_plugin.py +0 -0
  90. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/frameworks/ruby/taint_scanner.py +0 -0
  91. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/__init__.py +0 -0
  92. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/base.py +0 -0
  93. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/csharp/__init__.py +0 -0
  94. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/csharp/language_services.py +0 -0
  95. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/csharp/literals.py +0 -0
  96. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/csharp/parser.py +0 -0
  97. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/csharp/type_resolver.py +0 -0
  98. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/js/__init__.py +0 -0
  99. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/js/language_services.py +0 -0
  100. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/js/parser.py +0 -0
  101. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/jvm/__init__.py +0 -0
  102. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/jvm/language_services.py +0 -0
  103. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/jvm/parser.py +0 -0
  104. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/jvm/type_resolver.py +0 -0
  105. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/__init__.py +0 -0
  106. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/cbv_extractor.py +0 -0
  107. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/constant_resolver.py +0 -0
  108. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/cross_file_resolver.py +0 -0
  109. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/dynamic_route_detector.py +0 -0
  110. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/expression_utils.py +0 -0
  111. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/extraction_types.py +0 -0
  112. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/language_services.py +0 -0
  113. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/parameter_analyzer.py +0 -0
  114. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/parser.py +0 -0
  115. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/path_resolver.py +0 -0
  116. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/router_registry.py +0 -0
  117. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/type_resolver.py +0 -0
  118. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/python/visitors.py +0 -0
  119. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/ruby/__init__.py +0 -0
  120. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/ruby/language_services.py +0 -0
  121. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/ruby/parser.py +0 -0
  122. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/parsing/services.py +0 -0
  123. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/query/__init__.py +0 -0
  124. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/query/ast_cache.py +0 -0
  125. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/query/executor.py +0 -0
  126. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/query/handlers.py +0 -0
  127. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/src/apisec_code_bolt/query/manifest_context.py +0 -0
  128. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/conftest.py +0 -0
  129. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/Cargo.toml +0 -0
  130. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/Gemfile.lock +0 -0
  131. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/Pipfile +0 -0
  132. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/build.gradle +0 -0
  133. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/build.gradle.kts +0 -0
  134. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/go.mod +0 -0
  135. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/package.json +0 -0
  136. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/pom.xml +0 -0
  137. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/pyproject_pep621.toml +0 -0
  138. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/pyproject_poetry.toml +0 -0
  139. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/requirements-dev.txt +0 -0
  140. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/requirements.txt +0 -0
  141. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/setup.cfg +0 -0
  142. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/dep_files/setup.py +0 -0
  143. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_django/urls.py +0 -0
  144. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_django/views.py +0 -0
  145. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_express/app.ts +0 -0
  146. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_express/middleware/authenticate.ts +0 -0
  147. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_express/routes/auth.ts +0 -0
  148. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_express/routes/users.ts +0 -0
  149. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_fastapi/main.py +0 -0
  150. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_fastapi/routes/__init__.py +0 -0
  151. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_fastapi/routes/users.py +0 -0
  152. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_fastapi/services/__init__.py +0 -0
  153. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_fastapi/services/user_service.py +0 -0
  154. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_flask/app.py +0 -0
  155. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_flask_pydantic/app.py +0 -0
  156. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_grape/api.rb +0 -0
  157. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_nestjs/auth.controller.ts +0 -0
  158. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_nestjs/roles.decorator.ts +0 -0
  159. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_nestjs/roles.guard.ts +0 -0
  160. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_nestjs/users.controller.ts +0 -0
  161. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_sinatra/app.rb +0 -0
  162. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_spring_boot/src/main/java/com/example/demo/config/SecurityConfig.java +0 -0
  163. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_spring_boot/src/main/java/com/example/demo/controller/UserController.java +0 -0
  164. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_spring_boot/src/main/java/com/example/demo/model/User.java +0 -0
  165. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_spring_boot/src/main/java/com/example/demo/service/UserService.java +0 -0
  166. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/sample_spring_boot/src/main/resources/application.properties +0 -0
  167. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/VULNERABILITIES.md +0 -0
  168. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/config/SecurityConfig.java +0 -0
  169. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/controller/AdminController.java +0 -0
  170. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/controller/OrderController.java +0 -0
  171. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/controller/UserController.java +0 -0
  172. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/model/Order.java +0 -0
  173. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/model/User.java +0 -0
  174. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/service/OrderService.java +0 -0
  175. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/java/com/example/vulnapi/service/UserService.java +0 -0
  176. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vuln_spring_boot/src/main/resources/application.properties +0 -0
  177. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vulnerable_app/pom.xml +0 -0
  178. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/fixtures/vulnerable_app/src/main/java/com/demo/DemoController.java +0 -0
  179. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/__init__.py +0 -0
  180. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_cli.py +0 -0
  181. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_django_analysis.py +0 -0
  182. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_express_analysis.py +0 -0
  183. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_fastapi_analysis.py +0 -0
  184. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_flask_analysis.py +0 -0
  185. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_nestjs_analysis.py +0 -0
  186. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_spring_boot_analysis.py +0 -0
  187. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/integration/test_vulnerable_app_reachability_guard.py +0 -0
  188. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/__init__.py +0 -0
  189. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/fixtures/sample_app.py +0 -0
  190. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/test_auth_indicator_matching.py +0 -0
  191. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/test_executor.py +0 -0
  192. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/test_handlers.py +0 -0
  193. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/test_query/test_read_site_origin_types.py +0 -0
  194. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/__init__.py +0 -0
  195. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_apisec_client.py +0 -0
  196. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_aspnet_plugin.py +0 -0
  197. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_benchmark_audit.py +0 -0
  198. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_benchmark_run.py +0 -0
  199. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_call_graph.py +0 -0
  200. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_cbv_extractor.py +0 -0
  201. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_cli_console_url.py +0 -0
  202. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_config.py +0 -0
  203. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_config_scanner_framework.py +0 -0
  204. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_constant_resolver.py +0 -0
  205. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_cross_file_resolver.py +0 -0
  206. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_csharp_literals.py +0 -0
  207. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_csharp_type_resolver.py +0 -0
  208. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_data_flow.py +0 -0
  209. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_discovery.py +0 -0
  210. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_django_plugin.py +0 -0
  211. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_dynamic_routes.py +0 -0
  212. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_express_nestjs_plugin.py +0 -0
  213. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_fastapi_plugin.py +0 -0
  214. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_fastify_plugin.py +0 -0
  215. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_flow_analysis.py +0 -0
  216. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_grape_plugin.py +0 -0
  217. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_graphql_plugin.py +0 -0
  218. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_grpc_plugin.py +0 -0
  219. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_instrumentation.py +0 -0
  220. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_integration_detection.py +0 -0
  221. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_java_parser.py +0 -0
  222. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_jaxrs_plugin.py +0 -0
  223. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_js_graphql_plugin.py +0 -0
  224. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_jwt_config_extraction.py +0 -0
  225. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_legacy_aspnet_plugin.py +0 -0
  226. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_manifest_upload_gzip.py +0 -0
  227. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_micronaut_plugin.py +0 -0
  228. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_parameter_analyzer.py +0 -0
  229. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_path_resolver.py +0 -0
  230. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_pydantic_body_inference.py +0 -0
  231. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_python_parser.py +0 -0
  232. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_rails_plugin.py +0 -0
  233. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_refit_plugin.py +0 -0
  234. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_repo_canonical.py +0 -0
  235. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_router_registry.py +0 -0
  236. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_ruby_graphql_plugin.py +0 -0
  237. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_sensitivity_capability.py +0 -0
  238. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_sinatra_plugin.py +0 -0
  239. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_sink_evidence.py +0 -0
  240. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_spring_plugin.py +0 -0
  241. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_static_route_catchup.py +0 -0
  242. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_surface_enrichment.py +0 -0
  243. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_surface_extraction_client.py +0 -0
  244. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_telemetry.py +0 -0
  245. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_transformation_tracking.py +0 -0
  246. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_types.py +0 -0
  247. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_wcf_plugin.py +0 -0
  248. {apisec_code_bolt-0.1.6 → apisec_code_bolt-0.1.7}/tests/unit/test_webforms_plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apisec-code-bolt
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: Static analysis probe for extracting architectural metadata from codebases
5
5
  Project-URL: Homepage, https://apisec.ai
6
6
  Project-URL: Documentation, https://docs.apisec.ai/code-bolt
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apisec-code-bolt"
7
- version = "0.1.6"
7
+ version = "0.1.7"
8
8
  description = "Static analysis probe for extracting architectural metadata from codebases"
9
9
  readme = "README.md"
10
10
  license = {text = "Proprietary"}
@@ -882,87 +882,129 @@ class DependencyExtractor:
882
882
  """Resolve versions pinned by parent/imported BOMs by walking the POM
883
883
  ancestry (parents + ``<scope>import</scope>``) on Maven Central.
884
884
 
885
- Accumulates ``<properties>`` (nearest wins) and ``<dependencyManagement>``
886
- pins across the chain, then dereferences ``${...}`` refs. Bounded depth +
887
- cycle guard; any fetch/parse miss simply contributes nothing. Returns
888
- ``{groupId:artifactId -> version}``.
885
+ Maven properties are **POM-scoped, not global**: two BOMs can define the
886
+ same property name with different values (e.g. spring-boot-dependencies
887
+ carries a Jackson-2 version while ``tools.jackson:jackson-bom`` carries a
888
+ Jackson-3 one). So each BOM's ``<dependencyManagement>`` pins are resolved
889
+ against THAT BOM's own scope (its ``<properties>`` + its parent chain,
890
+ child overriding parent) — never a shared soup — and imported BOMs are
891
+ resolved recursively in their own scope. First-seen pin wins (nearest to
892
+ the app). An imported-BOM coordinate whose version is itself a ``${...}``
893
+ ref is dereferenced before fetching. Bounded depth + cycle guards; any
894
+ fetch/parse miss contributes nothing. ``seed_props`` is intentionally
895
+ unused — seed coordinates arrive already version-resolved from the app pom.
896
+ Returns ``{groupId:artifactId -> version}``.
889
897
  """
890
898
  import xml.etree.ElementTree as ET
891
899
 
892
- props: dict[str, str] = dict(seed_props)
893
- raw_pins: dict[str, str] = {}
894
- seen: set[str] = set()
895
- worklist = list(seeds)
896
- depth = 0
897
- while worklist and depth < 8:
898
- depth += 1
899
- nxt: list[tuple[str, str, str]] = []
900
- for g, a, v in worklist:
901
- if not (g and a and v):
902
- continue
903
- key = f"{g}:{a}:{v}"
904
- if key in seen:
905
- continue
906
- seen.add(key)
907
- data = self._fetch_pom(g, a, v)
908
- if not data:
909
- continue
910
- try:
911
- el = ET.fromstring(data)
912
- except ET.ParseError:
913
- continue
914
- bns = el.tag[: el.tag.index("}") + 1] if el.tag.startswith("{") else ""
915
-
916
- def bq(t: str, _ns: str = bns) -> str:
917
- return f"{_ns}{t}"
918
-
919
- pe = el.find(bq("properties"))
920
- if pe is not None:
921
- for c in pe:
922
- k = c.tag[len(bns):] if bns else c.tag
923
- if c.text and k not in props: # nearest (child) wins
924
- props[k] = c.text.strip()
925
- dm = el.find(bq("dependencyManagement"))
926
- dmd = dm.find(bq("dependencies")) if dm is not None else None
927
- if dmd is not None:
928
- for d in dmd.findall(bq("dependency")):
929
- dg = (d.findtext(bq("groupId")) or "").strip()
930
- da = (d.findtext(bq("artifactId")) or "").strip()
931
- dv = (d.findtext(bq("version")) or "").strip()
932
- scope = (d.findtext(bq("scope")) or "").strip().lower()
933
- if scope == "import" and dg and da and dv:
934
- nxt.append((dg, da, dv))
935
- elif dg and da and dv and f"{dg}:{da}" not in raw_pins:
936
- raw_pins[f"{dg}:{da}"] = dv
937
- par = el.find(bq("parent"))
938
- if par is not None:
939
- pg = (par.findtext(bq("groupId")) or "").strip()
940
- pa = (par.findtext(bq("artifactId")) or "").strip()
941
- pv = (par.findtext(bq("version")) or "").strip()
942
- if pg and pa and pv:
943
- nxt.append((pg, pa, pv))
944
- worklist = nxt
945
-
946
- def deref(val: str | None) -> str | None:
947
- if val is None:
900
+ parsed: dict[str, tuple | None] = {}
901
+
902
+ def parse(g: str, a: str, v: str):
903
+ """(own_props, dm_pins, imports, parent) for a POM; cached; None if unfetchable."""
904
+ key = f"{g}:{a}:{v}"
905
+ if key in parsed:
906
+ return parsed[key]
907
+ parsed[key] = None # guard re-entry / unfetchable
908
+ data = self._fetch_pom(g, a, v)
909
+ if not data:
948
910
  return None
949
- seen2: set[str] = set()
911
+ try:
912
+ el = ET.fromstring(data)
913
+ except ET.ParseError:
914
+ return None
915
+ ns = el.tag[: el.tag.index("}") + 1] if el.tag.startswith("{") else ""
916
+
917
+ def q(t: str) -> str:
918
+ return f"{ns}{t}"
919
+
920
+ own: dict[str, str] = {}
921
+ pe = el.find(q("properties"))
922
+ if pe is not None:
923
+ for c in pe:
924
+ k = c.tag[len(ns):] if ns else c.tag
925
+ if c.text:
926
+ own[k] = c.text.strip()
927
+ own.setdefault("project.version", v)
928
+ own.setdefault("project.groupId", g)
929
+ dm_pins: list[tuple[str, str, str]] = []
930
+ imports: list[tuple[str, str, str]] = []
931
+ dm = el.find(q("dependencyManagement"))
932
+ dmd = dm.find(q("dependencies")) if dm is not None else None
933
+ if dmd is not None:
934
+ for d in dmd.findall(q("dependency")):
935
+ dg = (d.findtext(q("groupId")) or "").strip()
936
+ da = (d.findtext(q("artifactId")) or "").strip()
937
+ dv = (d.findtext(q("version")) or "").strip()
938
+ sc = (d.findtext(q("scope")) or "").strip().lower()
939
+ if dg and da and dv:
940
+ (imports if sc == "import" else dm_pins).append((dg, da, dv))
941
+ parent = None
942
+ par = el.find(q("parent"))
943
+ if par is not None:
944
+ pg = (par.findtext(q("groupId")) or "").strip()
945
+ pa = (par.findtext(q("artifactId")) or "").strip()
946
+ pv = (par.findtext(q("version")) or "").strip()
947
+ if pg and pa and pv:
948
+ parent = (pg, pa, pv)
949
+ result = (own, dm_pins, imports, parent)
950
+ parsed[key] = result
951
+ return result
952
+
953
+ def interp(val: str, scope: dict[str, str]) -> str | None:
954
+ if not val:
955
+ return None
956
+ seen_p: set[str] = set()
950
957
  m = re.fullmatch(r"\$\{([^}]+)\}", val.strip())
951
- while m and m.group(1) not in seen2:
952
- seen2.add(m.group(1))
953
- repl = props.get(m.group(1))
958
+ while m and m.group(1) not in seen_p:
959
+ seen_p.add(m.group(1))
960
+ repl = scope.get(m.group(1))
954
961
  if repl is None:
955
962
  return None
956
963
  val = repl.strip()
957
964
  m = re.fullmatch(r"\$\{([^}]+)\}", val)
958
- return None if (val is None or "${" in val) else val
959
-
960
- out: dict[str, str] = {}
961
- for name, raw in raw_pins.items():
962
- rv = deref(raw)
963
- if rv:
964
- out[name] = rv
965
- return out
965
+ return None if (not val or "${" in val) else val
966
+
967
+ def scope_of(g: str, a: str, v: str, seen: frozenset, depth: int) -> dict[str, str]:
968
+ """A POM's effective property scope: parent chain, child overrides parent."""
969
+ key = f"{g}:{a}:{v}"
970
+ if depth > 10 or key in seen:
971
+ return {}
972
+ p = parse(g, a, v)
973
+ if p is None:
974
+ return {}
975
+ own, _dm, _imp, parent = p
976
+ merged = dict(scope_of(*parent, seen | {key}, depth + 1)) if parent else {}
977
+ merged.update(own) # own overrides inherited
978
+ return merged
979
+
980
+ pins: dict[str, str] = {}
981
+ resolved: set[str] = set()
982
+
983
+ def resolve_bom(g: str, a: str, v: str, depth: int) -> None:
984
+ key = f"{g}:{a}:{v}"
985
+ if depth > 10 or key in resolved:
986
+ return
987
+ resolved.add(key)
988
+ p = parse(g, a, v)
989
+ if p is None:
990
+ return
991
+ _own, dm_pins, imports, parent = p
992
+ scope = scope_of(g, a, v, frozenset(), 0) # resolve in THIS bom's own scope
993
+ for dg, da, dv in dm_pins:
994
+ rv = interp(dv, scope)
995
+ if rv:
996
+ pins.setdefault(f"{dg}:{da}", rv) # nearest-to-app wins
997
+ for dg, da, dv in imports:
998
+ iv = interp(dv, scope)
999
+ if iv:
1000
+ resolve_bom(interp(dg, scope) or dg, interp(da, scope) or da, iv, depth + 1)
1001
+ if parent: # inherited dependencyManagement from the parent chain
1002
+ resolve_bom(parent[0], parent[1], parent[2], depth + 1)
1003
+
1004
+ for g, a, v in seeds:
1005
+ if g and a and v:
1006
+ resolve_bom(g, a, v, 0)
1007
+ return pins
966
1008
 
967
1009
  # -- pom.xml (Maven / JVM) -------------------------------------------------
968
1010
 
@@ -523,6 +523,61 @@ class TestPomXml:
523
523
  by_name = {p.name: p for p in analyzer._parse_pom(pom)}
524
524
  assert by_name["com.h2database:h2"].version is None
525
525
 
526
+ def test_imported_bom_pins_resolve_in_own_scope(
527
+ self, analyzer: DependencyExtractor, tmp_path: Path, monkeypatch
528
+ ) -> None:
529
+ # Property scoping: an imported BOM's pins must resolve against THAT BOM's
530
+ # own properties, not a shared soup. Real case: spring-boot-dependencies
531
+ # carries a Jackson-2 `jackson.version` while tools.jackson:jackson-bom
532
+ # carries a Jackson-3 one; the Jackson-3 artifact must get 3.x, not the
533
+ # leaked 2.x. Regression guard for the null→wrong-version failure mode.
534
+ parent_pom = b"""<?xml version="1.0"?>
535
+ <project xmlns="http://maven.apache.org/POM/4.0.0">
536
+ <artifactId>starter-parent</artifactId>
537
+ <parent><groupId>org.example</groupId><artifactId>deps-bom</artifactId><version>1.0</version></parent>
538
+ </project>"""
539
+ # deps-bom sets jackson.version=2.9 (Jackson-2 line) AND imports the v3 bom.
540
+ deps_bom = b"""<?xml version="1.0"?>
541
+ <project xmlns="http://maven.apache.org/POM/4.0.0">
542
+ <artifactId>deps-bom</artifactId>
543
+ <properties><jackson.version>2.9.0</jackson.version><j3.version>3.1.2</j3.version></properties>
544
+ <dependencyManagement><dependencies>
545
+ <dependency><groupId>tools.jackson</groupId><artifactId>jackson-bom</artifactId>
546
+ <version>${j3.version}</version><type>pom</type><scope>import</scope></dependency>
547
+ </dependencies></dependencyManagement>
548
+ </project>"""
549
+ # jackson-bom (v3) sets its OWN jackson.version=3.1.2 and pins core with it.
550
+ j3_bom = b"""<?xml version="1.0"?>
551
+ <project xmlns="http://maven.apache.org/POM/4.0.0">
552
+ <artifactId>jackson-bom</artifactId>
553
+ <properties><jackson.version>3.1.2</jackson.version></properties>
554
+ <dependencyManagement><dependencies>
555
+ <dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-databind</artifactId>
556
+ <version>${jackson.version}</version></dependency>
557
+ </dependencies></dependencyManagement>
558
+ </project>"""
559
+
560
+ def fake_fetch(self, group, artifact, version): # noqa: ANN001
561
+ return {"starter-parent": parent_pom, "deps-bom": deps_bom,
562
+ "jackson-bom": j3_bom}.get(artifact)
563
+
564
+ monkeypatch.setattr(DependencyExtractor, "_fetch_pom", fake_fetch)
565
+ monkeypatch.setenv("APISEC_RESOLVE_MAVEN_BOMS", "1")
566
+ pom = tmp_path / "pom.xml"
567
+ pom.write_text(
568
+ """<?xml version="1.0"?>
569
+ <project xmlns="http://maven.apache.org/POM/4.0.0">
570
+ <parent><groupId>org.example</groupId><artifactId>starter-parent</artifactId><version>1.0</version></parent>
571
+ <dependencies>
572
+ <dependency><groupId>tools.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency>
573
+ </dependencies>
574
+ </project>
575
+ """
576
+ )
577
+ by_name = {p.name: p for p in analyzer._parse_pom(pom)}
578
+ # v3 bom's own scope wins → 3.1.2, NOT the 2.9.0 that deps-bom set.
579
+ assert by_name["tools.jackson.core:jackson-databind"].version == "3.1.2"
580
+
526
581
  def test_version_range_not_pinned(self, analyzer: DependencyExtractor) -> None:
527
582
  # Maven range [1.0,2.0) is not an exact pin. (F-1)
528
583
  by_name = {p.name: p for p in analyzer._parse_pom(FIXTURES / "pom.xml")}