voidaccess 1.7.2__tar.gz → 1.8.1__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 (231) hide show
  1. voidaccess-1.8.1/PKG-INFO +228 -0
  2. voidaccess-1.8.1/README.md +143 -0
  3. {voidaccess-1.7.2 → voidaccess-1.8.1}/analysis/opsec.py +9 -3
  4. {voidaccess-1.7.2 → voidaccess-1.8.1}/analysis/patterns.py +0 -1
  5. {voidaccess-1.7.2 → voidaccess-1.8.1}/analysis/temporal.py +57 -22
  6. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/auth.py +26 -2
  7. voidaccess-1.8.1/api/errors.py +99 -0
  8. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/main.py +42 -17
  9. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/admin.py +16 -8
  10. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/auth.py +1 -1
  11. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/entities.py +30 -7
  12. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/investigations.py +3279 -3111
  13. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/monitors.py +3 -4
  14. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/search.py +25 -7
  15. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/settings.py +7 -12
  16. voidaccess-1.8.1/auth/token_blacklist.py +140 -0
  17. {voidaccess-1.7.2 → voidaccess-1.8.1}/config.py +31 -5
  18. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0002_add_missing_tables.py +0 -2
  19. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0003_add_canonical_value_and_entity_links.py +6 -1
  20. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0006_add_monitor_alerts.py +0 -2
  21. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0007_add_actor_style_profiles.py +0 -3
  22. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0008_add_users_table.py +0 -1
  23. voidaccess-1.8.1/db/migrations/versions/0009_add_investigation_id_to_relationships.py +37 -0
  24. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0010_add_composite_index_entity_relationships.py +0 -1
  25. voidaccess-1.8.1/db/migrations/versions/0018_add_user_id_to_investigations.py +38 -0
  26. voidaccess-1.8.1/db/migrations/versions/0024_add_investigation_step_metrics.py +37 -0
  27. voidaccess-1.8.1/db/migrations/versions/0025_add_cancellation_requested.py +21 -0
  28. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/models.py +44 -1
  29. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/queries.py +92 -0
  30. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/session.py +0 -1
  31. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/ioc_package.py +1 -1
  32. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/snort_export.py +2 -2
  33. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/stix.py +63 -12
  34. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/yara_export.py +1 -1
  35. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/__init__.py +6 -0
  36. voidaccess-1.8.1/extractor/confidence.py +156 -0
  37. voidaccess-1.8.1/extractor/entity_shape.py +375 -0
  38. voidaccess-1.8.1/extractor/gazetteer.py +152 -0
  39. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/llm_extract.py +75 -16
  40. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/ner.py +19 -145
  41. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/normalizer.py +356 -68
  42. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/pipeline.py +128 -17
  43. {voidaccess-1.7.2 → voidaccess-1.8.1}/extractor/regex_patterns.py +14 -4
  44. voidaccess-1.8.1/extractor/relationship_extract.py +375 -0
  45. voidaccess-1.8.1/fingerprint/calibration.py +55 -0
  46. {voidaccess-1.7.2 → voidaccess-1.8.1}/fingerprint/profiler.py +20 -4
  47. {voidaccess-1.7.2 → voidaccess-1.8.1}/fingerprint/stylometry.py +107 -12
  48. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/model.py +10 -1
  49. {voidaccess-1.7.2 → voidaccess-1.8.1}/pyproject.toml +66 -44
  50. {voidaccess-1.7.2 → voidaccess-1.8.1}/scraper/scrape.py +31 -31
  51. {voidaccess-1.7.2 → voidaccess-1.8.1}/scraper/scrape_js.py +0 -2
  52. {voidaccess-1.7.2 → voidaccess-1.8.1}/search/__init__.py +1 -22
  53. voidaccess-1.8.1/search/engine_catalog.py +30 -0
  54. {voidaccess-1.7.2 → voidaccess-1.8.1}/search/search.py +1 -19
  55. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/__init__.py +29 -11
  56. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/actor_profiles.py +0 -1
  57. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/blockchain.py +1 -1
  58. voidaccess-1.8.1/sources/breach_lookup.py +550 -0
  59. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/cache.py +0 -1
  60. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/cisa.py +0 -1
  61. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/email_reputation.py +0 -1
  62. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/engines.py +20 -1
  63. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/enrichment.py +388 -37
  64. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/historical_intel.py +0 -3
  65. voidaccess-1.8.1/sources/infostealer.py +417 -0
  66. voidaccess-1.8.1/sources/nvd.py +245 -0
  67. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/pastes.py +1 -2
  68. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/proxy_client.py +19 -15
  69. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/rss_scraper.py +4 -1
  70. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/telegram.py +1 -2
  71. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_analysis_opsec.py +50 -34
  72. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_analysis_stylometry.py +50 -50
  73. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_analysis_temporal.py +84 -69
  74. voidaccess-1.8.1/tests/test_breach_lookup.py +113 -0
  75. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_cli_proxy_config.py +632 -632
  76. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_db.py +7 -6
  77. voidaccess-1.8.1/tests/test_extractor_llm_pipeline.py +80 -0
  78. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_graph.py +5 -5
  79. voidaccess-1.8.1/tests/test_infostealer.py +103 -0
  80. voidaccess-1.8.1/tests/test_nvd.py +110 -0
  81. voidaccess-1.8.1/tests/test_ransomlook.py +113 -0
  82. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_regex_patterns.py +12 -8
  83. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_scrape_js.py +79 -79
  84. voidaccess-1.8.1/utils/content_dedup.py +39 -0
  85. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/content_safety.py +0 -1
  86. voidaccess-1.8.1/utils/investigation_metrics.py +142 -0
  87. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/ioc_freshness.py +1 -1
  88. {voidaccess-1.7.2 → voidaccess-1.8.1}/vector/store.py +20 -1
  89. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess/llm.py +17 -8
  90. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess/llm_utils.py +2 -4
  91. voidaccess-1.8.1/voidaccess.egg-info/PKG-INFO +228 -0
  92. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess.egg-info/SOURCES.txt +19 -0
  93. voidaccess-1.8.1/voidaccess.egg-info/requires.txt +71 -0
  94. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/__init__.py +1 -1
  95. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/adapters/sqlite.py +0 -6
  96. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/configure.py +9 -12
  97. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/enrich.py +35 -1
  98. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/investigate.py +217 -28
  99. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/show.py +1 -1
  100. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/config.py +1 -1
  101. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/main.py +0 -1
  102. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/tor_detect.py +0 -1
  103. voidaccess-1.7.2/PKG-INFO +0 -588
  104. voidaccess-1.7.2/README.md +0 -503
  105. voidaccess-1.7.2/auth/token_blacklist.py +0 -115
  106. voidaccess-1.7.2/db/migrations/versions/0009_add_investigation_id_to_relationships.py +0 -29
  107. voidaccess-1.7.2/db/migrations/versions/0018_add_user_id_to_investigations.py +0 -33
  108. voidaccess-1.7.2/voidaccess.egg-info/PKG-INFO +0 -588
  109. voidaccess-1.7.2/voidaccess.egg-info/requires.txt +0 -71
  110. {voidaccess-1.7.2 → voidaccess-1.8.1}/LICENSE +0 -0
  111. {voidaccess-1.7.2 → voidaccess-1.8.1}/analysis/__init__.py +0 -0
  112. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/__init__.py +0 -0
  113. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/__init__.py +0 -0
  114. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/actors.py +0 -0
  115. {voidaccess-1.7.2 → voidaccess-1.8.1}/api/routes/export.py +0 -0
  116. {voidaccess-1.7.2 → voidaccess-1.8.1}/auth/__init__.py +0 -0
  117. {voidaccess-1.7.2 → voidaccess-1.8.1}/crawler/__init__.py +0 -0
  118. {voidaccess-1.7.2 → voidaccess-1.8.1}/crawler/dedup.py +0 -0
  119. {voidaccess-1.7.2 → voidaccess-1.8.1}/crawler/frontier.py +0 -0
  120. {voidaccess-1.7.2 → voidaccess-1.8.1}/crawler/spider.py +0 -0
  121. {voidaccess-1.7.2 → voidaccess-1.8.1}/crawler/utils.py +0 -0
  122. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/__init__.py +0 -0
  123. {voidaccess-1.7.2/voidaccess → voidaccess-1.8.1/db/migrations}/__init__.py +0 -0
  124. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/env.py +0 -0
  125. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0001_initial_schema.py +0 -0
  126. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0002_add_investigation_status_column.py +0 -0
  127. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0004_add_page_posted_at.py +0 -0
  128. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0005_add_extraction_method.py +0 -0
  129. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0011_add_page_extraction_cache.py +0 -0
  130. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0013_add_graph_status.py +0 -0
  131. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0015_add_progress_fields.py +0 -0
  132. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0016_backfill_graph_status.py +0 -0
  133. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0017_add_user_api_keys.py +0 -0
  134. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0019_add_content_safety_log.py +0 -0
  135. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0020_add_entity_source_tracking.py +0 -0
  136. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0021_add_search_engine_stats.py +0 -0
  137. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0022_add_actor_profiles.py +0 -0
  138. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/migrations/versions/0023_add_investigation_metadata.py +0 -0
  139. {voidaccess-1.7.2 → voidaccess-1.8.1}/db/search_engine_stats.py +0 -0
  140. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/__init__.py +0 -0
  141. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/misp.py +0 -0
  142. {voidaccess-1.7.2 → voidaccess-1.8.1}/export/sigma.py +0 -0
  143. {voidaccess-1.7.2 → voidaccess-1.8.1}/fingerprint/__init__.py +0 -0
  144. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/__init__.py +0 -0
  145. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/builder.py +0 -0
  146. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/export.py +0 -0
  147. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/queries.py +0 -0
  148. {voidaccess-1.7.2 → voidaccess-1.8.1}/graph/visualize.py +0 -0
  149. {voidaccess-1.7.2 → voidaccess-1.8.1}/i18n/__init__.py +0 -0
  150. {voidaccess-1.7.2 → voidaccess-1.8.1}/i18n/detect.py +0 -0
  151. {voidaccess-1.7.2 → voidaccess-1.8.1}/i18n/query_expand.py +0 -0
  152. {voidaccess-1.7.2 → voidaccess-1.8.1}/i18n/translate.py +0 -0
  153. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/__init__.py +0 -0
  154. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/_db.py +0 -0
  155. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/alerts.py +0 -0
  156. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/config.py +0 -0
  157. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/diff.py +0 -0
  158. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/jobs.py +0 -0
  159. {voidaccess-1.7.2 → voidaccess-1.8.1}/monitor/scheduler.py +0 -0
  160. {voidaccess-1.7.2/utils → voidaccess-1.8.1/scraper}/__init__.py +0 -0
  161. {voidaccess-1.7.2 → voidaccess-1.8.1}/search/circuit_breaker.py +0 -0
  162. {voidaccess-1.7.2 → voidaccess-1.8.1}/search/query_builder.py +0 -0
  163. {voidaccess-1.7.2 → voidaccess-1.8.1}/setup.cfg +0 -0
  164. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/data/__init__.py +0 -0
  165. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/data/onion_seeds.json +0 -0
  166. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/dns_enrichment.py +0 -0
  167. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/domain_reputation.py +0 -0
  168. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/github_scraper.py +0 -0
  169. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/gitlab_scraper.py +0 -0
  170. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/hash_reputation.py +0 -0
  171. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/ip_reputation.py +0 -0
  172. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/paste_scraper.py +0 -0
  173. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/seed_manager.py +0 -0
  174. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/seeds.py +0 -0
  175. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/shodan.py +0 -0
  176. {voidaccess-1.7.2 → voidaccess-1.8.1}/sources/virustotal.py +0 -0
  177. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_api.py +0 -0
  178. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_api_monitors.py +0 -0
  179. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_blockchain.py +0 -0
  180. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_confidence_calibration.py +0 -0
  181. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_config.py +0 -0
  182. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_crawler.py +0 -0
  183. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_dns_enrichment.py +0 -0
  184. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_domain_reputation.py +0 -0
  185. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_email_reputation.py +0 -0
  186. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_filter_e2e_manual.py +0 -0
  187. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_filter_parser_manual.py +0 -0
  188. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_fingerprint.py +0 -0
  189. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_github_scraper.py +0 -0
  190. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_gitlab_scraper.py +0 -0
  191. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_graph_integration.py +0 -0
  192. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_hash_reputation.py +0 -0
  193. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_i18n.py +0 -0
  194. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_ip_reputation.py +0 -0
  195. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_llm.py +0 -0
  196. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_llm_utils.py +0 -0
  197. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_model_singleton.py +0 -0
  198. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_monitor.py +0 -0
  199. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_monitor_integration.py +0 -0
  200. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_pagination.py +0 -0
  201. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_paste_scraper.py +0 -0
  202. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_proxy_client.py +0 -0
  203. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_rss_scraper.py +0 -0
  204. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_settings.py +0 -0
  205. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_snort_export.py +0 -0
  206. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_sources.py +0 -0
  207. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_sources_enrichment_new.py +0 -0
  208. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_stix_export.py +0 -0
  209. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_vector.py +0 -0
  210. {voidaccess-1.7.2 → voidaccess-1.8.1}/tests/test_yara_export.py +0 -0
  211. {voidaccess-1.7.2/scraper → voidaccess-1.8.1/utils}/__init__.py +0 -0
  212. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/async_utils.py +0 -0
  213. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/defang.py +0 -0
  214. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/encryption.py +0 -0
  215. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/enrichment_cache.py +0 -0
  216. {voidaccess-1.7.2 → voidaccess-1.8.1}/utils/user_keys.py +0 -0
  217. {voidaccess-1.7.2 → voidaccess-1.8.1}/vector/__init__.py +0 -0
  218. {voidaccess-1.7.2 → voidaccess-1.8.1}/vector/embedder.py +0 -0
  219. {voidaccess-1.7.2 → voidaccess-1.8.1}/vector/model_singleton.py +0 -0
  220. {voidaccess-1.7.2 → voidaccess-1.8.1}/vector/search.py +0 -0
  221. {voidaccess-1.7.2/db/migrations → voidaccess-1.8.1/voidaccess}/__init__.py +0 -0
  222. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess/config.py +0 -0
  223. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess.egg-info/dependency_links.txt +0 -0
  224. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess.egg-info/entry_points.txt +0 -0
  225. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess.egg-info/top_level.txt +0 -0
  226. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/adapters/__init__.py +0 -0
  227. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/browser.py +0 -0
  228. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/__init__.py +0 -0
  229. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/actors.py +0 -0
  230. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/commands/export.py +0 -0
  231. {voidaccess-1.7.2 → voidaccess-1.8.1}/voidaccess_cli/display.py +0 -0
@@ -0,0 +1,228 @@
1
+ Metadata-Version: 2.4
2
+ Name: voidaccess
3
+ Version: 1.8.1
4
+ Summary: Dark web OSINT CLI — automated threat intelligence from query to report
5
+ Author: VoidAccess
6
+ License-Expression: MIT
7
+ Keywords: osint,darkweb,threat-intelligence,tor,cli
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Information Technology
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Security
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: typer==0.24.1
20
+ Requires-Dist: rich==15.0.0
21
+ Requires-Dist: textual==8.2.7
22
+ Requires-Dist: click==8.3.2
23
+ Requires-Dist: fastapi==0.136.0
24
+ Requires-Dist: aiohttp==3.13.5
25
+ Requires-Dist: aiohttp-socks==0.11.0
26
+ Requires-Dist: alembic==1.18.4
27
+ Requires-Dist: apscheduler==3.11.2
28
+ Requires-Dist: pillow==12.2.0
29
+ Requires-Dist: sqlalchemy[asyncio]==2.0.49
30
+ Requires-Dist: aiosqlite==0.22.1
31
+ Requires-Dist: filelock==3.29.0
32
+ Requires-Dist: numpy==2.2.6
33
+ Requires-Dist: langchain==0.3.25
34
+ Requires-Dist: langchain-openai==0.3.19
35
+ Requires-Dist: langchain-anthropic==0.3.22
36
+ Requires-Dist: langchain-google-genai==2.1.12
37
+ Requires-Dist: langchain-groq==0.3.8
38
+ Requires-Dist: langchain-ollama==0.3.10
39
+ Requires-Dist: python-dotenv==1.2.2
40
+ Requires-Dist: httpx==0.28.1
41
+ Requires-Dist: spacy==3.8.14
42
+ Requires-Dist: beautifulsoup4==4.14.3
43
+ Requires-Dist: feedparser==6.0.11
44
+ Requires-Dist: networkx==3.4.2
45
+ Requires-Dist: pydantic==2.13.3
46
+ Requires-Dist: python-jose[cryptography]==3.5.0
47
+ Requires-Dist: passlib[bcrypt]==1.7.4
48
+ Requires-Dist: slowapi==0.1.9
49
+ Requires-Dist: redis==7.4.0
50
+ Requires-Dist: pyyaml==6.0.3
51
+ Requires-Dist: openai==1.109.1
52
+ Requires-Dist: cryptography==46.0.7
53
+ Requires-Dist: python-dateutil==2.9.0.post0
54
+ Requires-Dist: langdetect==1.0.9
55
+ Requires-Dist: pyvis==0.3.2
56
+ Requires-Dist: urllib3==2.6.3
57
+ Requires-Dist: trafilatura==2.0.0
58
+ Requires-Dist: requests==2.33.1
59
+ Requires-Dist: python-socks[asyncio]==2.8.1
60
+ Requires-Dist: tldextract==5.3.1
61
+ Requires-Dist: stix2==3.0.2
62
+ Provides-Extra: dev
63
+ Requires-Dist: pytest; extra == "dev"
64
+ Requires-Dist: pytest-asyncio; extra == "dev"
65
+ Provides-Extra: nlp
66
+ Requires-Dist: sentence-transformers>=3.0; extra == "nlp"
67
+ Requires-Dist: torch>=2.3; extra == "nlp"
68
+ Requires-Dist: transformers>=4.42; extra == "nlp"
69
+ Provides-Extra: vector
70
+ Requires-Dist: chromadb>=0.5; extra == "vector"
71
+ Requires-Dist: sentence-transformers>=3.0; extra == "vector"
72
+ Requires-Dist: torch>=2.3; extra == "vector"
73
+ Provides-Extra: telegram
74
+ Requires-Dist: telethon>=1.36; extra == "telegram"
75
+ Provides-Extra: js
76
+ Requires-Dist: playwright>=1.45; extra == "js"
77
+ Provides-Extra: all
78
+ Requires-Dist: chromadb>=0.5; extra == "all"
79
+ Requires-Dist: playwright>=1.45; extra == "all"
80
+ Requires-Dist: sentence-transformers>=3.0; extra == "all"
81
+ Requires-Dist: telethon>=1.36; extra == "all"
82
+ Requires-Dist: torch>=2.3; extra == "all"
83
+ Requires-Dist: transformers>=4.42; extra == "all"
84
+ Dynamic: license-file
85
+
86
+ <p align="center">
87
+ <img src="public/logo_circle.png" width="160" alt="VoidAccess logo">
88
+ </p>
89
+
90
+ <h1 align="center">VoidAccess</h1>
91
+
92
+ <p align="center">
93
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
94
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
95
+ <a href="docker-compose.yml"><img src="https://img.shields.io/badge/Docker-Compose-blue.svg" alt="Docker Compose"></a>
96
+ <a href="https://pypi.org/project/voidaccess/"><img src="https://img.shields.io/pypi/v/voidaccess?logo=pypi&logoColor=white" alt="PyPI version"></a>
97
+ <a href="https://pypi.org/project/voidaccess/"><img src="https://img.shields.io/pypi/dm/voidaccess" alt="PyPI downloads"></a>
98
+ </p>
99
+
100
+ Self-hostable OSINT for turning dark-web research queries into structured threat intelligence. Built for security researchers, threat-intelligence teams, and authorized investigators who need collection, enrichment, relationship mapping, and export in one workflow.
101
+
102
+ ## Terminal Output
103
+
104
+ ![VoidAccess](public/terminal-hero.png)
105
+
106
+ ## Quick Start
107
+
108
+ ```bash
109
+ pip install voidaccess
110
+ voidaccess investigate "LockBit ransomware" --no-llm --no-tor --depth shallow
111
+ voidaccess list
112
+ voidaccess actors
113
+ voidaccess status
114
+ ```
115
+
116
+ ![VoidAccess investigation demo](public/cli_investigation_gif.gif)
117
+
118
+ ## What It Does
119
+
120
+ - **Parallel collection** - searches Tor indexes, paste sites, code forges, security feeds, and curated `.onion` seeds.
121
+ - **Entity extraction** - finds IOCs, wallets, credentials, handles, vulnerabilities, actors, malware, people, and organizations.
122
+ - **Multi-source enrichment** - adds reputation, breach, passive DNS, sandbox, blockchain, and threat-feed context.
123
+ - **Actor intelligence** - persists aliases, infrastructure, notes, timelines, and cross-investigation relationships.
124
+ - **Relationship graphs** - builds co-occurrence graphs, communities, paths, and infrastructure clusters.
125
+ - **Content safety** - filters prohibited queries, URLs, content, and extracted entities at mandatory pipeline gates.
126
+ - **Structured exports** - produces STIX 2.1, MISP, Sigma, YARA, Snort, Suricata, CSV, Markdown, JSON, and IOC packages.
127
+ - **CLI and web UI** - runs locally with SQLite or as a Docker Compose stack with PostgreSQL and a browser interface.
128
+
129
+ ## Pipeline
130
+
131
+ | Stage | Action |
132
+ |---|---|
133
+ | 1 | Refine the investigation query with the selected LLM |
134
+ | 2 | Collect from Tor search, paste sites, code forges, RSS feeds, and curated seeds in parallel |
135
+ | 3 | Filter noisy or irrelevant pages |
136
+ | 4 | Enrich the query and early indicators from threat-intelligence sources |
137
+ | 5 | Discover additional `.onion` links recursively |
138
+ | 6 | Reuse recently processed pages from the vector cache |
139
+ | 7 | Fetch selected pages through Tor with response-size limits |
140
+ | 8 | Persist newly collected content |
141
+ | 9 | Merge collected and enriched intelligence |
142
+ | 10 | Extract entities with regex, NER, and optional LLM analysis |
143
+ | 11 | Cross-reference entities against historical and seed datasets |
144
+ | 12 | Build relationships, communities, and infrastructure clusters |
145
+ | 13 | Generate the final intelligence summary and export-ready result |
146
+
147
+ Full pipeline behavior, timeouts, recovery, and data flow are documented in [Architecture](docs/architecture.md).
148
+
149
+ ## Entity Types
150
+
151
+ | Category | Examples |
152
+ |---|---|
153
+ | Cryptocurrency | Bitcoin, Ethereum, Monero, Litecoin, Zcash, Solana, Tron, ENS |
154
+ | Network indicators | IPv4, IPv6, domains, URLs, `.onion` addresses, MAC addresses, PGP keys |
155
+ | File indicators | MD5, SHA-1, SHA-256, malware families |
156
+ | Credentials | Cloud keys, tokens, JWTs, API keys, stealer logs, combo-list entries |
157
+ | Messaging | Telegram, Discord, XMPP, Tox, Session, Matrix, Wire, ICQ, Wickr |
158
+ | Vulnerabilities | CVEs, MITRE ATT&CK techniques and tactics, Exploit-DB IDs |
159
+ | Detection content | YARA rules, Nuclei templates, Snort and Suricata indicators |
160
+ | Threat intelligence | Actor handles, ransomware groups, paste links, people, organizations, locations |
161
+
162
+ ## Collection and Enrichment
163
+
164
+ | Layer | Sources |
165
+ |---|---|
166
+ | Dark-web search | 16+ Tor search engines and curated `.onion` seeds |
167
+ | Open-web collection | Pastebin, dpaste, paste.ee, Rentry, GitHub, GitLab, and curated RSS feeds |
168
+ | Threat feeds | AlienVault OTX, abuse.ch, MalwareBazaar, ThreatFox, URLhaus, ransomware.live, CISA KEV |
169
+ | IP and domain context | Shodan InternetDB, GreyNoise, AbuseIPDB, Feodo Tracker, C2IntelFeeds, crt.sh, URLScan.io, Wayback Machine, CIRCL PDNS, RDAP |
170
+ | File and identity context | VirusTotal, Hybrid Analysis, Have I Been Pwned, EmailRep |
171
+ | Blockchain | BlockCypher and Etherscan |
172
+
173
+ Sources that need API keys skip cleanly when their keys are absent. The complete key and configuration reference is in [Architecture](docs/architecture.md#13-configuration-reference).
174
+
175
+ ## LLM Providers
176
+
177
+ | Provider | Typical models | Notes |
178
+ |---|---|---|
179
+ | OpenRouter | DeepSeek, Llama, Claude | Default route; free models are available |
180
+ | Groq | Llama | Fast hosted inference with a free tier |
181
+ | OpenAI | GPT models | API key required |
182
+ | Anthropic | Claude | Claude Haiku is the tested default |
183
+ | Google Gemini | Gemini Flash and Pro | Google AI Studio key required |
184
+ | Ollama | Any installed local model | Local and suitable for air-gapped deployments |
185
+
186
+ ## CLI Reference
187
+
188
+ | Command | Description |
189
+ |---------|-------------|
190
+ | `voidaccess investigate "QUERY"` | Run an investigation |
191
+ | `voidaccess show` | Open the interactive entity browser |
192
+ | `voidaccess export INVESTIGATION_ID --format FORMAT` | Export as STIX, MISP, Sigma, YARA, Snort, Suricata, package, CSV, Markdown, or JSON |
193
+ | `voidaccess package INVESTIGATION_ID` | Build an IOC package ZIP |
194
+ | `voidaccess enrich INVESTIGATION_ID` | Re-enrich a saved investigation |
195
+ | `voidaccess list` | List saved investigations |
196
+ | `voidaccess status` | Show configuration, Tor, cache, engine, and seed status |
197
+ | `voidaccess actors` | List persistent actor profiles |
198
+ | `voidaccess actor HANDLE` | Show an actor profile |
199
+ | `voidaccess actor HANDLE --timeline` | Show an actor activity timeline |
200
+ | `voidaccess actor HANDLE --note "TEXT"` | Add an analyst note to an actor profile |
201
+ | `voidaccess timeline HANDLE` | Open an actor timeline directly |
202
+ | `voidaccess configure` | Run the setup wizard |
203
+ | `voidaccess configure llm` | Configure the LLM provider, model, and key |
204
+ | `voidaccess configure keys` | Configure enrichment API keys |
205
+ | `voidaccess configure tor` | Override the Tor proxy host and port |
206
+ | `voidaccess version` | Print the installed version |
207
+
208
+ Optional clearnet requests can use [ScrapingAnt](https://scrapingant.com/?ref=mzliyzh) with `--use-scraping-api` or `--use-proxies`; Tor, `.onion`, GitHub, and GitLab traffic are unaffected.
209
+
210
+ ## Self-Hosting
211
+
212
+ Run the full PostgreSQL, Tor, FastAPI, and Next.js stack with Docker Compose. The [self-hosting guide](docs/self-hosting.md) covers guided setup, environment configuration, operations, and troubleshooting.
213
+
214
+ ## Links
215
+
216
+ | | |
217
+ |-|-|
218
+ | [Self-hosting guide](docs/self-hosting.md) | Docker Compose, environment setup, operations, and troubleshooting |
219
+ | [Architecture](docs/architecture.md) | Pipeline internals, modules, schema, API, enrichment, graph, and configuration reference |
220
+ | [Contributing](CONTRIBUTING.md) | Development setup, standards, and pull requests |
221
+ | [Security](SECURITY.md) | Supported versions and private vulnerability reporting |
222
+ | [Usage policy](docs/USAGE_POLICY.md) | Authorized-use requirements and prohibited activity |
223
+ | [PyPI](https://pypi.org/project/voidaccess/) | Published package and release files |
224
+ | [GitHub](https://github.com/KatrielMoses/VoidAccess) | Source, issues, and releases |
225
+
226
+ ## License
227
+
228
+ MIT. Use VoidAccess only for authorized security research and threat-intelligence work; see the [Usage Policy](docs/USAGE_POLICY.md).
@@ -0,0 +1,143 @@
1
+ <p align="center">
2
+ <img src="public/logo_circle.png" width="160" alt="VoidAccess logo">
3
+ </p>
4
+
5
+ <h1 align="center">VoidAccess</h1>
6
+
7
+ <p align="center">
8
+ <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT"></a>
9
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/Python-3.10%2B-blue.svg" alt="Python 3.10+"></a>
10
+ <a href="docker-compose.yml"><img src="https://img.shields.io/badge/Docker-Compose-blue.svg" alt="Docker Compose"></a>
11
+ <a href="https://pypi.org/project/voidaccess/"><img src="https://img.shields.io/pypi/v/voidaccess?logo=pypi&logoColor=white" alt="PyPI version"></a>
12
+ <a href="https://pypi.org/project/voidaccess/"><img src="https://img.shields.io/pypi/dm/voidaccess" alt="PyPI downloads"></a>
13
+ </p>
14
+
15
+ Self-hostable OSINT for turning dark-web research queries into structured threat intelligence. Built for security researchers, threat-intelligence teams, and authorized investigators who need collection, enrichment, relationship mapping, and export in one workflow.
16
+
17
+ ## Terminal Output
18
+
19
+ ![VoidAccess](public/terminal-hero.png)
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ pip install voidaccess
25
+ voidaccess investigate "LockBit ransomware" --no-llm --no-tor --depth shallow
26
+ voidaccess list
27
+ voidaccess actors
28
+ voidaccess status
29
+ ```
30
+
31
+ ![VoidAccess investigation demo](public/cli_investigation_gif.gif)
32
+
33
+ ## What It Does
34
+
35
+ - **Parallel collection** - searches Tor indexes, paste sites, code forges, security feeds, and curated `.onion` seeds.
36
+ - **Entity extraction** - finds IOCs, wallets, credentials, handles, vulnerabilities, actors, malware, people, and organizations.
37
+ - **Multi-source enrichment** - adds reputation, breach, passive DNS, sandbox, blockchain, and threat-feed context.
38
+ - **Actor intelligence** - persists aliases, infrastructure, notes, timelines, and cross-investigation relationships.
39
+ - **Relationship graphs** - builds co-occurrence graphs, communities, paths, and infrastructure clusters.
40
+ - **Content safety** - filters prohibited queries, URLs, content, and extracted entities at mandatory pipeline gates.
41
+ - **Structured exports** - produces STIX 2.1, MISP, Sigma, YARA, Snort, Suricata, CSV, Markdown, JSON, and IOC packages.
42
+ - **CLI and web UI** - runs locally with SQLite or as a Docker Compose stack with PostgreSQL and a browser interface.
43
+
44
+ ## Pipeline
45
+
46
+ | Stage | Action |
47
+ |---|---|
48
+ | 1 | Refine the investigation query with the selected LLM |
49
+ | 2 | Collect from Tor search, paste sites, code forges, RSS feeds, and curated seeds in parallel |
50
+ | 3 | Filter noisy or irrelevant pages |
51
+ | 4 | Enrich the query and early indicators from threat-intelligence sources |
52
+ | 5 | Discover additional `.onion` links recursively |
53
+ | 6 | Reuse recently processed pages from the vector cache |
54
+ | 7 | Fetch selected pages through Tor with response-size limits |
55
+ | 8 | Persist newly collected content |
56
+ | 9 | Merge collected and enriched intelligence |
57
+ | 10 | Extract entities with regex, NER, and optional LLM analysis |
58
+ | 11 | Cross-reference entities against historical and seed datasets |
59
+ | 12 | Build relationships, communities, and infrastructure clusters |
60
+ | 13 | Generate the final intelligence summary and export-ready result |
61
+
62
+ Full pipeline behavior, timeouts, recovery, and data flow are documented in [Architecture](docs/architecture.md).
63
+
64
+ ## Entity Types
65
+
66
+ | Category | Examples |
67
+ |---|---|
68
+ | Cryptocurrency | Bitcoin, Ethereum, Monero, Litecoin, Zcash, Solana, Tron, ENS |
69
+ | Network indicators | IPv4, IPv6, domains, URLs, `.onion` addresses, MAC addresses, PGP keys |
70
+ | File indicators | MD5, SHA-1, SHA-256, malware families |
71
+ | Credentials | Cloud keys, tokens, JWTs, API keys, stealer logs, combo-list entries |
72
+ | Messaging | Telegram, Discord, XMPP, Tox, Session, Matrix, Wire, ICQ, Wickr |
73
+ | Vulnerabilities | CVEs, MITRE ATT&CK techniques and tactics, Exploit-DB IDs |
74
+ | Detection content | YARA rules, Nuclei templates, Snort and Suricata indicators |
75
+ | Threat intelligence | Actor handles, ransomware groups, paste links, people, organizations, locations |
76
+
77
+ ## Collection and Enrichment
78
+
79
+ | Layer | Sources |
80
+ |---|---|
81
+ | Dark-web search | 16+ Tor search engines and curated `.onion` seeds |
82
+ | Open-web collection | Pastebin, dpaste, paste.ee, Rentry, GitHub, GitLab, and curated RSS feeds |
83
+ | Threat feeds | AlienVault OTX, abuse.ch, MalwareBazaar, ThreatFox, URLhaus, ransomware.live, CISA KEV |
84
+ | IP and domain context | Shodan InternetDB, GreyNoise, AbuseIPDB, Feodo Tracker, C2IntelFeeds, crt.sh, URLScan.io, Wayback Machine, CIRCL PDNS, RDAP |
85
+ | File and identity context | VirusTotal, Hybrid Analysis, Have I Been Pwned, EmailRep |
86
+ | Blockchain | BlockCypher and Etherscan |
87
+
88
+ Sources that need API keys skip cleanly when their keys are absent. The complete key and configuration reference is in [Architecture](docs/architecture.md#13-configuration-reference).
89
+
90
+ ## LLM Providers
91
+
92
+ | Provider | Typical models | Notes |
93
+ |---|---|---|
94
+ | OpenRouter | DeepSeek, Llama, Claude | Default route; free models are available |
95
+ | Groq | Llama | Fast hosted inference with a free tier |
96
+ | OpenAI | GPT models | API key required |
97
+ | Anthropic | Claude | Claude Haiku is the tested default |
98
+ | Google Gemini | Gemini Flash and Pro | Google AI Studio key required |
99
+ | Ollama | Any installed local model | Local and suitable for air-gapped deployments |
100
+
101
+ ## CLI Reference
102
+
103
+ | Command | Description |
104
+ |---------|-------------|
105
+ | `voidaccess investigate "QUERY"` | Run an investigation |
106
+ | `voidaccess show` | Open the interactive entity browser |
107
+ | `voidaccess export INVESTIGATION_ID --format FORMAT` | Export as STIX, MISP, Sigma, YARA, Snort, Suricata, package, CSV, Markdown, or JSON |
108
+ | `voidaccess package INVESTIGATION_ID` | Build an IOC package ZIP |
109
+ | `voidaccess enrich INVESTIGATION_ID` | Re-enrich a saved investigation |
110
+ | `voidaccess list` | List saved investigations |
111
+ | `voidaccess status` | Show configuration, Tor, cache, engine, and seed status |
112
+ | `voidaccess actors` | List persistent actor profiles |
113
+ | `voidaccess actor HANDLE` | Show an actor profile |
114
+ | `voidaccess actor HANDLE --timeline` | Show an actor activity timeline |
115
+ | `voidaccess actor HANDLE --note "TEXT"` | Add an analyst note to an actor profile |
116
+ | `voidaccess timeline HANDLE` | Open an actor timeline directly |
117
+ | `voidaccess configure` | Run the setup wizard |
118
+ | `voidaccess configure llm` | Configure the LLM provider, model, and key |
119
+ | `voidaccess configure keys` | Configure enrichment API keys |
120
+ | `voidaccess configure tor` | Override the Tor proxy host and port |
121
+ | `voidaccess version` | Print the installed version |
122
+
123
+ Optional clearnet requests can use [ScrapingAnt](https://scrapingant.com/?ref=mzliyzh) with `--use-scraping-api` or `--use-proxies`; Tor, `.onion`, GitHub, and GitLab traffic are unaffected.
124
+
125
+ ## Self-Hosting
126
+
127
+ Run the full PostgreSQL, Tor, FastAPI, and Next.js stack with Docker Compose. The [self-hosting guide](docs/self-hosting.md) covers guided setup, environment configuration, operations, and troubleshooting.
128
+
129
+ ## Links
130
+
131
+ | | |
132
+ |-|-|
133
+ | [Self-hosting guide](docs/self-hosting.md) | Docker Compose, environment setup, operations, and troubleshooting |
134
+ | [Architecture](docs/architecture.md) | Pipeline internals, modules, schema, API, enrichment, graph, and configuration reference |
135
+ | [Contributing](CONTRIBUTING.md) | Development setup, standards, and pull requests |
136
+ | [Security](SECURITY.md) | Supported versions and private vulnerability reporting |
137
+ | [Usage policy](docs/USAGE_POLICY.md) | Authorized-use requirements and prohibited activity |
138
+ | [PyPI](https://pypi.org/project/voidaccess/) | Published package and release files |
139
+ | [GitHub](https://github.com/KatrielMoses/VoidAccess) | Source, issues, and releases |
140
+
141
+ ## License
142
+
143
+ MIT. Use VoidAccess only for authorized security research and threat-intelligence work; see the [Usage Policy](docs/USAGE_POLICY.md).
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
  import logging
9
9
  import re
10
10
  from collections import Counter
11
+ from datetime import timezone
11
12
  from typing import Optional
12
13
 
13
14
  logger = logging.getLogger(__name__)
@@ -65,11 +66,16 @@ def detect_timezone_leak(texts_with_timestamps: list[dict]) -> dict:
65
66
  if ts is None:
66
67
  continue
67
68
  if hasattr(ts, "utcoffset") and ts.utcoffset() is not None:
68
- # Convert to UTC
69
- utc_ts = ts.astimezone(tz=None).replace(tzinfo=None)
69
+ # Convert to an explicit, host-independent reference frame. Using
70
+ # tz=None here made results depend on the analyst server's zone.
71
+ utc_ts = ts.astimezone(timezone.utc)
70
72
  posting_hours.append(utc_ts.hour)
71
73
  else:
72
- posting_hours.append(ts.hour)
74
+ # Naive timestamps have no information with which to convert. The
75
+ # ingestion contract treats them as UTC; make that assumption
76
+ # explicit instead of allowing the host timezone to leak in.
77
+ utc_ts = ts.replace(tzinfo=timezone.utc) if hasattr(ts, "replace") else ts
78
+ posting_hours.append(utc_ts.hour)
73
79
 
74
80
  if not posting_hours:
75
81
  return {
@@ -9,7 +9,6 @@ from __future__ import annotations
9
9
 
10
10
  import logging
11
11
  from datetime import date, timedelta
12
- from typing import Any
13
12
 
14
13
  from analysis.temporal import ( # noqa: E402 — imported at module level for patchability
15
14
  build_activity_timeline,
@@ -10,8 +10,9 @@ from __future__ import annotations
10
10
  import logging
11
11
  import math
12
12
  from collections import defaultdict
13
- from datetime import date, datetime, timedelta, timezone
14
- from typing import Any, Optional
13
+ from datetime import date, datetime
14
+ from statistics import median
15
+ from typing import Optional
15
16
 
16
17
  logger = logging.getLogger(__name__)
17
18
 
@@ -124,44 +125,78 @@ def compute_activity_stats(timeline: list[dict]) -> dict:
124
125
  }
125
126
 
126
127
 
127
- Z_SCORE_THRESHOLD = 2.5
128
+ POISSON_ALPHA = 0.01
128
129
  MIN_DATA_POINTS = 10
129
- MIN_ABSOLUTE_SPIKE = 5
130
+
131
+
132
+ def _poisson_lower_tail(count: int, rate: float) -> float:
133
+ """Return P(X <= count) for X ~ Poisson(rate), without scipy."""
134
+ if rate <= 0:
135
+ return 1.0 if count >= 0 else 0.0
136
+ term = math.exp(-rate)
137
+ total = term
138
+ for k in range(1, count + 1):
139
+ term *= rate / k
140
+ total += term
141
+ return min(1.0, max(0.0, total))
142
+
143
+
144
+ def _poisson_upper_tail(count: int, rate: float) -> float:
145
+ """Return P(X >= count) for X ~ Poisson(rate)."""
146
+ if count <= 0:
147
+ return 1.0
148
+ if rate <= 0:
149
+ return 0.0
150
+ return min(1.0, max(0.0, 1.0 - _poisson_lower_tail(count - 1, rate)))
130
151
 
131
152
 
132
153
  def detect_anomalies(
133
154
  timeline: list[dict],
134
- z_threshold: float = Z_SCORE_THRESHOLD,
155
+ alpha: float = POISSON_ALPHA,
156
+ z_threshold: float | None = None,
135
157
  ) -> list[dict]:
136
158
  """
137
- Flag days where activity deviates > z_threshold standard deviations.
159
+ Flag unusually rare daily counts under a Poisson event-count model.
138
160
 
139
- Returns list of {"date": date, "count": int, "z_score": float, "type": str}.
140
- Returns [] for timelines with fewer than 10 data points OR fewer than 5 posts.
161
+ The baseline rate is the median daily count, which is robust to the very
162
+ spikes this function is intended to detect. ``z_threshold`` remains an
163
+ ignored compatibility parameter for callers of the old API; decisions are
164
+ now made from Poisson tail probabilities and ``alpha``.
165
+
166
+ Returns dictionaries containing ``poisson_p_value`` and ``expected_rate``.
167
+ Returns [] for timelines with fewer than 10 data points.
141
168
  """
142
169
  if len(timeline) < MIN_DATA_POINTS:
143
170
  return []
144
-
145
- stats = compute_activity_stats(timeline)
146
- mean = stats["mean_daily"]
147
- std = stats["std_daily"]
148
-
149
- if std == 0.0:
171
+ if z_threshold is not None:
172
+ logger.debug("detect_anomalies: z_threshold is deprecated; using Poisson alpha=%s", alpha)
173
+ if not 0.0 < alpha < 1.0:
174
+ raise ValueError("alpha must be between 0 and 1")
175
+
176
+ counts = [max(0, int(entry.get("count", 0))) for entry in timeline]
177
+ rate = float(median(counts))
178
+ if rate == 0.0 and not any(counts):
150
179
  return []
151
180
 
152
181
  anomalies: list[dict] = []
153
- for entry in timeline:
154
- count = entry["count"]
155
- z = (count - mean) / std
156
- if abs(z) > z_threshold:
157
- if z > 0 and count < MIN_ABSOLUTE_SPIKE:
158
- continue
182
+ for entry, count in zip(timeline, counts):
183
+ if count > rate:
184
+ p_value = _poisson_upper_tail(count, rate)
185
+ anomaly_type = "spike"
186
+ elif count < rate:
187
+ p_value = _poisson_lower_tail(count, rate)
188
+ anomaly_type = "drop"
189
+ else:
190
+ p_value = 1.0
191
+ anomaly_type = "variation"
192
+ if p_value <= alpha:
159
193
  anomalies.append(
160
194
  {
161
195
  "date": entry["date"],
162
196
  "count": count,
163
- "z_score": float(z),
164
- "type": "spike" if z > 0 else "drop",
197
+ "poisson_p_value": float(p_value),
198
+ "expected_rate": rate,
199
+ "type": anomaly_type,
165
200
  }
166
201
  )
167
202
 
@@ -8,6 +8,7 @@ Flow:
8
8
  4. POST /auth/reset-password → sets new password, clears must_reset flag
9
9
  """
10
10
 
11
+ import logging
11
12
  import secrets
12
13
  from datetime import datetime, timedelta, timezone
13
14
  from typing import Optional
@@ -22,7 +23,9 @@ from sqlalchemy.orm import Session
22
23
  from config import JWT_SECRET
23
24
  from db.models import User
24
25
  from db.session import get_session, get_db
25
- from auth.token_blacklist import is_token_revoked
26
+ from auth.token_blacklist import is_token_revoked, BlacklistUnavailableError
27
+
28
+ logger = logging.getLogger(__name__)
26
29
 
27
30
  # Config — single canonical source from config.py
28
31
  SECRET = JWT_SECRET
@@ -130,7 +133,28 @@ async def get_current_user(
130
133
  raise credentials_exception
131
134
 
132
135
  if token_payload.jti:
133
- if await is_token_revoked(token_payload.jti):
136
+ # Fail closed when revocation was opted in (REDIS_URL set) but the
137
+ # blacklist store is unreachable: we cannot confirm the token has not
138
+ # been revoked, so we refuse rather than silently honour it. Enforcement
139
+ # resumes automatically once Redis is reachable again. When REDIS_URL is
140
+ # unset, is_token_revoked() simply returns False (disabled by design).
141
+ try:
142
+ token_revoked = await is_token_revoked(token_payload.jti)
143
+ except BlacklistUnavailableError:
144
+ logger.warning(
145
+ "Token revocation store unavailable — failing closed (rejecting "
146
+ "authentication). Restore Redis, or unset REDIS_URL to disable "
147
+ "blacklist enforcement (fail-open by design)."
148
+ )
149
+ raise HTTPException(
150
+ status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
151
+ detail=(
152
+ "Authentication temporarily unavailable: the token revocation "
153
+ "store is unreachable."
154
+ ),
155
+ headers={"WWW-Authenticate": "Bearer"},
156
+ )
157
+ if token_revoked:
134
158
  raise revoked_exception
135
159
 
136
160
  user = db.query(User).filter(
@@ -0,0 +1,99 @@
1
+ """
2
+ api/errors.py — Safe error responses: public generic message, private detailed log.
3
+
4
+ All internal/unhandled errors surfaced to API clients must go through these
5
+ helpers so that:
6
+
7
+ * the client only ever sees a generic message plus a correlation ID, and
8
+ * the full exception (message + traceback) is logged server-side, tagged
9
+ with the same correlation ID, for the operator to correlate.
10
+
11
+ This is the standard "public error, private log" split. Never place a raw
12
+ exception object or its string representation into a response body — a raw
13
+ message can leak database connection fragments, internal file paths, the
14
+ reason an enrichment provider call failed (which may include partial
15
+ credential material for a malformed key), or other implementation details
16
+ that make the system easier to probe. Use these helpers instead.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import logging
22
+ import uuid
23
+ from typing import Optional
24
+
25
+ from fastapi import HTTPException, Request
26
+
27
+ logger = logging.getLogger("voidaccess.api.errors")
28
+
29
+ # Generic, safe message shown to clients for any unhandled/internal error.
30
+ GENERIC_ERROR_MESSAGE = (
31
+ "An internal error occurred. Reference the correlation ID when contacting support."
32
+ )
33
+
34
+
35
+ def new_correlation_id() -> str:
36
+ """Return a fresh correlation ID for a single failed request."""
37
+ return uuid.uuid4().hex
38
+
39
+
40
+ def log_exception(
41
+ exc: BaseException,
42
+ *,
43
+ context: str = "",
44
+ request: Optional[Request] = None,
45
+ correlation_id: Optional[str] = None,
46
+ level: int = logging.ERROR,
47
+ ) -> str:
48
+ """Log the full exception detail server-side, tagged with a correlation ID.
49
+
50
+ Returns the correlation ID so the caller can echo it (and only it) back to
51
+ the client. The full message and traceback stay in the server log only.
52
+ """
53
+ correlation_id = correlation_id or new_correlation_id()
54
+
55
+ where = context
56
+ if request is not None:
57
+ try:
58
+ where = f"{request.method} {request.url.path}"
59
+ if context:
60
+ where = f"{where} ({context})"
61
+ except Exception: # never let logging path-extraction raise
62
+ where = context
63
+
64
+ logger.log(
65
+ level,
66
+ "[correlation_id=%s] Internal error in %s: %s",
67
+ correlation_id,
68
+ where or "request",
69
+ exc,
70
+ exc_info=exc,
71
+ )
72
+ return correlation_id
73
+
74
+
75
+ def safe_detail(correlation_id: str, message: str = GENERIC_ERROR_MESSAGE) -> str:
76
+ """A generic ``detail`` string safe to return to clients, with the ID appended."""
77
+ return f"{message} (correlation ID: {correlation_id})"
78
+
79
+
80
+ def safe_error_body(correlation_id: str, message: str = GENERIC_ERROR_MESSAGE) -> dict:
81
+ """A generic JSON body safe to return to clients (generic message + ID)."""
82
+ return {"detail": message, "correlation_id": correlation_id}
83
+
84
+
85
+ def internal_http_exception(
86
+ exc: BaseException,
87
+ *,
88
+ request: Optional[Request] = None,
89
+ context: str = "",
90
+ status_code: int = 500,
91
+ ) -> HTTPException:
92
+ """Log ``exc`` in full and build an ``HTTPException`` with a safe body.
93
+
94
+ Raise the returned exception from a route handler:
95
+
96
+ raise internal_http_exception(exc, context="create_monitor")
97
+ """
98
+ correlation_id = log_exception(exc, context=context, request=request)
99
+ return HTTPException(status_code=status_code, detail=safe_detail(correlation_id))