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.
- quirk_scanner-4.10.0/LICENSE +21 -0
- quirk_scanner-4.10.0/PKG-INFO +75 -0
- quirk_scanner-4.10.0/README.md +110 -0
- quirk_scanner-4.10.0/pyproject.toml +149 -0
- quirk_scanner-4.10.0/quirk/__init__.py +29 -0
- quirk_scanner-4.10.0/quirk/assessment/migration_advisor.py +102 -0
- quirk_scanner-4.10.0/quirk/assessment/operator_context.py +137 -0
- quirk_scanner-4.10.0/quirk/cbom/__init__.py +12 -0
- quirk_scanner-4.10.0/quirk/cbom/builder.py +716 -0
- quirk_scanner-4.10.0/quirk/cbom/classifier.py +249 -0
- quirk_scanner-4.10.0/quirk/cbom/writer.py +103 -0
- quirk_scanner-4.10.0/quirk/cli/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/cli/banner.py +179 -0
- quirk_scanner-4.10.0/quirk/cli/cmvp_cmd.py +148 -0
- quirk_scanner-4.10.0/quirk/cli/doctor_cmd.py +280 -0
- quirk_scanner-4.10.0/quirk/cli/errors_cmd.py +129 -0
- quirk_scanner-4.10.0/quirk/cli/init_cmd.py +64 -0
- quirk_scanner-4.10.0/quirk/cli/job_progress.py +107 -0
- quirk_scanner-4.10.0/quirk/cli/qramm_cmd.py +65 -0
- quirk_scanner-4.10.0/quirk/cli/schedule_cmd.py +192 -0
- quirk_scanner-4.10.0/quirk/cli/scheduler_cmd.py +244 -0
- quirk_scanner-4.10.0/quirk/compliance/__init__.py +292 -0
- quirk_scanner-4.10.0/quirk/compliance/cmvp.py +528 -0
- quirk_scanner-4.10.0/quirk/config.py +489 -0
- quirk_scanner-4.10.0/quirk/config_template.yaml +143 -0
- quirk_scanner-4.10.0/quirk/dashboard/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/app.py +159 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/deps.py +58 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/middleware/__init__.py +1 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/middleware/auth.py +50 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/middleware/csrf.py +29 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/middleware/rate_limit.py +68 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/health.py +12 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/jobs.py +211 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/pdf.py +118 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/qramm.py +749 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/scan.py +1267 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/schedules.py +192 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/routes/trends.py +195 -0
- quirk_scanner-4.10.0/quirk/dashboard/api/schemas.py +367 -0
- quirk_scanner-4.10.0/quirk/dashboard/server.py +50 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/index-D2zfvPHF.css +1 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/index-DwADKPu4.js +56 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/rolldown-runtime-B1FJdls4.js +1 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-charts-CBKA7ysD.js +63 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-graph-DyJHMikF.js +321 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-react-BaxwhIbJ.js +11 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/assets/vendor-table-BxYSsWPM.js +4 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/favicon.ico +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/favicon.png +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/favicon.svg +13 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/fonts/JetBrainsMono-Italic[wght].ttf +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/fonts/JetBrainsMono[wght].ttf +0 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/icons.svg +24 -0
- quirk_scanner-4.10.0/quirk/dashboard/static/index.html +21 -0
- quirk_scanner-4.10.0/quirk/db.py +423 -0
- quirk_scanner-4.10.0/quirk/discovery/coverage.py +30 -0
- quirk_scanner-4.10.0/quirk/discovery/nmap_parser.py +85 -0
- quirk_scanner-4.10.0/quirk/discovery/nmap_provider.py +138 -0
- quirk_scanner-4.10.0/quirk/engine/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/engine/cache.py +115 -0
- quirk_scanner-4.10.0/quirk/engine/findings_evaluator.py +924 -0
- quirk_scanner-4.10.0/quirk/engine/profiles.py +166 -0
- quirk_scanner-4.10.0/quirk/engine/rate_limiter.py +36 -0
- quirk_scanner-4.10.0/quirk/engine/risk_engine.py +10 -0
- quirk_scanner-4.10.0/quirk/errors.py +275 -0
- quirk_scanner-4.10.0/quirk/intelligence/__init__.py +29 -0
- quirk_scanner-4.10.0/quirk/intelligence/confidence.py +135 -0
- quirk_scanner-4.10.0/quirk/intelligence/evidence.py +432 -0
- quirk_scanner-4.10.0/quirk/intelligence/roadmap.py +468 -0
- quirk_scanner-4.10.0/quirk/intelligence/schema.py +115 -0
- quirk_scanner-4.10.0/quirk/intelligence/scoring.py +278 -0
- quirk_scanner-4.10.0/quirk/intelligence/trends.py +297 -0
- quirk_scanner-4.10.0/quirk/interactive.py +312 -0
- quirk_scanner-4.10.0/quirk/logging_util.py +44 -0
- quirk_scanner-4.10.0/quirk/models.py +243 -0
- quirk_scanner-4.10.0/quirk/qramm/__init__.py +7 -0
- quirk_scanner-4.10.0/quirk/qramm/compliance_map.py +126 -0
- quirk_scanner-4.10.0/quirk/qramm/evidence_bridge.py +261 -0
- quirk_scanner-4.10.0/quirk/qramm/model_meta.py +45 -0
- quirk_scanner-4.10.0/quirk/qramm/questions.py +208 -0
- quirk_scanner-4.10.0/quirk/qramm/scoring.py +94 -0
- quirk_scanner-4.10.0/quirk/reports/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/reports/_md_escape.py +34 -0
- quirk_scanner-4.10.0/quirk/reports/executive.py +286 -0
- quirk_scanner-4.10.0/quirk/reports/html_renderer.py +284 -0
- quirk_scanner-4.10.0/quirk/reports/technical.py +125 -0
- quirk_scanner-4.10.0/quirk/reports/templates/report.html.j2 +356 -0
- quirk_scanner-4.10.0/quirk/reports/writer.py +289 -0
- quirk_scanner-4.10.0/quirk/scanner/__init__.py +0 -0
- quirk_scanner-4.10.0/quirk/scanner/adcs_scanner.py +495 -0
- quirk_scanner-4.10.0/quirk/scanner/aws_connector.py +486 -0
- quirk_scanner-4.10.0/quirk/scanner/azure_connector.py +311 -0
- quirk_scanner-4.10.0/quirk/scanner/broker_scanner.py +812 -0
- quirk_scanner-4.10.0/quirk/scanner/container_scanner.py +142 -0
- quirk_scanner-4.10.0/quirk/scanner/db_connector.py +301 -0
- quirk_scanner-4.10.0/quirk/scanner/dnssec_scanner.py +365 -0
- quirk_scanner-4.10.0/quirk/scanner/email_scanner.py +607 -0
- quirk_scanner-4.10.0/quirk/scanner/fingerprint.py +206 -0
- quirk_scanner-4.10.0/quirk/scanner/gcp_connector.py +482 -0
- quirk_scanner-4.10.0/quirk/scanner/jwt_scanner.py +221 -0
- quirk_scanner-4.10.0/quirk/scanner/k8s_connector.py +573 -0
- quirk_scanner-4.10.0/quirk/scanner/kerberos_scanner.py +364 -0
- quirk_scanner-4.10.0/quirk/scanner/saml_scanner.py +503 -0
- quirk_scanner-4.10.0/quirk/scanner/smime_scanner.py +291 -0
- quirk_scanner-4.10.0/quirk/scanner/source_scanner.py +120 -0
- quirk_scanner-4.10.0/quirk/scanner/ssh_scanner.py +145 -0
- quirk_scanner-4.10.0/quirk/scanner/target_expander.py +71 -0
- quirk_scanner-4.10.0/quirk/scanner/tls_capabilities.py +246 -0
- quirk_scanner-4.10.0/quirk/scanner/tls_scanner.py +551 -0
- quirk_scanner-4.10.0/quirk/scanner/vault_connector.py +498 -0
- quirk_scanner-4.10.0/quirk/util/__init__.py +1 -0
- quirk_scanner-4.10.0/quirk/util/optional_extra.py +234 -0
- quirk_scanner-4.10.0/quirk/util/safe_exc.py +53 -0
- quirk_scanner-4.10.0/quirk/util/sanitize.py +80 -0
- quirk_scanner-4.10.0/quirk/util/subprocess_input.py +175 -0
- quirk_scanner-4.10.0/quirk/util/targets.py +316 -0
- quirk_scanner-4.10.0/quirk/util/url_allowlist.py +161 -0
- quirk_scanner-4.10.0/quirk/util/weak_crypto.py +102 -0
- quirk_scanner-4.10.0/quirk/validate.py +162 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/PKG-INFO +75 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/SOURCES.txt +332 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/dependency_links.txt +1 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/entry_points.txt +2 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/requires.txt +80 -0
- quirk_scanner-4.10.0/quirk_scanner.egg-info/top_level.txt +2 -0
- quirk_scanner-4.10.0/run_scan.py +1823 -0
- quirk_scanner-4.10.0/setup.cfg +4 -0
- quirk_scanner-4.10.0/tests/test_adcs_ast_gate.py +166 -0
- quirk_scanner-4.10.0/tests/test_adcs_no_writes.py +144 -0
- quirk_scanner-4.10.0/tests/test_adcs_scanner.py +241 -0
- quirk_scanner-4.10.0/tests/test_api_auth.py +434 -0
- quirk_scanner-4.10.0/tests/test_api_qramm_hardening.py +208 -0
- quirk_scanner-4.10.0/tests/test_api_scan_window.py +234 -0
- quirk_scanner-4.10.0/tests/test_apply_security_cli_overrides.py +44 -0
- quirk_scanner-4.10.0/tests/test_audit_ledger_zero_open.py +74 -0
- quirk_scanner-4.10.0/tests/test_aws_connector.py +323 -0
- quirk_scanner-4.10.0/tests/test_azure_blob.py +181 -0
- quirk_scanner-4.10.0/tests/test_azure_keyvault.py +152 -0
- quirk_scanner-4.10.0/tests/test_banner_comment_corrected.py +28 -0
- quirk_scanner-4.10.0/tests/test_broker_config_and_profile.py +82 -0
- quirk_scanner-4.10.0/tests/test_broker_db_schema.py +57 -0
- quirk_scanner-4.10.0/tests/test_broker_run_integration.py +319 -0
- quirk_scanner-4.10.0/tests/test_broker_scanner_kafka.py +310 -0
- quirk_scanner-4.10.0/tests/test_broker_scanner_rabbitmq.py +405 -0
- quirk_scanner-4.10.0/tests/test_broker_scanner_redis.py +281 -0
- quirk_scanner-4.10.0/tests/test_cache.py +192 -0
- quirk_scanner-4.10.0/tests/test_cbom_builder.py +561 -0
- quirk_scanner-4.10.0/tests/test_cbom_builder_algo_hints.py +41 -0
- quirk_scanner-4.10.0/tests/test_cbom_builder_ssh_json_error.py +27 -0
- quirk_scanner-4.10.0/tests/test_cbom_classifier.py +259 -0
- quirk_scanner-4.10.0/tests/test_cbom_classifier_coverage.py +122 -0
- quirk_scanner-4.10.0/tests/test_cbom_coverage.py +120 -0
- quirk_scanner-4.10.0/tests/test_cbom_integration.py +190 -0
- quirk_scanner-4.10.0/tests/test_cbom_motion_endpoints.py +667 -0
- quirk_scanner-4.10.0/tests/test_cbom_motion_golden.py +428 -0
- quirk_scanner-4.10.0/tests/test_cbom_scan_route.py +83 -0
- quirk_scanner-4.10.0/tests/test_cbom_schema_validation.py +92 -0
- quirk_scanner-4.10.0/tests/test_cbom_skip_lists.py +84 -0
- quirk_scanner-4.10.0/tests/test_cbom_vault_consistency.py +73 -0
- quirk_scanner-4.10.0/tests/test_cbom_writer.py +167 -0
- quirk_scanner-4.10.0/tests/test_cbom_writer_validation.py +188 -0
- quirk_scanner-4.10.0/tests/test_cert_pubkey_fix.py +58 -0
- quirk_scanner-4.10.0/tests/test_chaos_lab_idempotency.py +129 -0
- quirk_scanner-4.10.0/tests/test_chaos_lab_image_pinning.py +43 -0
- quirk_scanner-4.10.0/tests/test_chaos_storage.py +90 -0
- quirk_scanner-4.10.0/tests/test_cli_correctness.py +227 -0
- quirk_scanner-4.10.0/tests/test_cli_init.py +160 -0
- quirk_scanner-4.10.0/tests/test_cli_version.py +19 -0
- quirk_scanner-4.10.0/tests/test_cloud_connectors.py +395 -0
- quirk_scanner-4.10.0/tests/test_cmvp_coverage_query.py +219 -0
- quirk_scanner-4.10.0/tests/test_cmvp_freshness.py +113 -0
- quirk_scanner-4.10.0/tests/test_cmvp_no_certified_true.py +259 -0
- quirk_scanner-4.10.0/tests/test_cmvp_refresh.py +309 -0
- quirk_scanner-4.10.0/tests/test_cmvp_report_column.py +264 -0
- quirk_scanner-4.10.0/tests/test_compliance_cli.py +53 -0
- quirk_scanner-4.10.0/tests/test_compliance_coverage_status.py +37 -0
- quirk_scanner-4.10.0/tests/test_compliance_freshness.py +26 -0
- quirk_scanner-4.10.0/tests/test_compliance_report_section.py +79 -0
- quirk_scanner-4.10.0/tests/test_compliance_schema.py +126 -0
- quirk_scanner-4.10.0/tests/test_compliance_status_staleness.py +118 -0
- quirk_scanner-4.10.0/tests/test_compliance_title_join.py +28 -0
- quirk_scanner-4.10.0/tests/test_container_scanner.py +74 -0
- quirk_scanner-4.10.0/tests/test_coverage_bounds.py +79 -0
- quirk_scanner-4.10.0/tests/test_credential_leakage.py +92 -0
- quirk_scanner-4.10.0/tests/test_dar_dashboard.py +130 -0
- quirk_scanner-4.10.0/tests/test_dar_k8s_scoring.py +139 -0
- quirk_scanner-4.10.0/tests/test_dar_storage_scoring.py +117 -0
- quirk_scanner-4.10.0/tests/test_dar_vault_scoring.py +87 -0
- quirk_scanner-4.10.0/tests/test_dashboard_api.py +134 -0
- quirk_scanner-4.10.0/tests/test_dashboard_scan_history.py +350 -0
- quirk_scanner-4.10.0/tests/test_dashboard_schemas_finding_category.py +23 -0
- quirk_scanner-4.10.0/tests/test_dashboard_theme.py +31 -0
- quirk_scanner-4.10.0/tests/test_dashboard_trends.py +352 -0
- quirk_scanner-4.10.0/tests/test_dashboard_wiring.py +152 -0
- quirk_scanner-4.10.0/tests/test_db_connector.py +409 -0
- quirk_scanner-4.10.0/tests/test_db_ensure_columns_generic.py +97 -0
- quirk_scanner-4.10.0/tests/test_db_migrate_cli.py +238 -0
- quirk_scanner-4.10.0/tests/test_db_migrations.py +138 -0
- quirk_scanner-4.10.0/tests/test_dnssec_scanner.py +560 -0
- quirk_scanner-4.10.0/tests/test_dnssec_scanner_reserved_algs.py +29 -0
- quirk_scanner-4.10.0/tests/test_doctor_actionable.py +124 -0
- quirk_scanner-4.10.0/tests/test_doctor_cmd.py +76 -0
- quirk_scanner-4.10.0/tests/test_email_findings.py +171 -0
- quirk_scanner-4.10.0/tests/test_email_run_scan_wiring.py +188 -0
- quirk_scanner-4.10.0/tests/test_email_scanner.py +614 -0
- quirk_scanner-4.10.0/tests/test_error_codes_freshness.py +45 -0
- quirk_scanner-4.10.0/tests/test_errors.py +92 -0
- quirk_scanner-4.10.0/tests/test_errors_cmd.py +99 -0
- quirk_scanner-4.10.0/tests/test_evidence_bridge_correctness.py +226 -0
- quirk_scanner-4.10.0/tests/test_evidence_coverage_gap.py +49 -0
- quirk_scanner-4.10.0/tests/test_evidence_protocol_keys.py +29 -0
- quirk_scanner-4.10.0/tests/test_executive_score_guard.py +48 -0
- quirk_scanner-4.10.0/tests/test_executive_truncation_indicator.py +57 -0
- quirk_scanner-4.10.0/tests/test_extras_concurrency_expander.py +152 -0
- quirk_scanner-4.10.0/tests/test_extras_install_matrix.py +165 -0
- quirk_scanner-4.10.0/tests/test_findings_evaluator_dedupe.py +82 -0
- quirk_scanner-4.10.0/tests/test_fingerprint_host_header.py +32 -0
- quirk_scanner-4.10.0/tests/test_fingerprint_socket_cleanup.py +143 -0
- quirk_scanner-4.10.0/tests/test_gap_closure.py +95 -0
- quirk_scanner-4.10.0/tests/test_gap_closure_packaging.py +37 -0
- quirk_scanner-4.10.0/tests/test_gcp_connector.py +194 -0
- quirk_scanner-4.10.0/tests/test_gcs_reuse.py +56 -0
- quirk_scanner-4.10.0/tests/test_html_renderer_coverage_gaps.py +126 -0
- quirk_scanner-4.10.0/tests/test_html_renderer_roadmap_section.py +49 -0
- quirk_scanner-4.10.0/tests/test_html_report.py +89 -0
- quirk_scanner-4.10.0/tests/test_hygiene.py +223 -0
- quirk_scanner-4.10.0/tests/test_identity_findings_accuracy.py +215 -0
- quirk_scanner-4.10.0/tests/test_identity_infra.py +302 -0
- quirk_scanner-4.10.0/tests/test_identity_scanner_hardening.py +166 -0
- quirk_scanner-4.10.0/tests/test_identity_surface.py +689 -0
- quirk_scanner-4.10.0/tests/test_infra03_nyquist_coverage.py +393 -0
- quirk_scanner-4.10.0/tests/test_init_db_idempotent.py +86 -0
- quirk_scanner-4.10.0/tests/test_install_all_excludes_impacket.py +119 -0
- quirk_scanner-4.10.0/tests/test_install_errors.py +124 -0
- quirk_scanner-4.10.0/tests/test_intelligence_confidence.py +155 -0
- quirk_scanner-4.10.0/tests/test_intelligence_evidence.py +168 -0
- quirk_scanner-4.10.0/tests/test_intelligence_public_api.py +84 -0
- quirk_scanner-4.10.0/tests/test_intelligence_roadmap.py +137 -0
- quirk_scanner-4.10.0/tests/test_intelligence_schema.py +73 -0
- quirk_scanner-4.10.0/tests/test_intelligence_scoring.py +148 -0
- quirk_scanner-4.10.0/tests/test_intelligence_trends.py +228 -0
- quirk_scanner-4.10.0/tests/test_interactive_mode.py +534 -0
- quirk_scanner-4.10.0/tests/test_interactive_validate_routes.py +179 -0
- quirk_scanner-4.10.0/tests/test_job_progress.py +49 -0
- quirk_scanner-4.10.0/tests/test_jobs_api.py +335 -0
- quirk_scanner-4.10.0/tests/test_jwt_scanner.py +106 -0
- quirk_scanner-4.10.0/tests/test_k8s_connector.py +663 -0
- quirk_scanner-4.10.0/tests/test_kerberos_scanner.py +424 -0
- quirk_scanner-4.10.0/tests/test_kerberos_scanner_realm_ipaddress.py +46 -0
- quirk_scanner-4.10.0/tests/test_md_cell_escape.py +90 -0
- quirk_scanner-4.10.0/tests/test_migration_advisor_precision.py +84 -0
- quirk_scanner-4.10.0/tests/test_motion_scoring.py +221 -0
- quirk_scanner-4.10.0/tests/test_nmap_hardening.py +135 -0
- quirk_scanner-4.10.0/tests/test_nmap_provider.py +21 -0
- quirk_scanner-4.10.0/tests/test_operator_context_years_clamp.py +114 -0
- quirk_scanner-4.10.0/tests/test_optional_extra.py +409 -0
- quirk_scanner-4.10.0/tests/test_packaging.py +95 -0
- quirk_scanner-4.10.0/tests/test_pdf_export.py +32 -0
- quirk_scanner-4.10.0/tests/test_pdf_metadata_constants.py +109 -0
- quirk_scanner-4.10.0/tests/test_pdf_render_hardening.py +181 -0
- quirk_scanner-4.10.0/tests/test_phase50_docs_presence.py +57 -0
- quirk_scanner-4.10.0/tests/test_platform_version_single_source.py +70 -0
- quirk_scanner-4.10.0/tests/test_pqc_terminology_gate.py +47 -0
- quirk_scanner-4.10.0/tests/test_profiles.py +123 -0
- quirk_scanner-4.10.0/tests/test_qramm_answer.py +87 -0
- quirk_scanner-4.10.0/tests/test_qramm_compliance_map.py +188 -0
- quirk_scanner-4.10.0/tests/test_qramm_delete_session_fk.py +130 -0
- quirk_scanner-4.10.0/tests/test_qramm_evidence_bridge.py +311 -0
- quirk_scanner-4.10.0/tests/test_qramm_model_stale.py +46 -0
- quirk_scanner-4.10.0/tests/test_qramm_models.py +321 -0
- quirk_scanner-4.10.0/tests/test_qramm_multiplier.py +56 -0
- quirk_scanner-4.10.0/tests/test_qramm_multiplier_constants.py +71 -0
- quirk_scanner-4.10.0/tests/test_qramm_practice_scoring.py +94 -0
- quirk_scanner-4.10.0/tests/test_qramm_questions.py +83 -0
- quirk_scanner-4.10.0/tests/test_qramm_router.py +531 -0
- quirk_scanner-4.10.0/tests/test_qramm_routes_typed_response.py +46 -0
- quirk_scanner-4.10.0/tests/test_qramm_scoring.py +136 -0
- quirk_scanner-4.10.0/tests/test_qramm_staleness.py +126 -0
- quirk_scanner-4.10.0/tests/test_rate_limiter.py +69 -0
- quirk_scanner-4.10.0/tests/test_report_injection_hardening.py +268 -0
- quirk_scanner-4.10.0/tests/test_report_sanitization.py +170 -0
- quirk_scanner-4.10.0/tests/test_reports_writer.py +254 -0
- quirk_scanner-4.10.0/tests/test_rich_output.py +25 -0
- quirk_scanner-4.10.0/tests/test_risk_engine.py +475 -0
- quirk_scanner-4.10.0/tests/test_risk_engine_cert_defects.py +227 -0
- quirk_scanner-4.10.0/tests/test_risk_engine_coverage_gap.py +90 -0
- quirk_scanner-4.10.0/tests/test_roadmap_baseline_governance.py +63 -0
- quirk_scanner-4.10.0/tests/test_run_scan_budget_guard.py +117 -0
- quirk_scanner-4.10.0/tests/test_run_scan_init_db_scope.py +90 -0
- quirk_scanner-4.10.0/tests/test_run_scan_targets_file.py +109 -0
- quirk_scanner-4.10.0/tests/test_s3_encryption.py +190 -0
- quirk_scanner-4.10.0/tests/test_safe_exc.py +58 -0
- quirk_scanner-4.10.0/tests/test_safe_filter_audit.py +323 -0
- quirk_scanner-4.10.0/tests/test_saml_scanner.py +421 -0
- quirk_scanner-4.10.0/tests/test_saml_scanner_sha1_word_boundary.py +26 -0
- quirk_scanner-4.10.0/tests/test_sanitize_scanner_text.py +98 -0
- quirk_scanner-4.10.0/tests/test_scan_error_gate.py +286 -0
- quirk_scanner-4.10.0/tests/test_scan_robustness.py +140 -0
- quirk_scanner-4.10.0/tests/test_schedule_cmd.py +153 -0
- quirk_scanner-4.10.0/tests/test_scheduler_cmd.py +318 -0
- quirk_scanner-4.10.0/tests/test_schedules_api.py +307 -0
- quirk_scanner-4.10.0/tests/test_score_clamp_property.py +80 -0
- quirk_scanner-4.10.0/tests/test_score_weights_invariant.py +30 -0
- quirk_scanner-4.10.0/tests/test_scoring_consolidation.py +214 -0
- quirk_scanner-4.10.0/tests/test_scoring_correctness.py +198 -0
- quirk_scanner-4.10.0/tests/test_security_config.py +139 -0
- quirk_scanner-4.10.0/tests/test_skip_registry.py +116 -0
- quirk_scanner-4.10.0/tests/test_smime_ast_gate.py +120 -0
- quirk_scanner-4.10.0/tests/test_smime_no_envelope_leak.py +124 -0
- quirk_scanner-4.10.0/tests/test_smime_scanner.py +141 -0
- quirk_scanner-4.10.0/tests/test_source_scanner.py +105 -0
- quirk_scanner-4.10.0/tests/test_ssh_scanner.py +327 -0
- quirk_scanner-4.10.0/tests/test_sslyze_integration.py +494 -0
- quirk_scanner-4.10.0/tests/test_subprocess_logging.py +93 -0
- quirk_scanner-4.10.0/tests/test_targets_parser.py +288 -0
- quirk_scanner-4.10.0/tests/test_timeouts_config.py +111 -0
- quirk_scanner-4.10.0/tests/test_tls_capabilities_comment.py +27 -0
- quirk_scanner-4.10.0/tests/test_tls_kex_label.py +48 -0
- quirk_scanner-4.10.0/tests/test_tls_scanner_chain_verified.py +321 -0
- quirk_scanner-4.10.0/tests/test_tls_scanner_resource_cleanup.py +127 -0
- quirk_scanner-4.10.0/tests/test_trends_subsecond_sessions.py +113 -0
- quirk_scanner-4.10.0/tests/test_trends_yield_per.py +24 -0
- quirk_scanner-4.10.0/tests/test_uat_db_integration.py +96 -0
- quirk_scanner-4.10.0/tests/test_v41_gap_closure.py +70 -0
- quirk_scanner-4.10.0/tests/test_validate.py +82 -0
- quirk_scanner-4.10.0/tests/test_vault_connector.py +557 -0
- quirk_scanner-4.10.0/tests/test_version.py +72 -0
- quirk_scanner-4.10.0/tests/test_weak_crypto_helper.py +64 -0
- quirk_scanner-4.10.0/tests/test_weak_crypto_pfs_weak_helpers.py +70 -0
- quirk_scanner-4.10.0/tests/test_writer.py +54 -0
- 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
|
+
[](https://github.com/0xD1g5/QU.I.R.K./actions/workflows/python-staleness.yml)
|
|
2
|
+
[](https://pypi.org/project/quirk-scanner/)
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](docs/release-process.md#attestation-verification)
|
|
5
|
+
[](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
|
+

|
|
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
|
+
]
|