quirk-scanner 4.10.0__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 (334) hide show
  1. quirk_scanner-4.10.0/LICENSE +21 -0
  2. quirk_scanner-4.10.0/PKG-INFO +75 -0
  3. quirk_scanner-4.10.0/README.md +110 -0
  4. quirk_scanner-4.10.0/pyproject.toml +149 -0
  5. quirk_scanner-4.10.0/quirk/__init__.py +29 -0
  6. quirk_scanner-4.10.0/quirk/assessment/migration_advisor.py +102 -0
  7. quirk_scanner-4.10.0/quirk/assessment/operator_context.py +137 -0
  8. quirk_scanner-4.10.0/quirk/cbom/__init__.py +12 -0
  9. quirk_scanner-4.10.0/quirk/cbom/builder.py +716 -0
  10. quirk_scanner-4.10.0/quirk/cbom/classifier.py +249 -0
  11. quirk_scanner-4.10.0/quirk/cbom/writer.py +103 -0
  12. quirk_scanner-4.10.0/quirk/cli/__init__.py +0 -0
  13. quirk_scanner-4.10.0/quirk/cli/banner.py +179 -0
  14. quirk_scanner-4.10.0/quirk/cli/cmvp_cmd.py +148 -0
  15. quirk_scanner-4.10.0/quirk/cli/doctor_cmd.py +280 -0
  16. quirk_scanner-4.10.0/quirk/cli/errors_cmd.py +129 -0
  17. quirk_scanner-4.10.0/quirk/cli/init_cmd.py +64 -0
  18. quirk_scanner-4.10.0/quirk/cli/job_progress.py +107 -0
  19. quirk_scanner-4.10.0/quirk/cli/qramm_cmd.py +65 -0
  20. quirk_scanner-4.10.0/quirk/cli/schedule_cmd.py +192 -0
  21. quirk_scanner-4.10.0/quirk/cli/scheduler_cmd.py +244 -0
  22. quirk_scanner-4.10.0/quirk/compliance/__init__.py +292 -0
  23. quirk_scanner-4.10.0/quirk/compliance/cmvp.py +528 -0
  24. quirk_scanner-4.10.0/quirk/config.py +489 -0
  25. quirk_scanner-4.10.0/quirk/config_template.yaml +143 -0
  26. quirk_scanner-4.10.0/quirk/dashboard/__init__.py +0 -0
  27. quirk_scanner-4.10.0/quirk/dashboard/api/__init__.py +0 -0
  28. quirk_scanner-4.10.0/quirk/dashboard/api/app.py +159 -0
  29. quirk_scanner-4.10.0/quirk/dashboard/api/deps.py +58 -0
  30. quirk_scanner-4.10.0/quirk/dashboard/api/middleware/__init__.py +1 -0
  31. quirk_scanner-4.10.0/quirk/dashboard/api/middleware/auth.py +50 -0
  32. quirk_scanner-4.10.0/quirk/dashboard/api/middleware/csrf.py +29 -0
  33. quirk_scanner-4.10.0/quirk/dashboard/api/middleware/rate_limit.py +68 -0
  34. quirk_scanner-4.10.0/quirk/dashboard/api/routes/__init__.py +0 -0
  35. quirk_scanner-4.10.0/quirk/dashboard/api/routes/health.py +12 -0
  36. quirk_scanner-4.10.0/quirk/dashboard/api/routes/jobs.py +211 -0
  37. quirk_scanner-4.10.0/quirk/dashboard/api/routes/pdf.py +118 -0
  38. quirk_scanner-4.10.0/quirk/dashboard/api/routes/qramm.py +749 -0
  39. quirk_scanner-4.10.0/quirk/dashboard/api/routes/scan.py +1267 -0
  40. quirk_scanner-4.10.0/quirk/dashboard/api/routes/schedules.py +192 -0
  41. quirk_scanner-4.10.0/quirk/dashboard/api/routes/trends.py +195 -0
  42. quirk_scanner-4.10.0/quirk/dashboard/api/schemas.py +367 -0
  43. quirk_scanner-4.10.0/quirk/dashboard/server.py +50 -0
  44. quirk_scanner-4.10.0/quirk/dashboard/static/assets/index-D2zfvPHF.css +1 -0
  45. quirk_scanner-4.10.0/quirk/dashboard/static/assets/index-DwADKPu4.js +56 -0
  46. quirk_scanner-4.10.0/quirk/dashboard/static/assets/rolldown-runtime-B1FJdls4.js +1 -0
  47. quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-charts-CBKA7ysD.js +63 -0
  48. quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-graph-DyJHMikF.js +321 -0
  49. quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-react-BaxwhIbJ.js +11 -0
  50. quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-table-BxYSsWPM.js +4 -0
  51. quirk_scanner-4.10.0/quirk/dashboard/static/favicon.ico +0 -0
  52. quirk_scanner-4.10.0/quirk/dashboard/static/favicon.png +0 -0
  53. quirk_scanner-4.10.0/quirk/dashboard/static/favicon.svg +13 -0
  54. quirk_scanner-4.10.0/quirk/dashboard/static/fonts/JetBrainsMono-Italic[wght].ttf +0 -0
  55. quirk_scanner-4.10.0/quirk/dashboard/static/fonts/JetBrainsMono[wght].ttf +0 -0
  56. quirk_scanner-4.10.0/quirk/dashboard/static/icons.svg +24 -0
  57. quirk_scanner-4.10.0/quirk/dashboard/static/index.html +21 -0
  58. quirk_scanner-4.10.0/quirk/db.py +423 -0
  59. quirk_scanner-4.10.0/quirk/discovery/coverage.py +30 -0
  60. quirk_scanner-4.10.0/quirk/discovery/nmap_parser.py +85 -0
  61. quirk_scanner-4.10.0/quirk/discovery/nmap_provider.py +138 -0
  62. quirk_scanner-4.10.0/quirk/engine/__init__.py +0 -0
  63. quirk_scanner-4.10.0/quirk/engine/cache.py +115 -0
  64. quirk_scanner-4.10.0/quirk/engine/findings_evaluator.py +924 -0
  65. quirk_scanner-4.10.0/quirk/engine/profiles.py +166 -0
  66. quirk_scanner-4.10.0/quirk/engine/rate_limiter.py +36 -0
  67. quirk_scanner-4.10.0/quirk/engine/risk_engine.py +10 -0
  68. quirk_scanner-4.10.0/quirk/errors.py +275 -0
  69. quirk_scanner-4.10.0/quirk/intelligence/__init__.py +29 -0
  70. quirk_scanner-4.10.0/quirk/intelligence/confidence.py +135 -0
  71. quirk_scanner-4.10.0/quirk/intelligence/evidence.py +432 -0
  72. quirk_scanner-4.10.0/quirk/intelligence/roadmap.py +468 -0
  73. quirk_scanner-4.10.0/quirk/intelligence/schema.py +115 -0
  74. quirk_scanner-4.10.0/quirk/intelligence/scoring.py +278 -0
  75. quirk_scanner-4.10.0/quirk/intelligence/trends.py +297 -0
  76. quirk_scanner-4.10.0/quirk/interactive.py +312 -0
  77. quirk_scanner-4.10.0/quirk/logging_util.py +44 -0
  78. quirk_scanner-4.10.0/quirk/models.py +243 -0
  79. quirk_scanner-4.10.0/quirk/qramm/__init__.py +7 -0
  80. quirk_scanner-4.10.0/quirk/qramm/compliance_map.py +126 -0
  81. quirk_scanner-4.10.0/quirk/qramm/evidence_bridge.py +261 -0
  82. quirk_scanner-4.10.0/quirk/qramm/model_meta.py +45 -0
  83. quirk_scanner-4.10.0/quirk/qramm/questions.py +208 -0
  84. quirk_scanner-4.10.0/quirk/qramm/scoring.py +94 -0
  85. quirk_scanner-4.10.0/quirk/reports/__init__.py +0 -0
  86. quirk_scanner-4.10.0/quirk/reports/_md_escape.py +34 -0
  87. quirk_scanner-4.10.0/quirk/reports/executive.py +286 -0
  88. quirk_scanner-4.10.0/quirk/reports/html_renderer.py +284 -0
  89. quirk_scanner-4.10.0/quirk/reports/technical.py +125 -0
  90. quirk_scanner-4.10.0/quirk/reports/templates/report.html.j2 +356 -0
  91. quirk_scanner-4.10.0/quirk/reports/writer.py +289 -0
  92. quirk_scanner-4.10.0/quirk/scanner/__init__.py +0 -0
  93. quirk_scanner-4.10.0/quirk/scanner/adcs_scanner.py +495 -0
  94. quirk_scanner-4.10.0/quirk/scanner/aws_connector.py +486 -0
  95. quirk_scanner-4.10.0/quirk/scanner/azure_connector.py +311 -0
  96. quirk_scanner-4.10.0/quirk/scanner/broker_scanner.py +812 -0
  97. quirk_scanner-4.10.0/quirk/scanner/container_scanner.py +142 -0
  98. quirk_scanner-4.10.0/quirk/scanner/db_connector.py +301 -0
  99. quirk_scanner-4.10.0/quirk/scanner/dnssec_scanner.py +365 -0
  100. quirk_scanner-4.10.0/quirk/scanner/email_scanner.py +607 -0
  101. quirk_scanner-4.10.0/quirk/scanner/fingerprint.py +206 -0
  102. quirk_scanner-4.10.0/quirk/scanner/gcp_connector.py +482 -0
  103. quirk_scanner-4.10.0/quirk/scanner/jwt_scanner.py +221 -0
  104. quirk_scanner-4.10.0/quirk/scanner/k8s_connector.py +573 -0
  105. quirk_scanner-4.10.0/quirk/scanner/kerberos_scanner.py +364 -0
  106. quirk_scanner-4.10.0/quirk/scanner/saml_scanner.py +503 -0
  107. quirk_scanner-4.10.0/quirk/scanner/smime_scanner.py +291 -0
  108. quirk_scanner-4.10.0/quirk/scanner/source_scanner.py +120 -0
  109. quirk_scanner-4.10.0/quirk/scanner/ssh_scanner.py +145 -0
  110. quirk_scanner-4.10.0/quirk/scanner/target_expander.py +71 -0
  111. quirk_scanner-4.10.0/quirk/scanner/tls_capabilities.py +246 -0
  112. quirk_scanner-4.10.0/quirk/scanner/tls_scanner.py +551 -0
  113. quirk_scanner-4.10.0/quirk/scanner/vault_connector.py +498 -0
  114. quirk_scanner-4.10.0/quirk/util/__init__.py +1 -0
  115. quirk_scanner-4.10.0/quirk/util/optional_extra.py +234 -0
  116. quirk_scanner-4.10.0/quirk/util/safe_exc.py +53 -0
  117. quirk_scanner-4.10.0/quirk/util/sanitize.py +80 -0
  118. quirk_scanner-4.10.0/quirk/util/subprocess_input.py +175 -0
  119. quirk_scanner-4.10.0/quirk/util/targets.py +316 -0
  120. quirk_scanner-4.10.0/quirk/util/url_allowlist.py +161 -0
  121. quirk_scanner-4.10.0/quirk/util/weak_crypto.py +102 -0
  122. quirk_scanner-4.10.0/quirk/validate.py +162 -0
  123. quirk_scanner-4.10.0/quirk_scanner.egg-info/PKG-INFO +75 -0
  124. quirk_scanner-4.10.0/quirk_scanner.egg-info/SOURCES.txt +332 -0
  125. quirk_scanner-4.10.0/quirk_scanner.egg-info/dependency_links.txt +1 -0
  126. quirk_scanner-4.10.0/quirk_scanner.egg-info/entry_points.txt +2 -0
  127. quirk_scanner-4.10.0/quirk_scanner.egg-info/requires.txt +80 -0
  128. quirk_scanner-4.10.0/quirk_scanner.egg-info/top_level.txt +2 -0
  129. quirk_scanner-4.10.0/run_scan.py +1823 -0
  130. quirk_scanner-4.10.0/setup.cfg +4 -0
  131. quirk_scanner-4.10.0/tests/test_adcs_ast_gate.py +166 -0
  132. quirk_scanner-4.10.0/tests/test_adcs_no_writes.py +144 -0
  133. quirk_scanner-4.10.0/tests/test_adcs_scanner.py +241 -0
  134. quirk_scanner-4.10.0/tests/test_api_auth.py +434 -0
  135. quirk_scanner-4.10.0/tests/test_api_qramm_hardening.py +208 -0
  136. quirk_scanner-4.10.0/tests/test_api_scan_window.py +234 -0
  137. quirk_scanner-4.10.0/tests/test_apply_security_cli_overrides.py +44 -0
  138. quirk_scanner-4.10.0/tests/test_audit_ledger_zero_open.py +74 -0
  139. quirk_scanner-4.10.0/tests/test_aws_connector.py +323 -0
  140. quirk_scanner-4.10.0/tests/test_azure_blob.py +181 -0
  141. quirk_scanner-4.10.0/tests/test_azure_keyvault.py +152 -0
  142. quirk_scanner-4.10.0/tests/test_banner_comment_corrected.py +28 -0
  143. quirk_scanner-4.10.0/tests/test_broker_config_and_profile.py +82 -0
  144. quirk_scanner-4.10.0/tests/test_broker_db_schema.py +57 -0
  145. quirk_scanner-4.10.0/tests/test_broker_run_integration.py +319 -0
  146. quirk_scanner-4.10.0/tests/test_broker_scanner_kafka.py +310 -0
  147. quirk_scanner-4.10.0/tests/test_broker_scanner_rabbitmq.py +405 -0
  148. quirk_scanner-4.10.0/tests/test_broker_scanner_redis.py +281 -0
  149. quirk_scanner-4.10.0/tests/test_cache.py +192 -0
  150. quirk_scanner-4.10.0/tests/test_cbom_builder.py +561 -0
  151. quirk_scanner-4.10.0/tests/test_cbom_builder_algo_hints.py +41 -0
  152. quirk_scanner-4.10.0/tests/test_cbom_builder_ssh_json_error.py +27 -0
  153. quirk_scanner-4.10.0/tests/test_cbom_classifier.py +259 -0
  154. quirk_scanner-4.10.0/tests/test_cbom_classifier_coverage.py +122 -0
  155. quirk_scanner-4.10.0/tests/test_cbom_coverage.py +120 -0
  156. quirk_scanner-4.10.0/tests/test_cbom_integration.py +190 -0
  157. quirk_scanner-4.10.0/tests/test_cbom_motion_endpoints.py +667 -0
  158. quirk_scanner-4.10.0/tests/test_cbom_motion_golden.py +428 -0
  159. quirk_scanner-4.10.0/tests/test_cbom_scan_route.py +83 -0
  160. quirk_scanner-4.10.0/tests/test_cbom_schema_validation.py +92 -0
  161. quirk_scanner-4.10.0/tests/test_cbom_skip_lists.py +84 -0
  162. quirk_scanner-4.10.0/tests/test_cbom_vault_consistency.py +73 -0
  163. quirk_scanner-4.10.0/tests/test_cbom_writer.py +167 -0
  164. quirk_scanner-4.10.0/tests/test_cbom_writer_validation.py +188 -0
  165. quirk_scanner-4.10.0/tests/test_cert_pubkey_fix.py +58 -0
  166. quirk_scanner-4.10.0/tests/test_chaos_lab_idempotency.py +129 -0
  167. quirk_scanner-4.10.0/tests/test_chaos_lab_image_pinning.py +43 -0
  168. quirk_scanner-4.10.0/tests/test_chaos_storage.py +90 -0
  169. quirk_scanner-4.10.0/tests/test_cli_correctness.py +227 -0
  170. quirk_scanner-4.10.0/tests/test_cli_init.py +160 -0
  171. quirk_scanner-4.10.0/tests/test_cli_version.py +19 -0
  172. quirk_scanner-4.10.0/tests/test_cloud_connectors.py +395 -0
  173. quirk_scanner-4.10.0/tests/test_cmvp_coverage_query.py +219 -0
  174. quirk_scanner-4.10.0/tests/test_cmvp_freshness.py +113 -0
  175. quirk_scanner-4.10.0/tests/test_cmvp_no_certified_true.py +259 -0
  176. quirk_scanner-4.10.0/tests/test_cmvp_refresh.py +309 -0
  177. quirk_scanner-4.10.0/tests/test_cmvp_report_column.py +264 -0
  178. quirk_scanner-4.10.0/tests/test_compliance_cli.py +53 -0
  179. quirk_scanner-4.10.0/tests/test_compliance_coverage_status.py +37 -0
  180. quirk_scanner-4.10.0/tests/test_compliance_freshness.py +26 -0
  181. quirk_scanner-4.10.0/tests/test_compliance_report_section.py +79 -0
  182. quirk_scanner-4.10.0/tests/test_compliance_schema.py +126 -0
  183. quirk_scanner-4.10.0/tests/test_compliance_status_staleness.py +118 -0
  184. quirk_scanner-4.10.0/tests/test_compliance_title_join.py +28 -0
  185. quirk_scanner-4.10.0/tests/test_container_scanner.py +74 -0
  186. quirk_scanner-4.10.0/tests/test_coverage_bounds.py +79 -0
  187. quirk_scanner-4.10.0/tests/test_credential_leakage.py +92 -0
  188. quirk_scanner-4.10.0/tests/test_dar_dashboard.py +130 -0
  189. quirk_scanner-4.10.0/tests/test_dar_k8s_scoring.py +139 -0
  190. quirk_scanner-4.10.0/tests/test_dar_storage_scoring.py +117 -0
  191. quirk_scanner-4.10.0/tests/test_dar_vault_scoring.py +87 -0
  192. quirk_scanner-4.10.0/tests/test_dashboard_api.py +134 -0
  193. quirk_scanner-4.10.0/tests/test_dashboard_scan_history.py +350 -0
  194. quirk_scanner-4.10.0/tests/test_dashboard_schemas_finding_category.py +23 -0
  195. quirk_scanner-4.10.0/tests/test_dashboard_theme.py +31 -0
  196. quirk_scanner-4.10.0/tests/test_dashboard_trends.py +352 -0
  197. quirk_scanner-4.10.0/tests/test_dashboard_wiring.py +152 -0
  198. quirk_scanner-4.10.0/tests/test_db_connector.py +409 -0
  199. quirk_scanner-4.10.0/tests/test_db_ensure_columns_generic.py +97 -0
  200. quirk_scanner-4.10.0/tests/test_db_migrate_cli.py +238 -0
  201. quirk_scanner-4.10.0/tests/test_db_migrations.py +138 -0
  202. quirk_scanner-4.10.0/tests/test_dnssec_scanner.py +560 -0
  203. quirk_scanner-4.10.0/tests/test_dnssec_scanner_reserved_algs.py +29 -0
  204. quirk_scanner-4.10.0/tests/test_doctor_actionable.py +124 -0
  205. quirk_scanner-4.10.0/tests/test_doctor_cmd.py +76 -0
  206. quirk_scanner-4.10.0/tests/test_email_findings.py +171 -0
  207. quirk_scanner-4.10.0/tests/test_email_run_scan_wiring.py +188 -0
  208. quirk_scanner-4.10.0/tests/test_email_scanner.py +614 -0
  209. quirk_scanner-4.10.0/tests/test_error_codes_freshness.py +45 -0
  210. quirk_scanner-4.10.0/tests/test_errors.py +92 -0
  211. quirk_scanner-4.10.0/tests/test_errors_cmd.py +99 -0
  212. quirk_scanner-4.10.0/tests/test_evidence_bridge_correctness.py +226 -0
  213. quirk_scanner-4.10.0/tests/test_evidence_coverage_gap.py +49 -0
  214. quirk_scanner-4.10.0/tests/test_evidence_protocol_keys.py +29 -0
  215. quirk_scanner-4.10.0/tests/test_executive_score_guard.py +48 -0
  216. quirk_scanner-4.10.0/tests/test_executive_truncation_indicator.py +57 -0
  217. quirk_scanner-4.10.0/tests/test_extras_concurrency_expander.py +152 -0
  218. quirk_scanner-4.10.0/tests/test_extras_install_matrix.py +165 -0
  219. quirk_scanner-4.10.0/tests/test_findings_evaluator_dedupe.py +82 -0
  220. quirk_scanner-4.10.0/tests/test_fingerprint_host_header.py +32 -0
  221. quirk_scanner-4.10.0/tests/test_fingerprint_socket_cleanup.py +143 -0
  222. quirk_scanner-4.10.0/tests/test_gap_closure.py +95 -0
  223. quirk_scanner-4.10.0/tests/test_gap_closure_packaging.py +37 -0
  224. quirk_scanner-4.10.0/tests/test_gcp_connector.py +194 -0
  225. quirk_scanner-4.10.0/tests/test_gcs_reuse.py +56 -0
  226. quirk_scanner-4.10.0/tests/test_html_renderer_coverage_gaps.py +126 -0
  227. quirk_scanner-4.10.0/tests/test_html_renderer_roadmap_section.py +49 -0
  228. quirk_scanner-4.10.0/tests/test_html_report.py +89 -0
  229. quirk_scanner-4.10.0/tests/test_hygiene.py +223 -0
  230. quirk_scanner-4.10.0/tests/test_identity_findings_accuracy.py +215 -0
  231. quirk_scanner-4.10.0/tests/test_identity_infra.py +302 -0
  232. quirk_scanner-4.10.0/tests/test_identity_scanner_hardening.py +166 -0
  233. quirk_scanner-4.10.0/tests/test_identity_surface.py +689 -0
  234. quirk_scanner-4.10.0/tests/test_infra03_nyquist_coverage.py +393 -0
  235. quirk_scanner-4.10.0/tests/test_init_db_idempotent.py +86 -0
  236. quirk_scanner-4.10.0/tests/test_install_all_excludes_impacket.py +119 -0
  237. quirk_scanner-4.10.0/tests/test_install_errors.py +124 -0
  238. quirk_scanner-4.10.0/tests/test_intelligence_confidence.py +155 -0
  239. quirk_scanner-4.10.0/tests/test_intelligence_evidence.py +168 -0
  240. quirk_scanner-4.10.0/tests/test_intelligence_public_api.py +84 -0
  241. quirk_scanner-4.10.0/tests/test_intelligence_roadmap.py +137 -0
  242. quirk_scanner-4.10.0/tests/test_intelligence_schema.py +73 -0
  243. quirk_scanner-4.10.0/tests/test_intelligence_scoring.py +148 -0
  244. quirk_scanner-4.10.0/tests/test_intelligence_trends.py +228 -0
  245. quirk_scanner-4.10.0/tests/test_interactive_mode.py +534 -0
  246. quirk_scanner-4.10.0/tests/test_interactive_validate_routes.py +179 -0
  247. quirk_scanner-4.10.0/tests/test_job_progress.py +49 -0
  248. quirk_scanner-4.10.0/tests/test_jobs_api.py +335 -0
  249. quirk_scanner-4.10.0/tests/test_jwt_scanner.py +106 -0
  250. quirk_scanner-4.10.0/tests/test_k8s_connector.py +663 -0
  251. quirk_scanner-4.10.0/tests/test_kerberos_scanner.py +424 -0
  252. quirk_scanner-4.10.0/tests/test_kerberos_scanner_realm_ipaddress.py +46 -0
  253. quirk_scanner-4.10.0/tests/test_md_cell_escape.py +90 -0
  254. quirk_scanner-4.10.0/tests/test_migration_advisor_precision.py +84 -0
  255. quirk_scanner-4.10.0/tests/test_motion_scoring.py +221 -0
  256. quirk_scanner-4.10.0/tests/test_nmap_hardening.py +135 -0
  257. quirk_scanner-4.10.0/tests/test_nmap_provider.py +21 -0
  258. quirk_scanner-4.10.0/tests/test_operator_context_years_clamp.py +114 -0
  259. quirk_scanner-4.10.0/tests/test_optional_extra.py +409 -0
  260. quirk_scanner-4.10.0/tests/test_packaging.py +95 -0
  261. quirk_scanner-4.10.0/tests/test_pdf_export.py +32 -0
  262. quirk_scanner-4.10.0/tests/test_pdf_metadata_constants.py +109 -0
  263. quirk_scanner-4.10.0/tests/test_pdf_render_hardening.py +181 -0
  264. quirk_scanner-4.10.0/tests/test_phase50_docs_presence.py +57 -0
  265. quirk_scanner-4.10.0/tests/test_platform_version_single_source.py +70 -0
  266. quirk_scanner-4.10.0/tests/test_pqc_terminology_gate.py +47 -0
  267. quirk_scanner-4.10.0/tests/test_profiles.py +123 -0
  268. quirk_scanner-4.10.0/tests/test_qramm_answer.py +87 -0
  269. quirk_scanner-4.10.0/tests/test_qramm_compliance_map.py +188 -0
  270. quirk_scanner-4.10.0/tests/test_qramm_delete_session_fk.py +130 -0
  271. quirk_scanner-4.10.0/tests/test_qramm_evidence_bridge.py +311 -0
  272. quirk_scanner-4.10.0/tests/test_qramm_model_stale.py +46 -0
  273. quirk_scanner-4.10.0/tests/test_qramm_models.py +321 -0
  274. quirk_scanner-4.10.0/tests/test_qramm_multiplier.py +56 -0
  275. quirk_scanner-4.10.0/tests/test_qramm_multiplier_constants.py +71 -0
  276. quirk_scanner-4.10.0/tests/test_qramm_practice_scoring.py +94 -0
  277. quirk_scanner-4.10.0/tests/test_qramm_questions.py +83 -0
  278. quirk_scanner-4.10.0/tests/test_qramm_router.py +531 -0
  279. quirk_scanner-4.10.0/tests/test_qramm_routes_typed_response.py +46 -0
  280. quirk_scanner-4.10.0/tests/test_qramm_scoring.py +136 -0
  281. quirk_scanner-4.10.0/tests/test_qramm_staleness.py +126 -0
  282. quirk_scanner-4.10.0/tests/test_rate_limiter.py +69 -0
  283. quirk_scanner-4.10.0/tests/test_report_injection_hardening.py +268 -0
  284. quirk_scanner-4.10.0/tests/test_report_sanitization.py +170 -0
  285. quirk_scanner-4.10.0/tests/test_reports_writer.py +254 -0
  286. quirk_scanner-4.10.0/tests/test_rich_output.py +25 -0
  287. quirk_scanner-4.10.0/tests/test_risk_engine.py +475 -0
  288. quirk_scanner-4.10.0/tests/test_risk_engine_cert_defects.py +227 -0
  289. quirk_scanner-4.10.0/tests/test_risk_engine_coverage_gap.py +90 -0
  290. quirk_scanner-4.10.0/tests/test_roadmap_baseline_governance.py +63 -0
  291. quirk_scanner-4.10.0/tests/test_run_scan_budget_guard.py +117 -0
  292. quirk_scanner-4.10.0/tests/test_run_scan_init_db_scope.py +90 -0
  293. quirk_scanner-4.10.0/tests/test_run_scan_targets_file.py +109 -0
  294. quirk_scanner-4.10.0/tests/test_s3_encryption.py +190 -0
  295. quirk_scanner-4.10.0/tests/test_safe_exc.py +58 -0
  296. quirk_scanner-4.10.0/tests/test_safe_filter_audit.py +323 -0
  297. quirk_scanner-4.10.0/tests/test_saml_scanner.py +421 -0
  298. quirk_scanner-4.10.0/tests/test_saml_scanner_sha1_word_boundary.py +26 -0
  299. quirk_scanner-4.10.0/tests/test_sanitize_scanner_text.py +98 -0
  300. quirk_scanner-4.10.0/tests/test_scan_error_gate.py +286 -0
  301. quirk_scanner-4.10.0/tests/test_scan_robustness.py +140 -0
  302. quirk_scanner-4.10.0/tests/test_schedule_cmd.py +153 -0
  303. quirk_scanner-4.10.0/tests/test_scheduler_cmd.py +318 -0
  304. quirk_scanner-4.10.0/tests/test_schedules_api.py +307 -0
  305. quirk_scanner-4.10.0/tests/test_score_clamp_property.py +80 -0
  306. quirk_scanner-4.10.0/tests/test_score_weights_invariant.py +30 -0
  307. quirk_scanner-4.10.0/tests/test_scoring_consolidation.py +214 -0
  308. quirk_scanner-4.10.0/tests/test_scoring_correctness.py +198 -0
  309. quirk_scanner-4.10.0/tests/test_security_config.py +139 -0
  310. quirk_scanner-4.10.0/tests/test_skip_registry.py +116 -0
  311. quirk_scanner-4.10.0/tests/test_smime_ast_gate.py +120 -0
  312. quirk_scanner-4.10.0/tests/test_smime_no_envelope_leak.py +124 -0
  313. quirk_scanner-4.10.0/tests/test_smime_scanner.py +141 -0
  314. quirk_scanner-4.10.0/tests/test_source_scanner.py +105 -0
  315. quirk_scanner-4.10.0/tests/test_ssh_scanner.py +327 -0
  316. quirk_scanner-4.10.0/tests/test_sslyze_integration.py +494 -0
  317. quirk_scanner-4.10.0/tests/test_subprocess_logging.py +93 -0
  318. quirk_scanner-4.10.0/tests/test_targets_parser.py +288 -0
  319. quirk_scanner-4.10.0/tests/test_timeouts_config.py +111 -0
  320. quirk_scanner-4.10.0/tests/test_tls_capabilities_comment.py +27 -0
  321. quirk_scanner-4.10.0/tests/test_tls_kex_label.py +48 -0
  322. quirk_scanner-4.10.0/tests/test_tls_scanner_chain_verified.py +321 -0
  323. quirk_scanner-4.10.0/tests/test_tls_scanner_resource_cleanup.py +127 -0
  324. quirk_scanner-4.10.0/tests/test_trends_subsecond_sessions.py +113 -0
  325. quirk_scanner-4.10.0/tests/test_trends_yield_per.py +24 -0
  326. quirk_scanner-4.10.0/tests/test_uat_db_integration.py +96 -0
  327. quirk_scanner-4.10.0/tests/test_v41_gap_closure.py +70 -0
  328. quirk_scanner-4.10.0/tests/test_validate.py +82 -0
  329. quirk_scanner-4.10.0/tests/test_vault_connector.py +557 -0
  330. quirk_scanner-4.10.0/tests/test_version.py +72 -0
  331. quirk_scanner-4.10.0/tests/test_weak_crypto_helper.py +64 -0
  332. quirk_scanner-4.10.0/tests/test_weak_crypto_pfs_weak_helpers.py +70 -0
  333. quirk_scanner-4.10.0/tests/test_writer.py +54 -0
  334. quirk_scanner-4.10.0/tests/test_writer_hosts_count_filters_falsy.py +36 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Digs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: quirk-scanner
3
+ Version: 4.10.0
4
+ Summary: QU.I.R.K. -- Quantum Infrastructure Readiness Kit
5
+ License: Proprietary
6
+ Requires-Python: >=3.10
7
+ License-File: LICENSE
8
+ Requires-Dist: PyYAML>=6.0
9
+ Requires-Dist: cryptography>=44.0
10
+ Requires-Dist: SQLAlchemy>=2.0
11
+ Requires-Dist: tqdm>=4.67
12
+ Requires-Dist: cyclonedx-python-lib[json-validation]<12,>=11.7.0
13
+ Requires-Dist: httpx>=0.28.0
14
+ Requires-Dist: jinja2>=3.1.0
15
+ Requires-Dist: rich>=13.0.0
16
+ Requires-Dist: PyJWT>=2.12.0
17
+ Requires-Dist: python-jose>=3.5.0
18
+ Requires-Dist: boto3>=1.42.0
19
+ Requires-Dist: azure-identity>=1.25.0
20
+ Requires-Dist: azure-keyvault-certificates>=4.10.0
21
+ Requires-Dist: azure-keyvault-keys>=4.11.0
22
+ Requires-Dist: azure-mgmt-network>=30.2.0
23
+ Requires-Dist: dnspython[dnssec]>=2.8.0
24
+ Requires-Dist: lxml>=6.0
25
+ Requires-Dist: beautifulsoup4>=4.13.0
26
+ Requires-Dist: defusedxml>=0.7.1
27
+ Requires-Dist: signxml>=4.4.0
28
+ Requires-Dist: nh3>=0.2.17
29
+ Provides-Extra: dashboard
30
+ Requires-Dist: fastapi>=0.128.8; extra == "dashboard"
31
+ Requires-Dist: uvicorn[standard]>=0.39.0; extra == "dashboard"
32
+ Requires-Dist: python-multipart>=0.0.20; extra == "dashboard"
33
+ Requires-Dist: playwright>=1.58.0; extra == "dashboard"
34
+ Requires-Dist: croniter>=1.4.0; extra == "dashboard"
35
+ Requires-Dist: pypdf>=4.0; extra == "dashboard"
36
+ Provides-Extra: identity
37
+ Requires-Dist: impacket<0.14,>=0.13.0; extra == "identity"
38
+ Requires-Dist: ldap3>=2.9.1; extra == "identity"
39
+ Provides-Extra: adcs
40
+ Requires-Dist: ldap3>=2.9.1; extra == "adcs"
41
+ Provides-Extra: cloud
42
+ Requires-Dist: google-api-python-client>=2.0.0; extra == "cloud"
43
+ Requires-Dist: google-auth>=2.36.0; extra == "cloud"
44
+ Requires-Dist: azure-mgmt-storage>=21.0.0; extra == "cloud"
45
+ Requires-Dist: kubernetes>=35.0.0; extra == "cloud"
46
+ Requires-Dist: google-cloud-container>=2.0.0; extra == "cloud"
47
+ Requires-Dist: azure-mgmt-containerservice>=35.0.0; extra == "cloud"
48
+ Requires-Dist: hvac>=2.4.0; extra == "cloud"
49
+ Provides-Extra: db
50
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == "db"
51
+ Requires-Dist: PyMySQL>=1.1.0; extra == "db"
52
+ Provides-Extra: motion
53
+ Requires-Dist: quirk-scanner[email]; extra == "motion"
54
+ Requires-Dist: quirk-scanner[broker]; extra == "motion"
55
+ Requires-Dist: quirk-scanner[kafka]; extra == "motion"
56
+ Provides-Extra: email
57
+ Provides-Extra: broker
58
+ Requires-Dist: redis>=5.0; extra == "broker"
59
+ Provides-Extra: kafka
60
+ Requires-Dist: kafka-python>=2.0; extra == "kafka"
61
+ Provides-Extra: redis
62
+ Requires-Dist: redis>=5.0; extra == "redis"
63
+ Provides-Extra: cbom
64
+ Requires-Dist: cyclonedx-python-lib[json-validation]<12,>=11.7.0; extra == "cbom"
65
+ Provides-Extra: dev
66
+ Requires-Dist: towncrier>=24.7.0; extra == "dev"
67
+ Provides-Extra: all
68
+ Requires-Dist: quirk-scanner[cloud]; extra == "all"
69
+ Requires-Dist: quirk-scanner[cbom]; extra == "all"
70
+ Requires-Dist: quirk-scanner[db]; extra == "all"
71
+ Requires-Dist: quirk-scanner[motion]; extra == "all"
72
+ Requires-Dist: quirk-scanner[redis]; extra == "all"
73
+ Requires-Dist: quirk-scanner[dashboard]; extra == "all"
74
+ Requires-Dist: quirk-scanner[adcs]; extra == "all"
75
+ Dynamic: license-file
@@ -0,0 +1,110 @@
1
+ [![Python Staleness Gate](https://img.shields.io/github/actions/workflow/status/0xD1g5/QU.I.R.K./python-staleness.yml?branch=main&label=CI)](https://github.com/0xD1g5/QU.I.R.K./actions/workflows/python-staleness.yml)
2
+ [![PyPI version](https://img.shields.io/pypi/v/quirk-scanner.svg)](https://pypi.org/project/quirk-scanner/)
3
+ [![License: MIT](https://img.shields.io/github/license/0xD1g5/QU.I.R.K.)](LICENSE)
4
+ [![Sigstore attested](https://img.shields.io/badge/sigstore-attested-blue)](docs/release-process.md#attestation-verification)
5
+ [![Security Policy](https://img.shields.io/badge/security-policy-blue)](SECURITY.md)
6
+
7
+ # QU.I.R.K. — v4.10.0
8
+
9
+ **Quantum Infrastructure Readiness Kit** — consulting-grade cryptographic inventory and quantum-readiness assessment.
10
+
11
+ QU.I.R.K. is an agentless scanner that discovers crypto material across TLS endpoints, SSH services, JWT-issuing APIs, container images, Git repositories, AWS cloud resources, and Azure cloud resources. It produces a Cryptography Bill of Materials (CBOM) in CycloneDX JSON and XML, computes a quantum-readiness score (0–100), and generates a professional PDF report a consultant can hand directly to a client.
12
+
13
+ ## For your role
14
+
15
+ **For the security consultant.** QU.I.R.K. produces the deliverable you bill for: a CycloneDX CBOM, a 0–100 quantum-readiness score with four subscores (Hygiene, Modern TLS, Identity, Agility), and a client-ready PDF report. Point it at a client's TLS endpoints, SSH services, JWT-issuing APIs, and cloud accounts; hand back the findings and the prioritized remediation roadmap. No agents to deploy, no software for the client to install.
16
+
17
+ **For the IT generalist.** Start with the simple question — *what crypto do we even have running?* — and end with an answerable inventory. QU.I.R.K. walks your environment, names every TLS endpoint, SSH host, container image, and KMS key it can reach, and tells you which ones are quantum-vulnerable. The dashboard at `http://localhost:8512` lets you browse the findings interactively before you commit to any remediation work.
18
+
19
+ **For the compliance officer.** Quantum-readiness is on the audit radar (NIST PQC, CNSA 2.0, FIPS 140-3 transitions). QU.I.R.K. ships compliance mappings against CMVP / FIPS 140-3 with documented staleness review cadence, surfaces algorithm classifications that map to those frameworks, and produces artifact-grade output (CBOM JSON/XML, PDF reports) you can attach to an audit response.
20
+
21
+ ![QU.I.R.K. dashboard against the chaos lab](docs/images/dashboard-hero.png)
22
+ *Dashboard view of a scan against the chaos lab — quantum-readiness score, subscores, findings, and CBOM browser.*
23
+
24
+ <!-- TODO(LAUNCH-01): replace docs/images/dashboard-hero.png with a real screenshot captured against a running dashboard. The current file is a placeholder (1×1 transparent PNG) per Phase 85-05 deviation; capture a real screenshot post-merge from a live macOS arm64 run against the chaos lab `phaseA` (tls-weak) profile. -->
25
+
26
+ ## Quick Start
27
+
28
+ Three commands to a working scan:
29
+
30
+ ```bash
31
+ pip install quirk-scanner[all]
32
+ quirk init
33
+ quirk --config config.yaml
34
+ ```
35
+
36
+ Watch a 60-second run: `<asciinema-link-here>` *(recording is a manual post-merge task — see `.planning/phases/85-public-launch-polish/85-05-SUMMARY.md`)*.
37
+
38
+ Then follow the [Getting Started guide](docs/getting-started.md) for a walkthrough of the 3-step quickstart with explanations of each command.
39
+
40
+ ## Documentation
41
+
42
+ | Guide | Description |
43
+ |-------|-------------|
44
+ | [Getting Started](docs/getting-started.md) | Zero to first scan in under 10 minutes |
45
+ | [Installation](docs/installation.md) | System requirements, macOS, Linux, Windows WSL |
46
+ | [Configuration Reference](docs/configuration.md) | All config.yaml options and CLI flags |
47
+ | [Connector Guides](docs/connectors/) | AWS, Azure, Docker, Git setup with credential templates |
48
+ | [Report Interpretation](docs/report-interpretation.md) | What every score and finding means, client conversation guide |
49
+ | [CBOM Guide](docs/cbom-guide.md) | What a CBOM is and how to cite it as compliance evidence |
50
+ | [Chaos Lab Operator Guide](docs/chaos-lab.md) | Lab profiles, port matrix, expected findings |
51
+ | [Intelligence Schema](docs/intelligence-schema.md) | `intelligence-*.json` output format reference |
52
+ | [Upgrade Guide](docs/upgrade-guide.md) | v4.x → v4.10 upgrade procedure with `quirk db migrate` |
53
+ | [Release Process](docs/release-process.md) | PyPI / GHCR / Homebrew tap publish procedure + Sigstore attestation verification |
54
+ | [UAT Test Series](docs/UAT-SERIES.md) | Full user acceptance testing guide — CLI, lab, dashboard |
55
+
56
+ ## What QU.I.R.K. Scans
57
+
58
+ - **TLS/HTTPS endpoints** — certificate metadata, cipher suites, TLS version, chain trust
59
+ - **SSH services** — host key algorithms, KEX algorithms, MAC algorithms, cipher suites
60
+ - **JWT-issuing APIs** — algorithm discovery via JWKS and OIDC endpoints
61
+ - **Docker container images** — crypto libraries detected via Syft SBOM analysis
62
+ - **Git repositories / source code** — cryptographic API usage via Semgrep analysis
63
+ - **AWS** — ACM certificates, KMS key specs, CloudFront distributions, ELBv2 listeners
64
+ - **Azure** — Key Vault keys and certificates, Application Gateway TLS policies
65
+
66
+ ## Output Artifacts
67
+
68
+ - **Quantum-readiness score** (0–100) — overall score with four subscores: Hygiene, Modern TLS, Identity, Agility
69
+ - **CBOM** in CycloneDX JSON + XML — inventory of all discovered cryptographic components
70
+ - **Web dashboard** at `http://localhost:8512` — interactive findings browser and CBOM graph
71
+ - **PDF report** — client-ready export from the dashboard
72
+
73
+ Sample CBOM fixtures live in [`examples/cbom/`](examples/) — one per major scan profile (TLS-only, identity, data-at-rest, data-in-motion), deterministic and committed to the repo.
74
+
75
+ ## What's New in v4.3
76
+
77
+ - **GCP Connector (Phase 26)** — Cloud KMS key classification (47-entry algorithm map including PQC), Cloud SQL TLS enforcement, and GCS CMEK detection.
78
+ - **Database Encryption Detection (Phase 27)** — PostgreSQL, MySQL, and RDS encryption posture surfaced via a new `data_at_rest` subscore.
79
+ - **Object Storage Audit (Phase 28)** — S3 SSE-S3/SSE-KMS/CMK, Azure Blob CMK/platform-managed, GCS CMEK using zero duplicate API calls.
80
+ - **Kubernetes Secrets Inspection (Phase 29)** — EKS/GKE/AKS managed cluster encryption APIs; RBAC-403 graceful degradation.
81
+ - **HashiCorp Vault Connector (Phase 30)** — Transit key type classification (including ml-dsa/slh-dsa PQC positive), PKI mount CA cert auditing, and auth method risk tiering.
82
+ - **Trend Analysis (v4.3, Phase 31)** — `quirk/intelligence/trends.py` produces a session-over-session score delta plus per-severity new/resolved finding counts between the two most recent scan sessions. Surfaced via `GET /api/trends` and a new dashboard `/trends` tab. No new SQLite table — uses existing scanned_at grouping.
83
+
84
+ ## Install From Other Channels
85
+
86
+ - **PyPI (recommended):** `pip install quirk-scanner[all]` — see Quick Start above. The release is signed and attestation-verified via Sigstore + PyPI Trusted Publishers (`gh attestation verify`).
87
+ - **Homebrew (macOS):** `brew install 0xD1g5/quirk/quirk` — installs into an isolated `pipx`-style venv under `libexec`. *(Tap bootstrap is a manual post-release task; becomes functional once the `0xD1g5/homebrew-quirk` tap repo is published with the first signed sdist sha256.)* See [Homebrew Tap](docs/release-process.md#homebrew-tap-launch-02) for the bootstrap procedure.
88
+ - **Docker (GHCR, multi-arch):** `docker run ghcr.io/0xd1g5/quirk:latest --help` — `linux/amd64` + `linux/arm64`. See [Container Image](docs/release-process.md#container-image-launch-03).
89
+
90
+ > **No `curl | bash` installer.** This is a deliberate non-feature, not an oversight — see [`docs/release-process.md` → `curl | bash` Non-Decision](docs/release-process.md). Piping HTTP to a shell defeats the integrity guarantees of Sigstore attestations and PyPI Trusted Publishers; install via pip / brew / docker only.
91
+
92
+ <details>
93
+ <summary>Develop from source</summary>
94
+
95
+ ```bash
96
+ git clone https://github.com/0xD1g5/QU.I.R.K.
97
+ cd QU.I.R.K.
98
+ python -m venv .venv && source .venv/bin/activate
99
+ pip install -e '.[dashboard]'
100
+ playwright install chromium
101
+ quirk --help
102
+ ```
103
+
104
+ Editable install is for contributors — end users should prefer the PyPI / Homebrew / GHCR paths above.
105
+
106
+ </details>
107
+
108
+ ## License
109
+
110
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,149 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "quirk-scanner"
7
+ version = "4.10.0"
8
+ description = "QU.I.R.K. -- Quantum Infrastructure Readiness Kit"
9
+ requires-python = ">=3.10"
10
+ license = {text = "Proprietary"}
11
+ dependencies = [
12
+ "PyYAML>=6.0",
13
+ "cryptography>=44.0",
14
+ "SQLAlchemy>=2.0",
15
+ "tqdm>=4.67",
16
+ "cyclonedx-python-lib[json-validation]>=11.7.0,<12",
17
+ "httpx>=0.28.0",
18
+ "jinja2>=3.1.0",
19
+ "rich>=13.0.0",
20
+ "PyJWT>=2.12.0",
21
+ "python-jose>=3.5.0",
22
+ "boto3>=1.42.0",
23
+ "azure-identity>=1.25.0",
24
+ "azure-keyvault-certificates>=4.10.0",
25
+ "azure-keyvault-keys>=4.11.0",
26
+ "azure-mgmt-network>=30.2.0",
27
+ "dnspython[dnssec]>=2.8.0",
28
+ "lxml>=6.0",
29
+ "beautifulsoup4>=4.13.0",
30
+ "defusedxml>=0.7.1",
31
+ "signxml>=4.4.0",
32
+ "nh3>=0.2.17",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ dashboard = [
37
+ "fastapi>=0.128.8",
38
+ "uvicorn[standard]>=0.39.0",
39
+ "python-multipart>=0.0.20",
40
+ "playwright>=1.58.0",
41
+ "croniter>=1.4.0",
42
+ "pypdf>=4.0", # Phase 78 / HARDEN-04: PDF metadata verification (test-only)
43
+ ]
44
+ identity = [
45
+ "impacket>=0.13.0,<0.14",
46
+ "ldap3>=2.9.1",
47
+ ]
48
+ # Phase 80 ADCS-07: dedicated extras group for the AD CS scanner. Lists ONLY
49
+ # ldap3 (NOT impacket) so that quirk[adcs] can safely be included in [all]
50
+ # without re-introducing the pyOpenSSL/cryptography downgrade chain that
51
+ # quirk[identity] would. tests/test_install_all_excludes_impacket.py guards
52
+ # this invariant. cryptography is already a core dep (>=44.0); Plan 80-04
53
+ # adds a CI matrix asserting the floor across [adcs] + [all] combinations.
54
+ adcs = [
55
+ "ldap3>=2.9.1",
56
+ ]
57
+ cloud = [
58
+ "google-api-python-client>=2.0.0",
59
+ "google-auth>=2.36.0",
60
+ "azure-mgmt-storage>=21.0.0", # Phase 28: Azure Blob encryption audit (STOR-02)
61
+ "kubernetes>=35.0.0", # Phase 29: Kubernetes secrets inspection (K8S-01, K8S-02)
62
+ "google-cloud-container>=2.0.0", # Phase 29: GKE databaseEncryption.state (K8S-01)
63
+ "azure-mgmt-containerservice>=35.0.0", # Phase 29: AKS Key Vault KMS detection (K8S-01)
64
+ "hvac>=2.4.0", # Phase 30: HashiCorp Vault connector (VAULT-01/02/03)
65
+ ]
66
+ db = [
67
+ "psycopg2-binary>=2.9.0",
68
+ "PyMySQL>=1.1.0",
69
+ ]
70
+ motion = ["quirk-scanner[email]", "quirk-scanner[broker]", "quirk-scanner[kafka]"]
71
+ email = []
72
+ broker = ["redis>=5.0"]
73
+ kafka = ["kafka-python>=2.0"]
74
+ redis = ["redis>=5.0"]
75
+ cbom = ["cyclonedx-python-lib[json-validation]>=11.7.0,<12"]
76
+ dev = [
77
+ "towncrier>=24.7.0",
78
+ ]
79
+ all = [
80
+ "quirk-scanner[cloud]",
81
+ "quirk-scanner[cbom]",
82
+ "quirk-scanner[db]",
83
+ "quirk-scanner[motion]",
84
+ "quirk-scanner[redis]",
85
+ "quirk-scanner[dashboard]",
86
+ "quirk-scanner[adcs]", # Phase 80 ADCS-07 — ldap3-only, no impacket
87
+ ]
88
+ # NOTE: quirk[identity] is INTENTIONALLY EXCLUDED from [all] -- impacket
89
+ # transitively pulls pyOpenSSL which downgrades cryptography and breaks the
90
+ # TLS scanner. See Phase 45 / D-01. tests/test_install_all_excludes_impacket.py
91
+ # guards this; do NOT add quirk[identity] to the list above.
92
+
93
+ [project.scripts]
94
+ quirk = "run_scan:_run_main_with_job_guard"
95
+
96
+ [tool.setuptools.packages.find]
97
+ include = ["quirk*"]
98
+
99
+ [tool.setuptools]
100
+ py-modules = ["run_scan"]
101
+
102
+ [tool.setuptools.package-data]
103
+ quirk = ["reports/templates/*.j2", "config_template.yaml", "dashboard/static/**/*"]
104
+
105
+ [tool.pytest.ini_options]
106
+ markers = [
107
+ "slow: marks tests as slow (deselect with '-m not slow')",
108
+ "live_infra: marks tests requiring live external infrastructure (Docker/cloud/etc)",
109
+ ]
110
+ addopts = "-m 'not slow'"
111
+ testpaths = ["tests"]
112
+ pythonpath = ["."]
113
+
114
+ # RELENG-04 (Phase 84-02): towncrier CHANGELOG automation.
115
+ # Per-PR fragments live under changelog.d/<id>.<kind>.md. At release time:
116
+ # towncrier build --version X.Y.Z --yes
117
+ # consumes fragments and prepends a rendered section above the
118
+ # `<!-- towncrier release notes start -->` marker in CHANGELOG.md.
119
+ [tool.towncrier]
120
+ package = "quirk"
121
+ directory = "changelog.d"
122
+ filename = "CHANGELOG.md"
123
+ title_format = "## [{version}] - {project_date}"
124
+ start_string = "<!-- towncrier release notes start -->\n"
125
+
126
+ [[tool.towncrier.type]]
127
+ directory = "feature"
128
+ name = "Added"
129
+ showcontent = true
130
+
131
+ [[tool.towncrier.type]]
132
+ directory = "bugfix"
133
+ name = "Fixed"
134
+ showcontent = true
135
+
136
+ [[tool.towncrier.type]]
137
+ directory = "doc"
138
+ name = "Documentation"
139
+ showcontent = true
140
+
141
+ [[tool.towncrier.type]]
142
+ directory = "removal"
143
+ name = "Removed"
144
+ showcontent = true
145
+
146
+ [[tool.towncrier.type]]
147
+ directory = "misc"
148
+ name = "Misc"
149
+ showcontent = false
@@ -0,0 +1,29 @@
1
+ """QU.I.R.K. -- Quantum Infrastructure Readiness Kit
2
+
3
+ Version resolution (D-84-R1 / v4.10 D-02):
4
+ ``pyproject.toml [project.version]`` is the canonical source of truth. The
5
+ ``__version__`` attribute below derives from installed package metadata via
6
+ ``importlib.metadata.version`` so there is no second literal to keep in sync.
7
+
8
+ For unpackaged dev runs (fresh checkout, never ``pip install -e .``-d), the
9
+ ``PackageNotFoundError`` fallback parses ``pyproject.toml`` directly via
10
+ ``tomllib`` so importing the package never fails on a bare clone.
11
+ """
12
+ from importlib.metadata import PackageNotFoundError, version as _pkg_version
13
+
14
+ _DIST_NAME = "quirk-scanner"
15
+
16
+ try:
17
+ __version__ = _pkg_version(_DIST_NAME)
18
+ except PackageNotFoundError:
19
+ # Unpackaged dev run — parse pyproject.toml at the repo root.
20
+ import tomllib
21
+ from pathlib import Path
22
+
23
+ _pyproject_path = Path(__file__).resolve().parent.parent / "pyproject.toml"
24
+ try:
25
+ _pyproject = tomllib.loads(_pyproject_path.read_text(encoding="utf-8"))
26
+ __version__ = _pyproject["project"]["version"]
27
+ except (OSError, KeyError, tomllib.TOMLDecodeError):
28
+ # Last-resort sentinel — keeps imports safe even in pathological envs.
29
+ __version__ = "0.0.0+unknown"
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from typing import Dict, Final, FrozenSet, List
5
+
6
+
7
+ # Phase 74-03 D-08 (WR-09): canonical algorithm synonym map + word-boundary
8
+ # matcher. Replaces substring matching that produced false positives like
9
+ # `'DES' in 'DESede'` and `'DES' in 'libdes3.so'`. Word-boundaries (`\b`)
10
+ # eliminate the substring-inside-identifier class. Also consumed by
11
+ # `quirk/qramm/evidence_bridge.py::_walk_json_for_alg_strings` (D-09).
12
+ CANONICAL_ALG_SYNONYMS: Final[Dict[str, FrozenSet[str]]] = {
13
+ "DES": frozenset({"DES", "DES-EDE", "DES-CBC"}),
14
+ "3DES": frozenset({"3DES", "TripleDES", "DES-EDE3"}),
15
+ "RC4": frozenset({"RC4", "ARCFOUR"}),
16
+ "MD5": frozenset({"MD5"}),
17
+ "SHA1": frozenset({"SHA1", "SHA-1"}),
18
+ }
19
+
20
+
21
+ def _matches(canonical: str, text: str) -> bool:
22
+ """Word-boundary regex match for ``canonical`` (or any of its synonyms) in ``text``.
23
+
24
+ Case-insensitive. Returns False when the canonical token appears only as a
25
+ substring inside a larger identifier (e.g. ``DESede``, ``libdes3.so``).
26
+ """
27
+ variants = CANONICAL_ALG_SYNONYMS.get(canonical, frozenset({canonical}))
28
+ pattern = r"\b(" + "|".join(re.escape(v) for v in variants) + r")\b"
29
+ return bool(re.search(pattern, text, re.IGNORECASE))
30
+
31
+
32
+ def recommend_migration_paths(findings: List[Dict]) -> List[Dict]:
33
+ """
34
+ Convert raw findings into recommended migration paths.
35
+ This is a rules-based v3.5 layer; later becomes richer with
36
+ cipher enumeration, chain analysis, cloud/PKI connectors.
37
+ """
38
+ recs: List[Dict] = []
39
+
40
+ for f in findings:
41
+ title = (f.get("title") or "").lower()
42
+ sev = f.get("severity")
43
+ host = f.get("host")
44
+ port = f.get("port")
45
+
46
+ if sev == "INFO":
47
+ continue
48
+
49
+ # Legacy TLS — Phase 74 D-08: word-boundary match on title token
50
+ if re.search(r"\blegacy tls\b", title):
51
+ recs.append({
52
+ "host": host,
53
+ "port": port,
54
+ "severity": sev,
55
+ "path": "Hygiene → Modernization",
56
+ "recommendation": "Upgrade to TLS 1.2+ immediately; prefer TLS 1.3. Standardize termination configs and block legacy versions at gateways/LBs.",
57
+ })
58
+ continue
59
+
60
+ # Plaintext HTTP — Phase 74 D-08: word-boundary
61
+ if re.search(r"\bplaintext http\b", title):
62
+ recs.append({
63
+ "host": host,
64
+ "port": port,
65
+ "severity": sev,
66
+ "path": "Hygiene",
67
+ "recommendation": "Migrate HTTP→HTTPS. If legacy, front with TLS-terminating reverse proxy and enforce redirects + HSTS where applicable.",
68
+ })
69
+ continue
70
+
71
+ # Quantum transition — Phase 74 D-08: word-boundary
72
+ if re.search(r"\bquantum\b", title):
73
+ recs.append({
74
+ "host": host,
75
+ "port": port,
76
+ "severity": sev,
77
+ "path": "Modernization → PQC Preparation",
78
+ "recommendation": "Stabilize on modern classical crypto baselines (TLS 1.3, standardized configs) and prepare for hybrid/PQC vendor upgrades. Prioritize long-lived sensitive data flows and trust anchors.",
79
+ })
80
+ continue
81
+
82
+ # SSH planning — Phase 74 D-08: word-boundary closes `sshfp` false positive
83
+ if re.search(r"\bssh\b", title):
84
+ recs.append({
85
+ "host": host,
86
+ "port": port,
87
+ "severity": sev,
88
+ "path": "Modernization → PQC Preparation",
89
+ "recommendation": "Inventory SSH host keys/KEX algorithms, rotate off legacy RSA where feasible, and plan for vendor support of hybrid/PQC approaches as they mature.",
90
+ })
91
+ continue
92
+
93
+ # Default
94
+ recs.append({
95
+ "host": host,
96
+ "port": port,
97
+ "severity": sev,
98
+ "path": "Modernization",
99
+ "recommendation": f.get("recommendation") or "Standardize crypto baselines and plan phased upgrades.",
100
+ })
101
+
102
+ return recs
@@ -0,0 +1,137 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from dataclasses import dataclass, asdict
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ @dataclass
11
+ class OperatorContext:
12
+ data_types: List[str] # e.g., ["PCI","PHI"]
13
+ data_longevity_years: int # e.g., 7
14
+ exposure: str # "internal" | "mixed" | "internet"
15
+ crown_jewels: List[str] # list of hosts / fqdn / ip strings
16
+
17
+ def to_dict(self) -> Dict[str, Any]:
18
+ return asdict(self)
19
+
20
+
21
+ def _normalize_choice(s: str) -> str:
22
+ return (s or "").strip().upper()
23
+
24
+
25
+ def _prompt_list(prompt: str) -> List[str]:
26
+ raw = input(prompt).strip()
27
+ if not raw:
28
+ return []
29
+ return [x.strip() for x in raw.split(",") if x.strip()]
30
+
31
+
32
+ def prompt_for_context() -> OperatorContext:
33
+ print("\n🧠 Assessment Context (v3.5.1)")
34
+ print("This helps generate a better Quantum Readiness Score + Transition Roadmap.\n")
35
+
36
+ print("Select data types present (comma-separated). Examples: PCI, PHI, FINANCIAL, TRADE, PUBLIC")
37
+ dt = _prompt_list("Data types: ")
38
+ dt = [_normalize_choice(x) for x in dt]
39
+
40
+ # Longevity
41
+ try:
42
+ years_raw = input("How many years must this data remain confidential? (default 7): ").strip()
43
+ years = int(years_raw) if years_raw else 7
44
+ except Exception:
45
+ years = 7
46
+ if years < 1:
47
+ raise ValueError(
48
+ f"Data longevity years must be at least 1 (got: {years_raw!r})"
49
+ )
50
+
51
+ # Exposure
52
+ print("\nExposure context:")
53
+ print(" 1) internal (internal-only / segmented)")
54
+ print(" 2) mixed (internal + some internet-facing)")
55
+ print(" 3) internet (many internet-facing services)")
56
+ exp_raw = input("Choose 1/2/3 (default 2): ").strip()
57
+ exposure = "mixed"
58
+ if exp_raw == "1":
59
+ exposure = "internal"
60
+ elif exp_raw == "3":
61
+ exposure = "internet"
62
+
63
+ # Crown jewels
64
+ cj = _prompt_list("\nOptional: crown jewels hosts/IPs/FQDNs (comma-separated, blank to skip): ")
65
+ cj = [x.strip() for x in cj if x.strip()]
66
+
67
+ ctx = OperatorContext(
68
+ data_types=dt or ["PUBLIC"],
69
+ data_longevity_years=years,
70
+ exposure=exposure,
71
+ crown_jewels=cj,
72
+ )
73
+ print("\n✅ Context captured.\n")
74
+ return ctx
75
+
76
+
77
+ def attach_context(cfg, ctx: OperatorContext) -> None:
78
+ """
79
+ Attach context to cfg in a way that is safe across dataclass/pydantic configs.
80
+ Prefer cfg.assessment_context if possible; otherwise attach to cfg.assessment if it exists.
81
+ """
82
+ ctx_dict = ctx.to_dict()
83
+
84
+ # Try top-level storage
85
+ # D-07 (WR-08): narrow AttributeError so missing-attribute paths are visible
86
+ # in logs (not silently swallowed). Trailing `except Exception` is a user-
87
+ # override safety net — log AND re-raise so unexpected failures remain
88
+ # observable (e.g. dataclass FrozenInstanceError, custom __setattr__ raising
89
+ # ValueError, etc.).
90
+ try:
91
+ setattr(cfg, "assessment_context", ctx_dict)
92
+ return
93
+ except AttributeError as e:
94
+ logger.warning("attach_context skipped — source object missing attribute: %s", e)
95
+ except Exception as e:
96
+ logger.warning("attach_context unexpected: %s", e)
97
+ raise
98
+
99
+ # Try nested (cfg.assessment.*)
100
+ try:
101
+ assessment = getattr(cfg, "assessment", None)
102
+ if assessment is not None:
103
+ setattr(assessment, "context", ctx_dict)
104
+ return
105
+ except AttributeError as e:
106
+ logger.warning("attach_context skipped — source object missing attribute: %s", e)
107
+ except Exception as e:
108
+ logger.warning("attach_context unexpected: %s", e)
109
+ raise
110
+
111
+ # Last resort: no-op (still safe; score engine will default)
112
+ return
113
+
114
+
115
+ def get_context(cfg) -> Dict[str, Any]:
116
+ """
117
+ Retrieve context dict if available; otherwise return defaults.
118
+ """
119
+ # 1) top-level
120
+ ctx = getattr(cfg, "assessment_context", None)
121
+ if isinstance(ctx, dict):
122
+ return ctx
123
+
124
+ # 2) nested
125
+ assessment = getattr(cfg, "assessment", None)
126
+ if assessment is not None:
127
+ ctx2 = getattr(assessment, "context", None)
128
+ if isinstance(ctx2, dict):
129
+ return ctx2
130
+
131
+ # defaults
132
+ return {
133
+ "data_types": ["PUBLIC"],
134
+ "data_longevity_years": 7,
135
+ "exposure": "mixed",
136
+ "crown_jewels": [],
137
+ }
@@ -0,0 +1,12 @@
1
+ """CBOM generation pipeline."""
2
+ from quirk.cbom.builder import build_cbom
3
+ from quirk.cbom.classifier import classify_algorithm, quantum_safety_label, QuantumSafety
4
+ from quirk.cbom.writer import write_cbom_files
5
+
6
+ __all__ = [
7
+ "build_cbom",
8
+ "classify_algorithm",
9
+ "quantum_safety_label",
10
+ "QuantumSafety",
11
+ "write_cbom_files",
12
+ ]