voidaccess 1.7.0__tar.gz → 1.7.2__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 (206) hide show
  1. {voidaccess-1.7.0 → voidaccess-1.7.2}/PKG-INFO +12 -6
  2. {voidaccess-1.7.0 → voidaccess-1.7.2}/README.md +11 -5
  3. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/actors.py +42 -23
  4. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/entities.py +8 -12
  5. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/investigations.py +3126 -3453
  6. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/search.py +81 -61
  7. {voidaccess-1.7.0 → voidaccess-1.7.2}/config.py +13 -7
  8. {voidaccess-1.7.0 → voidaccess-1.7.2}/crawler/spider.py +30 -4
  9. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/queries.py +18 -9
  10. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/ioc_package.py +1 -1
  11. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/misp.py +2 -3
  12. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/sigma.py +2 -3
  13. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/stix.py +64 -20
  14. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/ner.py +136 -1
  15. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/normalizer.py +186 -90
  16. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/pipeline.py +74 -13
  17. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/__init__.py +2 -0
  18. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/builder.py +148 -16
  19. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/config.py +4 -0
  20. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/jobs.py +68 -11
  21. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/scheduler.py +4 -6
  22. {voidaccess-1.7.0 → voidaccess-1.7.2}/pyproject.toml +1 -1
  23. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/actor_profiles.py +2 -2
  24. voidaccess-1.7.2/tests/test_confidence_calibration.py +108 -0
  25. voidaccess-1.7.2/tests/test_graph_integration.py +141 -0
  26. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_monitor.py +11 -8
  27. voidaccess-1.7.2/tests/test_monitor_integration.py +150 -0
  28. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_regex_patterns.py +39 -44
  29. voidaccess-1.7.2/tests/test_stix_export.py +281 -0
  30. {voidaccess-1.7.0 → voidaccess-1.7.2}/vector/__init__.py +2 -0
  31. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess/llm.py +97 -5
  32. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/PKG-INFO +12 -6
  33. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/SOURCES.txt +3 -0
  34. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/__init__.py +1 -1
  35. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/adapters/sqlite.py +97 -6
  36. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/investigate.py +20 -17
  37. voidaccess-1.7.0/tests/test_stix_export.py +0 -19
  38. {voidaccess-1.7.0 → voidaccess-1.7.2}/LICENSE +0 -0
  39. {voidaccess-1.7.0 → voidaccess-1.7.2}/analysis/__init__.py +0 -0
  40. {voidaccess-1.7.0 → voidaccess-1.7.2}/analysis/opsec.py +0 -0
  41. {voidaccess-1.7.0 → voidaccess-1.7.2}/analysis/patterns.py +0 -0
  42. {voidaccess-1.7.0 → voidaccess-1.7.2}/analysis/temporal.py +0 -0
  43. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/__init__.py +0 -0
  44. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/auth.py +0 -0
  45. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/main.py +0 -0
  46. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/__init__.py +0 -0
  47. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/admin.py +0 -0
  48. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/auth.py +0 -0
  49. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/export.py +0 -0
  50. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/monitors.py +0 -0
  51. {voidaccess-1.7.0 → voidaccess-1.7.2}/api/routes/settings.py +0 -0
  52. {voidaccess-1.7.0 → voidaccess-1.7.2}/auth/__init__.py +0 -0
  53. {voidaccess-1.7.0 → voidaccess-1.7.2}/auth/token_blacklist.py +0 -0
  54. {voidaccess-1.7.0 → voidaccess-1.7.2}/crawler/__init__.py +0 -0
  55. {voidaccess-1.7.0 → voidaccess-1.7.2}/crawler/dedup.py +0 -0
  56. {voidaccess-1.7.0 → voidaccess-1.7.2}/crawler/frontier.py +0 -0
  57. {voidaccess-1.7.0 → voidaccess-1.7.2}/crawler/utils.py +0 -0
  58. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/__init__.py +0 -0
  59. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/__init__.py +0 -0
  60. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/env.py +0 -0
  61. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0001_initial_schema.py +0 -0
  62. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0002_add_investigation_status_column.py +0 -0
  63. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0002_add_missing_tables.py +0 -0
  64. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0003_add_canonical_value_and_entity_links.py +0 -0
  65. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0004_add_page_posted_at.py +0 -0
  66. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0005_add_extraction_method.py +0 -0
  67. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0006_add_monitor_alerts.py +0 -0
  68. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0007_add_actor_style_profiles.py +0 -0
  69. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0008_add_users_table.py +0 -0
  70. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0009_add_investigation_id_to_relationships.py +0 -0
  71. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0010_add_composite_index_entity_relationships.py +0 -0
  72. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0011_add_page_extraction_cache.py +0 -0
  73. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0013_add_graph_status.py +0 -0
  74. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0015_add_progress_fields.py +0 -0
  75. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0016_backfill_graph_status.py +0 -0
  76. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0017_add_user_api_keys.py +0 -0
  77. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0018_add_user_id_to_investigations.py +0 -0
  78. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0019_add_content_safety_log.py +0 -0
  79. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0020_add_entity_source_tracking.py +0 -0
  80. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0021_add_search_engine_stats.py +0 -0
  81. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0022_add_actor_profiles.py +0 -0
  82. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/migrations/versions/0023_add_investigation_metadata.py +0 -0
  83. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/models.py +0 -0
  84. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/search_engine_stats.py +0 -0
  85. {voidaccess-1.7.0 → voidaccess-1.7.2}/db/session.py +0 -0
  86. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/__init__.py +0 -0
  87. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/snort_export.py +0 -0
  88. {voidaccess-1.7.0 → voidaccess-1.7.2}/export/yara_export.py +0 -0
  89. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/__init__.py +0 -0
  90. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/llm_extract.py +0 -0
  91. {voidaccess-1.7.0 → voidaccess-1.7.2}/extractor/regex_patterns.py +0 -0
  92. {voidaccess-1.7.0 → voidaccess-1.7.2}/fingerprint/__init__.py +0 -0
  93. {voidaccess-1.7.0 → voidaccess-1.7.2}/fingerprint/profiler.py +0 -0
  94. {voidaccess-1.7.0 → voidaccess-1.7.2}/fingerprint/stylometry.py +0 -0
  95. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/export.py +0 -0
  96. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/model.py +0 -0
  97. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/queries.py +0 -0
  98. {voidaccess-1.7.0 → voidaccess-1.7.2}/graph/visualize.py +0 -0
  99. {voidaccess-1.7.0 → voidaccess-1.7.2}/i18n/__init__.py +0 -0
  100. {voidaccess-1.7.0 → voidaccess-1.7.2}/i18n/detect.py +0 -0
  101. {voidaccess-1.7.0 → voidaccess-1.7.2}/i18n/query_expand.py +0 -0
  102. {voidaccess-1.7.0 → voidaccess-1.7.2}/i18n/translate.py +0 -0
  103. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/__init__.py +0 -0
  104. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/_db.py +0 -0
  105. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/alerts.py +0 -0
  106. {voidaccess-1.7.0 → voidaccess-1.7.2}/monitor/diff.py +0 -0
  107. {voidaccess-1.7.0 → voidaccess-1.7.2}/scraper/__init__.py +0 -0
  108. {voidaccess-1.7.0 → voidaccess-1.7.2}/scraper/scrape.py +0 -0
  109. {voidaccess-1.7.0 → voidaccess-1.7.2}/scraper/scrape_js.py +0 -0
  110. {voidaccess-1.7.0 → voidaccess-1.7.2}/search/__init__.py +0 -0
  111. {voidaccess-1.7.0 → voidaccess-1.7.2}/search/circuit_breaker.py +0 -0
  112. {voidaccess-1.7.0 → voidaccess-1.7.2}/search/query_builder.py +0 -0
  113. {voidaccess-1.7.0 → voidaccess-1.7.2}/search/search.py +0 -0
  114. {voidaccess-1.7.0 → voidaccess-1.7.2}/setup.cfg +0 -0
  115. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/__init__.py +0 -0
  116. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/blockchain.py +0 -0
  117. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/cache.py +0 -0
  118. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/cisa.py +0 -0
  119. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/data/__init__.py +0 -0
  120. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/data/onion_seeds.json +0 -0
  121. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/dns_enrichment.py +0 -0
  122. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/domain_reputation.py +0 -0
  123. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/email_reputation.py +0 -0
  124. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/engines.py +0 -0
  125. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/enrichment.py +0 -0
  126. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/github_scraper.py +0 -0
  127. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/gitlab_scraper.py +0 -0
  128. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/hash_reputation.py +0 -0
  129. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/historical_intel.py +0 -0
  130. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/ip_reputation.py +0 -0
  131. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/paste_scraper.py +0 -0
  132. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/pastes.py +0 -0
  133. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/proxy_client.py +0 -0
  134. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/rss_scraper.py +0 -0
  135. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/seed_manager.py +0 -0
  136. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/seeds.py +0 -0
  137. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/shodan.py +0 -0
  138. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/telegram.py +0 -0
  139. {voidaccess-1.7.0 → voidaccess-1.7.2}/sources/virustotal.py +0 -0
  140. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_analysis_opsec.py +0 -0
  141. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_analysis_stylometry.py +0 -0
  142. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_analysis_temporal.py +0 -0
  143. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_api.py +0 -0
  144. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_api_monitors.py +0 -0
  145. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_blockchain.py +0 -0
  146. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_cli_proxy_config.py +0 -0
  147. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_config.py +0 -0
  148. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_crawler.py +0 -0
  149. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_db.py +0 -0
  150. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_dns_enrichment.py +0 -0
  151. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_domain_reputation.py +0 -0
  152. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_email_reputation.py +0 -0
  153. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_filter_e2e_manual.py +0 -0
  154. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_filter_parser_manual.py +0 -0
  155. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_fingerprint.py +0 -0
  156. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_github_scraper.py +0 -0
  157. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_gitlab_scraper.py +0 -0
  158. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_graph.py +0 -0
  159. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_hash_reputation.py +0 -0
  160. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_i18n.py +0 -0
  161. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_ip_reputation.py +0 -0
  162. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_llm.py +0 -0
  163. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_llm_utils.py +0 -0
  164. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_model_singleton.py +0 -0
  165. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_pagination.py +0 -0
  166. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_paste_scraper.py +0 -0
  167. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_proxy_client.py +0 -0
  168. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_rss_scraper.py +0 -0
  169. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_scrape_js.py +0 -0
  170. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_settings.py +0 -0
  171. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_snort_export.py +0 -0
  172. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_sources.py +0 -0
  173. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_sources_enrichment_new.py +0 -0
  174. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_vector.py +0 -0
  175. {voidaccess-1.7.0 → voidaccess-1.7.2}/tests/test_yara_export.py +0 -0
  176. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/__init__.py +0 -0
  177. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/async_utils.py +0 -0
  178. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/content_safety.py +0 -0
  179. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/defang.py +0 -0
  180. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/encryption.py +0 -0
  181. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/enrichment_cache.py +0 -0
  182. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/ioc_freshness.py +0 -0
  183. {voidaccess-1.7.0 → voidaccess-1.7.2}/utils/user_keys.py +0 -0
  184. {voidaccess-1.7.0 → voidaccess-1.7.2}/vector/embedder.py +0 -0
  185. {voidaccess-1.7.0 → voidaccess-1.7.2}/vector/model_singleton.py +0 -0
  186. {voidaccess-1.7.0 → voidaccess-1.7.2}/vector/search.py +0 -0
  187. {voidaccess-1.7.0 → voidaccess-1.7.2}/vector/store.py +0 -0
  188. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess/__init__.py +0 -0
  189. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess/config.py +0 -0
  190. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess/llm_utils.py +0 -0
  191. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/dependency_links.txt +0 -0
  192. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/entry_points.txt +0 -0
  193. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/requires.txt +0 -0
  194. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess.egg-info/top_level.txt +0 -0
  195. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/adapters/__init__.py +0 -0
  196. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/browser.py +0 -0
  197. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/__init__.py +0 -0
  198. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/actors.py +0 -0
  199. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/configure.py +0 -0
  200. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/enrich.py +0 -0
  201. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/export.py +0 -0
  202. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/commands/show.py +0 -0
  203. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/config.py +0 -0
  204. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/display.py +0 -0
  205. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/main.py +0 -0
  206. {voidaccess-1.7.0 → voidaccess-1.7.2}/voidaccess_cli/tor_detect.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voidaccess
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Dark web OSINT CLI — automated threat intelligence from query to report
5
5
  Author: VoidAccess
6
6
  License-Expression: MIT
@@ -104,7 +104,16 @@ Commercial threat intelligence platforms often charge prohibitive annual fees fo
104
104
 
105
105
  ---
106
106
 
107
- ## What's New in v1.7.0
107
+ ## What's New in v1.7.2
108
+ - STIX export relationship generation is bounded and semantic, avoiding page-level cartesian edge explosions at larger entity counts.
109
+ - Repeated NER false positives for threat actor handles and organization names are filtered before persistence.
110
+ - MISP/Sigma/API query paths no longer use the SQLAlchemy subquery-in-`IN()` pattern that produced warnings.
111
+ - Optional configuration warnings are summarized once per process.
112
+ - Refined LLM query output is persisted as a short search string, not explanatory Markdown.
113
+
114
+ ---
115
+
116
+ ## What's New in the Previous Release
108
117
 
109
118
  - **Dependency optional-extras split** — `sentence-transformers`, `torch`, `transformers`, `telethon`, and `playwright` are now optional extras: `voidaccess[nlp]`, `voidaccess[telegram]`, `voidaccess[js]`, and `voidaccess[all]`.
110
119
  - **Optional clearnet ScrapingAnt integration** — paste sites and RSS feeds can now be routed through ScrapingAnt. Affects clearnet scraping only; Tor, `.onion`, GitHub, and GitLab traffic are never affected.
@@ -310,7 +319,7 @@ Export formats:
310
319
 
311
320
  ## Actor Intelligence
312
321
 
313
- VoidAccess v1.7.0 persists actor profiles across investigations in `actor_profiles`, with linked aliases and infrastructure in `actor_aliases` and `actor_infrastructure`. Profiles are populated from threat actor, ransomware group, and handle entities, then enriched with co-occurring infrastructure and timeline events.
322
+ VoidAccess v1.7.1 persists actor profiles across investigations in `actor_profiles`, with linked aliases and infrastructure in `actor_aliases` and `actor_infrastructure`. Profiles are populated from threat actor, ransomware group, and handle entities, then enriched with co-occurring infrastructure and timeline events.
314
323
 
315
324
  Cross-alias resolution scores five signals: shared infrastructure, shared PGP, string similarity, temporal co-activity, and co-investigation. Use `voidaccess actors` to list profiles, `voidaccess actor <handle>` for the full profile, `voidaccess actor <handle> --timeline` for chronology, and `voidaccess actor <handle> --note "text"` for analyst notes.
316
325
 
@@ -577,6 +586,3 @@ To report a security vulnerability, see [docs/SECURITY.md](docs/SECURITY.md).
577
586
 
578
587
  MIT License. See [LICENSE](LICENSE) for details.
579
588
 
580
-
581
-
582
-
@@ -19,7 +19,16 @@ Commercial threat intelligence platforms often charge prohibitive annual fees fo
19
19
 
20
20
  ---
21
21
 
22
- ## What's New in v1.7.0
22
+ ## What's New in v1.7.2
23
+ - STIX export relationship generation is bounded and semantic, avoiding page-level cartesian edge explosions at larger entity counts.
24
+ - Repeated NER false positives for threat actor handles and organization names are filtered before persistence.
25
+ - MISP/Sigma/API query paths no longer use the SQLAlchemy subquery-in-`IN()` pattern that produced warnings.
26
+ - Optional configuration warnings are summarized once per process.
27
+ - Refined LLM query output is persisted as a short search string, not explanatory Markdown.
28
+
29
+ ---
30
+
31
+ ## What's New in the Previous Release
23
32
 
24
33
  - **Dependency optional-extras split** — `sentence-transformers`, `torch`, `transformers`, `telethon`, and `playwright` are now optional extras: `voidaccess[nlp]`, `voidaccess[telegram]`, `voidaccess[js]`, and `voidaccess[all]`.
25
34
  - **Optional clearnet ScrapingAnt integration** — paste sites and RSS feeds can now be routed through ScrapingAnt. Affects clearnet scraping only; Tor, `.onion`, GitHub, and GitLab traffic are never affected.
@@ -225,7 +234,7 @@ Export formats:
225
234
 
226
235
  ## Actor Intelligence
227
236
 
228
- VoidAccess v1.7.0 persists actor profiles across investigations in `actor_profiles`, with linked aliases and infrastructure in `actor_aliases` and `actor_infrastructure`. Profiles are populated from threat actor, ransomware group, and handle entities, then enriched with co-occurring infrastructure and timeline events.
237
+ VoidAccess v1.7.1 persists actor profiles across investigations in `actor_profiles`, with linked aliases and infrastructure in `actor_aliases` and `actor_infrastructure`. Profiles are populated from threat actor, ransomware group, and handle entities, then enriched with co-occurring infrastructure and timeline events.
229
238
 
230
239
  Cross-alias resolution scores five signals: shared infrastructure, shared PGP, string similarity, temporal co-activity, and co-investigation. Use `voidaccess actors` to list profiles, `voidaccess actor <handle>` for the full profile, `voidaccess actor <handle> --timeline` for chronology, and `voidaccess actor <handle> --note "text"` for analyst notes.
231
240
 
@@ -492,6 +501,3 @@ To report a security vulnerability, see [docs/SECURITY.md](docs/SECURITY.md).
492
501
 
493
502
  MIT License. See [LICENSE](LICENSE) for details.
494
503
 
495
-
496
-
497
-
@@ -17,11 +17,12 @@ from __future__ import annotations
17
17
 
18
18
  import logging
19
19
  import os
20
+ import uuid
20
21
  from typing import Optional
21
22
 
22
23
  from fastapi import APIRouter, Depends, HTTPException, Query
23
24
  from pydantic import BaseModel, Field
24
- from sqlalchemy import or_
25
+ from sqlalchemy import or_, select
25
26
 
26
27
  from api.auth import CurrentUser, get_current_user
27
28
 
@@ -87,26 +88,22 @@ def _db_required() -> None:
87
88
  raise HTTPException(status_code=503, detail="Database not configured")
88
89
 
89
90
 
90
- def _user_owned_investigation_ids_subq(session, user_id: int):
91
+ def _user_owned_investigation_ids_select(user_id: int):
91
92
  from db.models import Investigation
92
93
 
93
- return (
94
- session.query(Investigation.id)
95
- .filter(Investigation.user_id == user_id)
96
- .subquery()
97
- )
94
+ return select(Investigation.id).where(Investigation.user_id == user_id)
98
95
 
99
96
 
100
97
  def _profile_accessible(session, profile_id: str, user_id: int) -> bool:
101
98
  """True when at least one investigation linked to the profile is owned by the user."""
102
- from db.models import ActorAlias, ActorInfrastructure, Investigation
99
+ from db.models import ActorAlias, ActorInfrastructure
103
100
 
104
- inv_ids_subq = _user_owned_investigation_ids_subq(session, user_id)
101
+ inv_ids_select = _user_owned_investigation_ids_select(user_id)
105
102
  alias_match = (
106
103
  session.query(ActorAlias.id)
107
104
  .filter(
108
105
  ActorAlias.actor_id == profile_id,
109
- ActorAlias.source_investigation_id.in_(inv_ids_subq),
106
+ ActorAlias.source_investigation_id.in_(inv_ids_select),
110
107
  )
111
108
  .first()
112
109
  )
@@ -116,7 +113,7 @@ def _profile_accessible(session, profile_id: str, user_id: int) -> bool:
116
113
  session.query(ActorInfrastructure.id)
117
114
  .filter(
118
115
  ActorInfrastructure.actor_id == profile_id,
119
- ActorInfrastructure.source_investigation_id.in_(inv_ids_subq),
116
+ ActorInfrastructure.source_investigation_id.in_(inv_ids_select),
120
117
  )
121
118
  .first()
122
119
  )
@@ -145,20 +142,15 @@ async def list_actors(
145
142
  users since the seed is public data.
146
143
  """
147
144
  _db_required()
148
- from sqlalchemy import select
149
-
150
145
  from db.models import (
151
146
  ActorProfile,
152
147
  ActorAlias,
153
148
  ActorInfrastructure,
154
- Investigation,
155
149
  )
156
150
 
157
151
  try:
158
152
  with _session() as session:
159
- user_inv_ids = _user_owned_investigation_ids_subq(
160
- session, current_user.user.id
161
- )
153
+ user_inv_ids = _user_owned_investigation_ids_select(current_user.user.id)
162
154
 
163
155
  # Subquery of profile IDs visible to this user
164
156
  visible_alias = select(ActorAlias.actor_id).where(
@@ -176,13 +168,13 @@ async def list_actors(
176
168
  )
177
169
  if search:
178
170
  pattern = f"%{search.lower()}%"
179
- alias_subq = select(ActorAlias.actor_id).where(
171
+ alias_select = select(ActorAlias.actor_id).where(
180
172
  ActorAlias.alias_value.ilike(pattern)
181
173
  )
182
174
  q = q.filter(
183
175
  or_(
184
176
  ActorProfile.canonical_handle.ilike(pattern),
185
- ActorProfile.id.in_(alias_subq),
177
+ ActorProfile.id.in_(alias_select),
186
178
  )
187
179
  )
188
180
 
@@ -258,6 +250,35 @@ async def get_actor(
258
250
  return profile
259
251
 
260
252
 
253
+ @router.get("/{node_id}/profile")
254
+ async def get_actor_profile_by_node(
255
+ node_id: str,
256
+ investigation_id: str = Query(..., description="Investigation ID"),
257
+ current_user: CurrentUser = Depends(get_current_user),
258
+ ) -> dict:
259
+ """Return the graph actor profile for a node inside one investigation."""
260
+ _db_required()
261
+ try:
262
+ inv_uuid = uuid.UUID(investigation_id)
263
+ except ValueError:
264
+ raise HTTPException(status_code=422, detail="Invalid investigation ID format")
265
+ from db.queries import get_investigation_by_id_or_run
266
+ from graph import build_graph_from_db_cached, get_actor_profile
267
+
268
+ with _session() as session:
269
+ inv = get_investigation_by_id_or_run(session, inv_uuid)
270
+ if inv is None:
271
+ raise HTTPException(status_code=404, detail="Investigation not found")
272
+ if str(inv.user_id) != str(current_user.user.id):
273
+ raise HTTPException(status_code=403, detail="Forbidden")
274
+ graph = build_graph_from_db_cached(investigation_id=inv.id)
275
+
276
+ profile = get_actor_profile(graph, node_id)
277
+ if not profile:
278
+ raise HTTPException(status_code=404, detail="Graph node not found")
279
+ return profile
280
+
281
+
261
282
  @router.get("/{handle}/investigations")
262
283
  async def get_actor_investigations(
263
284
  handle: str,
@@ -284,9 +305,7 @@ async def get_actor_investigations(
284
305
 
285
306
  try:
286
307
  with _session() as session:
287
- user_inv_ids = _user_owned_investigation_ids_subq(
288
- session, current_user.user.id
289
- )
308
+ user_inv_ids = _user_owned_investigation_ids_select(current_user.user.id)
290
309
  all_ids = [
291
310
  uuid_value(s)
292
311
  for s in profile.get("investigation_ids", [])
@@ -732,4 +751,4 @@ def uuid_value(s):
732
751
  import uuid as _uuid
733
752
  return _uuid.UUID(str(s))
734
753
  except (ValueError, TypeError):
735
- return None
754
+ return None
@@ -57,14 +57,12 @@ async def list_entities(
57
57
 
58
58
  with get_session() as session:
59
59
  user_inv_ids = (
60
- session.query(Investigation.id)
61
- .filter(Investigation.user_id == current_user.user.id)
62
- .subquery()
60
+ sa.select(Investigation.id)
61
+ .where(Investigation.user_id == current_user.user.id)
63
62
  )
64
63
  linked_entity_ids = (
65
- session.query(InvestigationEntityLink.entity_id)
66
- .filter(InvestigationEntityLink.investigation_id.in_(user_inv_ids))
67
- .subquery()
64
+ sa.select(InvestigationEntityLink.entity_id)
65
+ .where(InvestigationEntityLink.investigation_id.in_(user_inv_ids))
68
66
  )
69
67
  q = session.query(Entity).filter(
70
68
  sa.or_(
@@ -749,14 +747,12 @@ def _assert_entity_accessible(session, entity_id: uuid.UUID, user_id: int) -> No
749
747
  from db.models import Entity, Investigation, InvestigationEntityLink # noqa: PLC0415
750
748
 
751
749
  user_inv_ids = (
752
- session.query(Investigation.id)
753
- .filter(Investigation.user_id == user_id)
754
- .subquery()
750
+ sa.select(Investigation.id)
751
+ .where(Investigation.user_id == user_id)
755
752
  )
756
753
  linked_entity_ids = (
757
- session.query(InvestigationEntityLink.entity_id)
758
- .filter(InvestigationEntityLink.investigation_id.in_(user_inv_ids))
759
- .subquery()
754
+ sa.select(InvestigationEntityLink.entity_id)
755
+ .where(InvestigationEntityLink.investigation_id.in_(user_inv_ids))
760
756
  )
761
757
  accessible = (
762
758
  session.query(Entity.id)