icdev 0.0.3__py3-none-any.whl
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.
- args/agent_config.yaml +113 -0
- args/audit_regimes/cisa_sbd.json +381 -0
- args/audit_regimes/cmmc_l2.json +906 -0
- args/audit_regimes/dod_cssp.json +393 -0
- args/audit_regimes/dodi_5000_87.json +297 -0
- args/audit_regimes/fedramp_moderate.json +650 -0
- args/audit_regimes/ieee_1012.json +373 -0
- args/audit_regimes/nist_800_171.json +624 -0
- args/audit_regimes/nist_800_53.json +907 -0
- args/cloudforge_blueprints/aws_commercial.yaml +29 -0
- args/cloudforge_blueprints/aws_govcloud_il4.yaml +34 -0
- args/cloudforge_blueprints/aws_govcloud_il5.yaml +38 -0
- args/cloudforge_blueprints/azure_commercial.yaml +28 -0
- args/cloudforge_blueprints/azure_gov_il4.yaml +32 -0
- args/cloudforge_blueprints/azure_gov_il5.yaml +36 -0
- args/cloudforge_blueprints/gcp_commercial.yaml +28 -0
- args/cloudforge_blueprints/oci_commercial.yaml +28 -0
- args/cloudforge_config.yaml +231 -0
- args/cloudforge_runbook_templates/backup_verify.yaml +98 -0
- args/cloudforge_runbook_templates/dr_failover.yaml +107 -0
- args/cloudforge_runbook_templates/health_check.yaml +97 -0
- args/cloudforge_runbook_templates/incident_response.yaml +101 -0
- args/cloudforge_runbook_templates/migration_cutover.yaml +105 -0
- args/cloudforge_runbook_templates/patch_rollout.yaml +92 -0
- args/cloudforge_runbook_templates/zone_provision.yaml +93 -0
- args/code_pattern_config.yaml +151 -0
- args/code_quality_config.yaml +47 -0
- args/compliance_config.yaml +17 -0
- args/control_inheritance.yaml +177 -0
- args/csp_mcp_config.yaml +41 -0
- args/cui_markings.yaml +35 -0
- args/databridge_config.yaml +232 -0
- args/db_config.yaml +116 -0
- args/decision_tables/agent_trust_decision.yaml +143 -0
- args/decision_tables/ato_boundary_impact.yaml +132 -0
- args/decision_tables/deployment_approval.yaml +152 -0
- args/degradation_matrix.yaml +163 -0
- args/devsecops_config.yaml +286 -0
- args/endpoint_security_config.yaml +207 -0
- args/exit_criteria.yaml +102 -0
- args/feature_flags.yaml +235 -0
- args/file_access_tiers.yaml +88 -0
- args/forge_studio/blueprint_config.yaml +27 -0
- args/forge_studio/component_catalog.json +411 -0
- args/forge_studio/workflow_templates.yaml +103 -0
- args/govcon_config.yaml +41 -0
- args/harness_config.yaml +67 -0
- args/innovation_config.yaml +321 -0
- args/knowledge_graph_config.yaml +113 -0
- args/llm_config.yaml +222 -0
- args/marketplace_config.yaml +260 -0
- args/monitoring_config.yaml +127 -0
- args/mosa_config.yaml +190 -0
- args/observability_tracing_config.yaml +170 -0
- args/owasp_agentic_config.yaml +171 -0
- args/pipeline_gates.yaml +197 -0
- args/project_defaults.yaml +235 -0
- args/prompt_chains.yaml +163 -0
- args/rag_config.yaml +167 -0
- args/research_config.yaml +89 -0
- args/resilience_config.yaml +197 -0
- args/ricoas_config.yaml +191 -0
- args/security_gates.yaml +763 -0
- args/storage_config.yaml +63 -0
- args/writeguard_config.yaml +131 -0
- args/zta_config.yaml +247 -0
- context/__init__.py +6 -0
- context/agent/__init__.py +6 -0
- context/agent/response_schemas/__init__.py +6 -0
- context/agent/response_schemas/debate_position.json +46 -0
- context/agent/response_schemas/fitness_scorecard.json +74 -0
- context/agent/response_schemas/review_decision.json +39 -0
- context/agent/response_schemas/task_decomposition.json +82 -0
- context/agent/response_schemas/veto_decision.json +40 -0
- context/agentic/__init__.py +6 -0
- context/agentic/architecture_patterns.md +269 -0
- context/agentic/capability_registry.yaml +223 -0
- context/agentic/csp_integration.md +30 -0
- context/agentic/csp_mcp_registry.yaml +280 -0
- context/agentic/fitness_rubric.md +56 -0
- context/agentic/governance_baseline.md +205 -0
- context/ci/__init__.py +6 -0
- context/ci/worktree_templates.json +44 -0
- context/cloud/__init__.py +6 -0
- context/cloud/csp_service_registry.json +739 -0
- context/compliance/__init__.py +6 -0
- context/compliance/ai_rmf_crosswalk.yaml +226 -0
- context/compliance/atlas_mitigations.json +293 -0
- context/compliance/atlas_techniques.json +833 -0
- context/compliance/cisa_sbd_requirements.json +477 -0
- context/compliance/cjis_security_policy.json +522 -0
- context/compliance/cmmc_practices.json +2494 -0
- context/compliance/cmmc_report_template.md +142 -0
- context/compliance/cnssi_1253_overlay.json +109 -0
- context/compliance/control_crosswalk.json +1914 -0
- context/compliance/control_families/__init__.py +6 -0
- context/compliance/csp_certifications.json +251 -0
- context/compliance/cssp_report_template.md +193 -0
- context/compliance/cui_templates/__init__.py +6 -0
- context/compliance/cui_templates/banner_block.txt +4 -0
- context/compliance/cui_templates/code_header.txt +8 -0
- context/compliance/cui_templates/document_template.md +35 -0
- context/compliance/data_type_framework_map.json +321 -0
- context/compliance/data_type_registry.json +147 -0
- context/compliance/dod_cssp_8530.json +463 -0
- context/compliance/eu_ai_act_annex_iii.json +108 -0
- context/compliance/export_templates/__init__.py +6 -0
- context/compliance/export_templates/emass_controls.csv.j2 +4 -0
- context/compliance/export_templates/evidence_package.md.j2 +39 -0
- context/compliance/export_templates/executive_summary.md.j2 +55 -0
- context/compliance/export_templates/poam_tracking.csv.j2 +4 -0
- context/compliance/fedramp_20x_ksi_schemas.json +133 -0
- context/compliance/fedramp_high_baseline.json +4370 -0
- context/compliance/fedramp_moderate_baseline.json +2183 -0
- context/compliance/fedramp_report_template.md +181 -0
- context/compliance/fips_200_areas.json +362 -0
- context/compliance/gao_ai_accountability.json +262 -0
- context/compliance/hipaa_security_rule.json +720 -0
- context/compliance/hitrust_csf_v11.json +930 -0
- context/compliance/impact_level_profiles.json +251 -0
- context/compliance/incident_response_template.md +1110 -0
- context/compliance/iso27001_2022_controls.json +750 -0
- context/compliance/iso27001_nist_bridge.json +382 -0
- context/compliance/iso42001_controls.json +254 -0
- context/compliance/ivv_checklist_template.md +80 -0
- context/compliance/ivv_report_template.md +116 -0
- context/compliance/ivv_requirements.json +372 -0
- context/compliance/mosa_crosswalk.json +327 -0
- context/compliance/mosa_framework.json +250 -0
- context/compliance/narrative_templates/AC.md.j2 +101 -0
- context/compliance/narrative_templates/AU.md.j2 +106 -0
- context/compliance/narrative_templates/IA.md.j2 +104 -0
- context/compliance/narrative_templates/SC.md.j2 +102 -0
- context/compliance/narrative_templates/SI.md.j2 +111 -0
- context/compliance/narrative_templates/__init__.py +6 -0
- context/compliance/narrative_templates/default.md.j2 +50 -0
- context/compliance/narrative_templates/executive_summary.j2 +27 -0
- context/compliance/narrative_templates/poam_milestone.j2 +19 -0
- context/compliance/narrative_templates/ssp_section.j2 +11 -0
- context/compliance/nist_800_171_controls.json +1552 -0
- context/compliance/nist_800_207_crosswalk.json +399 -0
- context/compliance/nist_800_207_zta.json +258 -0
- context/compliance/nist_800_53.json +324 -0
- context/compliance/nist_ai_600_1_genai.json +326 -0
- context/compliance/nist_ai_rmf.json +206 -0
- context/compliance/nist_sp_800_60_types.json +1667 -0
- context/compliance/omb_m25_21_high_impact_ai.json +248 -0
- context/compliance/omb_m26_04_unbiased_ai.json +262 -0
- context/compliance/owasp_agentic_asi.json +133 -0
- context/compliance/owasp_agentic_threats.json +285 -0
- context/compliance/owasp_llm_top10.json +274 -0
- context/compliance/pci_dss_v4.json +510 -0
- context/compliance/poam_template.md +117 -0
- context/compliance/safeai_controls.json +512 -0
- context/compliance/sbd_report_template.md +77 -0
- context/compliance/siem_config_templates/__init__.py +6 -0
- context/compliance/siem_config_templates/filebeat.yml +213 -0
- context/compliance/siem_config_templates/log_sources.json +208 -0
- context/compliance/soc2_trust_criteria.json +661 -0
- context/compliance/ssp_template.md +432 -0
- context/compliance/stig_templates/__init__.py +6 -0
- context/compliance/stig_templates/webapp_stig.json +139 -0
- context/compliance/xai_requirements.json +108 -0
- context/dashboard/__init__.py +6 -0
- context/dashboard/nlq_examples.json +50 -0
- context/dashboard/schema_descriptions.json +23 -0
- context/icdev_methodology.md +100 -0
- context/integration/__init__.py +6 -0
- context/integration/approval_workflows.json +32 -0
- context/integration/gitlab_field_mappings.json +33 -0
- context/integration/jira_field_mappings.json +32 -0
- context/integration/reqif_export_schema.json +23 -0
- context/integration/servicenow_field_mappings.json +22 -0
- context/languages/__init__.py +6 -0
- context/languages/framework_patterns.json +205 -0
- context/languages/language_registry.json +279 -0
- context/llm/__init__.py +6 -0
- context/llm/example_provider.py +89 -0
- context/marketplace/assets/writeguard-core.yaml +100 -0
- context/marketplace/assets/writeguard-govcon.yaml +45 -0
- context/marketplace/assets/writeguard-style-guides.yaml +44 -0
- context/mbse/__init__.py +6 -0
- context/mbse/des_report_template.md +162 -0
- context/mbse/des_requirements.json +411 -0
- context/mbse/digital_thread_patterns.json +403 -0
- context/mbse/reqif_schema.json +280 -0
- context/mbse/sysml_element_types.json +432 -0
- context/oscal/NIST_SP-800-53_rev5_catalog.json +254987 -0
- context/oscal/README.md +43 -0
- context/patterns/__init__.py +6 -0
- context/profiles/__init__.py +6 -0
- context/profiles/dod_baseline_v1.yaml +145 -0
- context/profiles/fedramp_baseline_v1.yaml +143 -0
- context/profiles/financial_baseline_v1.yaml +142 -0
- context/profiles/healthcare_baseline_v1.yaml +135 -0
- context/profiles/law_enforcement_v1.yaml +129 -0
- context/profiles/startup_v1.yaml +134 -0
- context/rag/source_mappings.json +42 -0
- context/requirements/__init__.py +6 -0
- context/requirements/ambiguity_patterns.json +97 -0
- context/requirements/boundary_impact_rules.json +123 -0
- context/requirements/default_constitutions.json +67 -0
- context/requirements/document_extraction_rules.json +58 -0
- context/requirements/gap_patterns.json +108 -0
- context/requirements/readiness_rubric.json +78 -0
- context/requirements/red_alternative_patterns.json +210 -0
- context/requirements/safe_templates.json +72 -0
- context/requirements/spec_quality_checklist.json +122 -0
- context/research/regulatory_registry.json +114 -0
- context/research/verticals/cybersecurity.json +127 -0
- context/research/verticals/defense.json +104 -0
- context/research/verticals/fintech.json +125 -0
- context/research/verticals/healthcare.json +118 -0
- context/research/verticals/logistics.json +117 -0
- context/research/verticals/trading.json +145 -0
- context/simulation/__init__.py +6 -0
- context/simulation/architecture_patterns.json +36 -0
- context/simulation/coa_templates.json +38 -0
- context/simulation/cost_models.json +23 -0
- context/simulation/risk_categories.json +46 -0
- context/supply_chain/__init__.py +6 -0
- context/supply_chain/isa_templates.json +129 -0
- context/supply_chain/nist_800_161_controls.json +247 -0
- context/supply_chain/scrm_risk_matrix.json +147 -0
- context/templates/__init__.py +6 -0
- context/templates/ansible/__init__.py +6 -0
- context/templates/ansible/playbooks/__init__.py +6 -0
- context/templates/ansible/roles/__init__.py +6 -0
- context/templates/gitlab_ci/__init__.py +6 -0
- context/templates/grafana/__init__.py +6 -0
- context/templates/kubernetes/__init__.py +6 -0
- context/templates/project/__init__.py +6 -0
- context/templates/project/api/__init__.py +6 -0
- context/templates/project/cli/__init__.py +6 -0
- context/templates/project/data_pipeline/__init__.py +6 -0
- context/templates/project/iac/__init__.py +6 -0
- context/templates/project/javascript_frontend/__init__.py +6 -0
- context/templates/project/javascript_frontend/src/__init__.py +6 -0
- context/templates/project/javascript_frontend/tests/__init__.py +6 -0
- context/templates/project/microservice/__init__.py +6 -0
- context/templates/project/python_backend/__init__.py +6 -0
- context/templates/project/python_backend/src/__init__.py +6 -0
- context/templates/project/python_backend/tests/__init__.py +6 -0
- context/templates/project/python_backend/tests/features/__init__.py +6 -0
- context/templates/project/python_backend/tests/steps/__init__.py +6 -0
- context/templates/terraform/__init__.py +6 -0
- context/templates/terraform/govcloud_base/__init__.py +6 -0
- context/templates/terraform/modules/__init__.py +6 -0
- context/tone/__init__.py +6 -0
- context/writing/grammar_rules/common_errors.json +306 -0
- context/writing/grammar_rules/govcon_vocabulary.json +113 -0
- context/writing/style_guides/academic.yaml +43 -0
- context/writing/style_guides/business.yaml +42 -0
- context/writing/style_guides/government.yaml +59 -0
- context/writing/style_guides/proposal.yaml +58 -0
- context/writing/style_guides/technical.yaml +43 -0
- docs/adr/README.md +66 -0
- docs/adr/connector-forge-decisions.md +318 -0
- docs/adr/core-decisions.md +289 -0
- docs/adr/db-decisions.md +94 -0
- docs/adr/harness-decisions.md +122 -0
- docs/adr/innovation-decisions.md +262 -0
- docs/adr/marketplace-decisions.md +109 -0
- docs/adr/sbd-decisions.md +109 -0
- docs/adr/scale-engine-decisions.md +108 -0
- docs/adr/writeguard-decisions.md +136 -0
- docs/architecture/bounded-contexts.md +1032 -0
- docs/features/phase-65-writeguard.md +139 -0
- docs/features/phase-66-marketplace-commerce.md +79 -0
- docs/features/phase-67-knowledge-ingestion-rag-autodraft.md +97 -0
- docs/features/phase-68-enhanced-autodraft-pipeline.md +109 -0
- docs/features/phase-69-proposalai-marketplace-module.md +131 -0
- docs/features/phase-70-databridge.md +214 -0
- docs/features/phase-71-databridge-messaging.md +102 -0
- docs/implementation-plan-architecture-evolution.md +614 -0
- docs/marketplace/CONTRIBUTING.md +124 -0
- docs/marketplace/module_manifest_schema.yaml +83 -0
- docs/research/ai-architecture-patterns-2024-2026.md +1236 -0
- docs/research/app-builder-platform-analysis.md +582 -0
- docs/research/architecture-patterns-c4-ddd-agentic.md +871 -0
- docs/research/flowable-boat-competitive-analysis.md +426 -0
- docs/research/modern-dev-practices-2024-2026.md +1615 -0
- docs/research/secure-by-design-cloudyrion-adaptation.md +270 -0
- goals/agent_management.md +144 -0
- goals/ai_accountability.md +90 -0
- goals/ai_narratives.md +79 -0
- goals/ai_transparency.md +76 -0
- goals/ato_simulator.md +78 -0
- goals/audit_engine.md +177 -0
- goals/bite_sized_plans.md +225 -0
- goals/boundary_supply_chain.md +206 -0
- goals/brainstorming_gate.md +186 -0
- goals/build_app.md +604 -0
- goals/cato_live_evidence.md +77 -0
- goals/cloudforge.md +106 -0
- goals/code_intelligence.md +197 -0
- goals/compliance_workflow.md +858 -0
- goals/connector_forge.md +133 -0
- goals/databridge.md +128 -0
- goals/deploy_workflow.md +390 -0
- goals/developer_scorecard.md +78 -0
- goals/devsecops_workflow.md +408 -0
- goals/firmware_sbom.md +79 -0
- goals/forge_hub.md +78 -0
- goals/golden_path.md +77 -0
- goals/harness_engineering.md +91 -0
- goals/integration_testing.md +189 -0
- goals/knowledge_graph.md +128 -0
- goals/maintenance_audit.md +196 -0
- goals/manifest.md +50 -0
- goals/monitoring.md +126 -0
- goals/mosa_workflow.md +463 -0
- goals/multi_agent_orchestration.md +68 -0
- goals/observability_traceability_xai.md +154 -0
- goals/owasp_agentic_security.md +395 -0
- goals/pr_intelligence.md +78 -0
- goals/requirements_intake.md +213 -0
- goals/secure_by_design.md +135 -0
- goals/security_scan.md +381 -0
- goals/self_healing.md +120 -0
- goals/simulation_engine.md +111 -0
- goals/subagent_review.md +205 -0
- goals/systematic_debugging.md +257 -0
- goals/tdd_workflow.md +403 -0
- goals/template_exchange.md +77 -0
- goals/thread_heatmap.md +77 -0
- goals/threat_modeler.md +77 -0
- goals/verification_iron_law.md +192 -0
- goals/vsm_dashboard.md +76 -0
- goals/writeguard.md +89 -0
- goals/zero_trust_architecture.md +403 -0
- hardprompts/__init__.py +6 -0
- hardprompts/agent/__init__.py +6 -0
- hardprompts/agent/agentic_architect.md +100 -0
- hardprompts/agent/debate_prompt.md +32 -0
- hardprompts/agent/fitness_evaluation.md +48 -0
- hardprompts/agent/governance_review.md +214 -0
- hardprompts/agent/reviewer_prompt.md +34 -0
- hardprompts/agent/skill_design.md +172 -0
- hardprompts/agent/task_decomposition.md +275 -0
- hardprompts/agent/veto_check_prompt.md +33 -0
- hardprompts/architect/__init__.py +6 -0
- hardprompts/architect/api_design.md +283 -0
- hardprompts/architect/data_model.md +277 -0
- hardprompts/architect/system_design.md +180 -0
- hardprompts/builder/__init__.py +6 -0
- hardprompts/builder/code_generation.md +59 -0
- hardprompts/builder/refactor.md +58 -0
- hardprompts/builder/scaffold_project.md +69 -0
- hardprompts/builder/test_generation.md +87 -0
- hardprompts/ci/__init__.py +6 -0
- hardprompts/ci/worktree_setup.md +35 -0
- hardprompts/compliance/__init__.py +6 -0
- hardprompts/compliance/cmmc_assessment.md +63 -0
- hardprompts/compliance/cssp_assessment.md +75 -0
- hardprompts/compliance/cui_marking.md +86 -0
- hardprompts/compliance/fedramp_assessment.md +55 -0
- hardprompts/compliance/ivv_assessment.md +96 -0
- hardprompts/compliance/poam_generation.md +57 -0
- hardprompts/compliance/sbd_assessment.md +101 -0
- hardprompts/compliance/security_categorization.md +74 -0
- hardprompts/compliance/ssp_generation.md +56 -0
- hardprompts/compliance/stig_evaluation.md +63 -0
- hardprompts/dashboard/__init__.py +6 -0
- hardprompts/dashboard/nlq_system_prompt.md +26 -0
- hardprompts/infra/__init__.py +6 -0
- hardprompts/infra/k8s_manifests.md +118 -0
- hardprompts/infra/pipeline_generation.md +160 -0
- hardprompts/infra/terraform_generation.md +92 -0
- hardprompts/integration/__init__.py +6 -0
- hardprompts/integration/approval_review.md +17 -0
- hardprompts/integration/jira_mapping.md +25 -0
- hardprompts/integration/servicenow_mapping.md +14 -0
- hardprompts/knowledge/__init__.py +6 -0
- hardprompts/knowledge/pattern_detection.md +73 -0
- hardprompts/knowledge/recommendation_engine.md +90 -0
- hardprompts/knowledge/root_cause_analysis.md +91 -0
- hardprompts/maintenance/__init__.py +6 -0
- hardprompts/maintenance/maintenance_assessment.md +82 -0
- hardprompts/mbse/__init__.py +6 -0
- hardprompts/mbse/digital_thread.md +67 -0
- hardprompts/mbse/model_import.md +62 -0
- hardprompts/mbse/model_to_code.md +65 -0
- hardprompts/modernization/__init__.py +6 -0
- hardprompts/modernization/legacy_analysis.md +93 -0
- hardprompts/modernization/migration_planning.md +150 -0
- hardprompts/modernization/seven_r_assessment.md +107 -0
- hardprompts/proposal_draft.md +53 -0
- hardprompts/rag_citation.md +12 -0
- hardprompts/rag_rerank.md +31 -0
- hardprompts/requirements/__init__.py +6 -0
- hardprompts/requirements/bdd_generation.md +35 -0
- hardprompts/requirements/clarification_prioritization.md +29 -0
- hardprompts/requirements/decomposition.md +60 -0
- hardprompts/requirements/document_extraction.md +45 -0
- hardprompts/requirements/gap_detection.md +70 -0
- hardprompts/requirements/intake_conversation.md +101 -0
- hardprompts/requirements/readiness_assessment.md +39 -0
- hardprompts/requirements/spec_quality.md +33 -0
- hardprompts/requirements/traceability_analysis.md +23 -0
- hardprompts/security/__init__.py +6 -0
- hardprompts/security/endpoint_security.md +78 -0
- hardprompts/security/threat_model.md +70 -0
- hardprompts/security/vulnerability_assessment.md +81 -0
- hardprompts/simulation/__init__.py +6 -0
- hardprompts/simulation/architecture_impact.md +27 -0
- hardprompts/simulation/coa_alternative.md +27 -0
- hardprompts/simulation/coa_generation.md +25 -0
- hardprompts/simulation/compliance_impact.md +28 -0
- hardprompts/simulation/cost_estimation.md +33 -0
- hardprompts/simulation/risk_assessment.md +28 -0
- hardprompts/translation/code_translation.md +68 -0
- hardprompts/translation/dependency_suggestion.md +44 -0
- hardprompts/translation/test_translation.md +64 -0
- hardprompts/translation/translation_repair.md +59 -0
- icdev-0.0.3.dist-info/METADATA +909 -0
- icdev-0.0.3.dist-info/RECORD +1214 -0
- icdev-0.0.3.dist-info/WHEEL +5 -0
- icdev-0.0.3.dist-info/entry_points.txt +9 -0
- icdev-0.0.3.dist-info/licenses/LICENSE +201 -0
- icdev-0.0.3.dist-info/licenses/NOTICE +11 -0
- icdev-0.0.3.dist-info/top_level.txt +7 -0
- memory/MEMORY.md +52 -0
- memory/logs/2026-02-14.md +17 -0
- memory/logs/2026-03-03.md +2 -0
- memory/logs/__init__.py +1 -0
- tools/a2a/icdev_callback_client.py +210 -0
- tools/agent/cards/architect_card.json +29 -0
- tools/agent/cards/builder_card.json +34 -0
- tools/agent/cards/compliance_card.json +29 -0
- tools/agent/cards/connector_forge_card.json +49 -0
- tools/agent/cards/devsecops_zta_card.json +24 -0
- tools/agent/cards/knowledge_card.json +29 -0
- tools/agent/cards/monitor_card.json +29 -0
- tools/agent/cards/orchestrator_card.json +29 -0
- tools/agent/cards/requirements_analyst_card.json +24 -0
- tools/agent/cards/security_card.json +29 -0
- tools/agent/cards/simulation_card.json +24 -0
- tools/agent/cards/supply_chain_card.json +24 -0
- tools/analysis/__init__.py +1 -0
- tools/analysis/code_analyzer.py +770 -0
- tools/analysis/runtime_feedback.py +379 -0
- tools/analytics/__init__.py +2 -0
- tools/analytics/scorecard.py +538 -0
- tools/analytics/vsm_engine.py +612 -0
- tools/architecture/__init__.py +2 -0
- tools/architecture/adr_extractor.py +393 -0
- tools/audit/__init__.py +1 -0
- tools/audit/audit_logger.py +199 -0
- tools/audit/audit_query.py +153 -0
- tools/audit/decision_recorder.py +73 -0
- tools/audit_engine/__init__.py +12 -0
- tools/audit_engine/ai_advisor.py +906 -0
- tools/audit_engine/cli.py +286 -0
- tools/audit_engine/comparator.py +305 -0
- tools/audit_engine/eject_scaffolder.py +399 -0
- tools/audit_engine/engine.py +614 -0
- tools/audit_engine/git_fetcher.py +341 -0
- tools/audit_engine/regime_loader.py +200 -0
- tools/audit_engine/regime_updater.py +325 -0
- tools/audit_engine/report_card.py +289 -0
- tools/audit_engine/scanner.py +684 -0
- tools/audit_engine/self_heal.py +1042 -0
- tools/ci/__init__.py +2 -0
- tools/ci/connectors/__init__.py +2 -0
- tools/ci/connectors/base_connector.py +80 -0
- tools/ci/connectors/connector_registry.py +188 -0
- tools/ci/connectors/mattermost_connector.py +159 -0
- tools/ci/connectors/slack_connector.py +197 -0
- tools/ci/core/__init__.py +2 -0
- tools/ci/core/air_gap_detector.py +115 -0
- tools/ci/core/comment_handler.py +192 -0
- tools/ci/core/conversation_manager.py +480 -0
- tools/ci/core/event_envelope.py +500 -0
- tools/ci/core/event_router.py +444 -0
- tools/ci/core/failure_parser.py +397 -0
- tools/ci/core/recovery_engine.py +527 -0
- tools/ci/gate_enforcer.py +361 -0
- tools/ci/modules/__init__.py +2 -0
- tools/ci/modules/agent.py +271 -0
- tools/ci/modules/git_ops.py +175 -0
- tools/ci/modules/state.py +117 -0
- tools/ci/modules/vcs.py +303 -0
- tools/ci/modules/workflow_ops.py +295 -0
- tools/ci/modules/worktree.py +337 -0
- tools/ci/pipeline_config_generator.py +558 -0
- tools/ci/pr_intelligence.py +485 -0
- tools/ci/triggers/__init__.py +2 -0
- tools/ci/triggers/gitlab_task_monitor.py +327 -0
- tools/ci/triggers/poll_trigger.py +237 -0
- tools/ci/triggers/webhook_server.py +356 -0
- tools/ci/workflows/__init__.py +2 -0
- tools/ci/workflows/icdev_build.py +140 -0
- tools/ci/workflows/icdev_comply.py +284 -0
- tools/ci/workflows/icdev_document.py +152 -0
- tools/ci/workflows/icdev_e2e.py +188 -0
- tools/ci/workflows/icdev_patch.py +186 -0
- tools/ci/workflows/icdev_plan.py +202 -0
- tools/ci/workflows/icdev_plan_build.py +41 -0
- tools/ci/workflows/icdev_plan_build_test.py +46 -0
- tools/ci/workflows/icdev_plan_build_test_review.py +47 -0
- tools/ci/workflows/icdev_review.py +126 -0
- tools/ci/workflows/icdev_sdlc.py +261 -0
- tools/ci/workflows/icdev_test.py +240 -0
- tools/cli/__init__.py +1 -0
- tools/cli/output_formatter.py +756 -0
- tools/cloudforge/__init__.py +12 -0
- tools/cloudforge/airgap/__init__.py +2 -0
- tools/cloudforge/airgap/il_classifier.py +70 -0
- tools/cloudforge/airgap/offline_validator.py +42 -0
- tools/cloudforge/airgap/shift_emulator.py +155 -0
- tools/cloudforge/airgap/sneakernet.py +91 -0
- tools/cloudforge/cd_hub/__init__.py +2 -0
- tools/cloudforge/cd_hub/canary_deployer.py +88 -0
- tools/cloudforge/cd_hub/gitops_renderer.py +123 -0
- tools/cloudforge/cd_hub/hub_controller.py +143 -0
- tools/cloudforge/cd_hub/pipeline_bridge.py +30 -0
- tools/cloudforge/cd_hub/rollback_engine.py +29 -0
- tools/cloudforge/cd_hub/spoke_agent.py +51 -0
- tools/cloudforge/compliance/__init__.py +2 -0
- tools/cloudforge/compliance/ato_accelerator.py +272 -0
- tools/cloudforge/compliance/control_inheritor.py +127 -0
- tools/cloudforge/compliance/evidence_generator.py +129 -0
- tools/cloudforge/compliance/poam_bridge.py +41 -0
- tools/cloudforge/compliance/ssp_bridge.py +52 -0
- tools/cloudforge/compliance/stig_bridge.py +41 -0
- tools/cloudforge/container_forge/__init__.py +2 -0
- tools/cloudforge/container_forge/bigbang_renderer.py +85 -0
- tools/cloudforge/container_forge/hardener.py +169 -0
- tools/cloudforge/container_forge/image_scanner_bridge.py +33 -0
- tools/cloudforge/container_forge/runtime_policy.py +87 -0
- tools/cloudforge/container_forge/sbom_bridge.py +42 -0
- tools/cloudforge/finops/__init__.py +2 -0
- tools/cloudforge/finops/anomaly_detector.py +78 -0
- tools/cloudforge/finops/budget_tracker.py +96 -0
- tools/cloudforge/finops/chargeback.py +69 -0
- tools/cloudforge/finops/cost_collector.py +141 -0
- tools/cloudforge/finops/optimizer.py +55 -0
- tools/cloudforge/hybrid/__init__.py +2 -0
- tools/cloudforge/hybrid/connection_manager.py +141 -0
- tools/cloudforge/hybrid/dns_federator.py +56 -0
- tools/cloudforge/hybrid/health_monitor.py +108 -0
- tools/cloudforge/hybrid/identity_federator.py +53 -0
- tools/cloudforge/hybrid/network_bridge.py +68 -0
- tools/cloudforge/hybrid/topology_manager.py +147 -0
- tools/cloudforge/hybrid/workload_abstractor.py +92 -0
- tools/cloudforge/iac/__init__.py +2 -0
- tools/cloudforge/iac/drift_detector.py +154 -0
- tools/cloudforge/iac/module_library.py +265 -0
- tools/cloudforge/iac/opentofu_adapter.py +89 -0
- tools/cloudforge/iac/pulumi_renderer.py +292 -0
- tools/cloudforge/iac/state_backend.py +146 -0
- tools/cloudforge/iac/terraform_renderer.py +626 -0
- tools/cloudforge/landing_zone/__init__.py +2 -0
- tools/cloudforge/landing_zone/blueprint_loader.py +98 -0
- tools/cloudforge/landing_zone/blueprint_validator.py +113 -0
- tools/cloudforge/landing_zone/zone_provisioner.py +306 -0
- tools/cloudforge/landing_zone/zone_state.py +143 -0
- tools/cloudforge/mbse_thread/__init__.py +2 -0
- tools/cloudforge/mbse_thread/ato_thread_weaver.py +111 -0
- tools/cloudforge/mbse_thread/control_tracer.py +68 -0
- tools/cloudforge/mbse_thread/system_boundary.py +83 -0
- tools/cloudforge/metastore/__init__.py +2 -0
- tools/cloudforge/metastore/dependency_graph.py +202 -0
- tools/cloudforge/metastore/discovery.py +192 -0
- tools/cloudforge/metastore/registry.py +185 -0
- tools/cloudforge/metastore/rto_tracker.py +92 -0
- tools/cloudforge/metastore/runbook_linker.py +82 -0
- tools/cloudforge/migration/__init__.py +2 -0
- tools/cloudforge/migration/assessor.py +187 -0
- tools/cloudforge/migration/cutover_orchestrator.py +117 -0
- tools/cloudforge/migration/databridge_bridge.py +92 -0
- tools/cloudforge/migration/planner.py +98 -0
- tools/cloudforge/migration/risk_scorer.py +97 -0
- tools/cloudforge/migration/validation_runner.py +45 -0
- tools/cloudforge/migration/workload_inventory.py +107 -0
- tools/cloudforge/provider.py +319 -0
- tools/cloudforge/providers/__init__.py +2 -0
- tools/cloudforge/providers/aws_commercial.py +92 -0
- tools/cloudforge/providers/aws_govcloud.py +229 -0
- tools/cloudforge/providers/aws_secret.py +83 -0
- tools/cloudforge/providers/azure_commercial.py +80 -0
- tools/cloudforge/providers/azure_gov.py +91 -0
- tools/cloudforge/providers/azure_secret.py +71 -0
- tools/cloudforge/providers/gcp.py +102 -0
- tools/cloudforge/providers/oci.py +102 -0
- tools/cloudforge/registry.py +140 -0
- tools/cloudforge/runbooks/__init__.py +2 -0
- tools/cloudforge/runbooks/ai_generator.py +119 -0
- tools/cloudforge/runbooks/dag_validator.py +219 -0
- tools/cloudforge/runbooks/engine.py +470 -0
- tools/cloudforge/runbooks/models.py +99 -0
- tools/cloudforge/runbooks/snippet_library.py +158 -0
- tools/cloudforge/runbooks/template_loader.py +122 -0
- tools/cloudforge/runbooks/visualization.py +108 -0
- tools/cloudforge/siem/__init__.py +2 -0
- tools/cloudforge/siem/alert_rules.py +86 -0
- tools/cloudforge/siem/correlation_engine.py +61 -0
- tools/cloudforge/siem/log_aggregator.py +113 -0
- tools/cloudforge/siem/siem_dashboard_data.py +28 -0
- tools/cloudforge/supply_chain/__init__.py +2 -0
- tools/cloudforge/supply_chain/bridge.py +33 -0
- tools/cloudforge/supply_chain/iac_dependency_scanner.py +36 -0
- tools/cloudforge/supply_chain/provider_trust_scorer.py +54 -0
- tools/compat/__init__.py +21 -0
- tools/compat/cli_harmonizer.py +251 -0
- tools/compat/datetime_utils.py +18 -0
- tools/compat/db_utils.py +190 -0
- tools/compat/platform_utils.py +123 -0
- tools/compliance/__init__.py +1 -0
- tools/compliance/accountability_manager.py +391 -0
- tools/compliance/ai_accountability_audit.py +287 -0
- tools/compliance/ai_impact_assessor.py +267 -0
- tools/compliance/ai_incident_response.py +295 -0
- tools/compliance/ai_inventory_manager.py +233 -0
- tools/compliance/ai_reassessment_scheduler.py +250 -0
- tools/compliance/ai_transparency_audit.py +247 -0
- tools/compliance/atlas_assessor.py +276 -0
- tools/compliance/atlas_report_generator.py +1199 -0
- tools/compliance/base_assessor.py +591 -0
- tools/compliance/cato_live_engine.py +607 -0
- tools/compliance/cato_monitor.py +1371 -0
- tools/compliance/cato_scheduler.py +698 -0
- tools/compliance/cjis_assessor.py +76 -0
- tools/compliance/classification_manager.py +1340 -0
- tools/compliance/cmmc_assessor.py +1478 -0
- tools/compliance/cmmc_report_generator.py +1087 -0
- tools/compliance/compliance_detector.py +452 -0
- tools/compliance/compliance_exporter.py +418 -0
- tools/compliance/compliance_status.py +810 -0
- tools/compliance/control_mapper.py +488 -0
- tools/compliance/crosswalk_engine.py +1208 -0
- tools/compliance/cssp_assessor.py +1032 -0
- tools/compliance/cssp_evidence_collector.py +716 -0
- tools/compliance/cssp_report_generator.py +1103 -0
- tools/compliance/cui_marker.py +387 -0
- tools/compliance/diagram_validator.py +599 -0
- tools/compliance/emass/__init__.py +2 -0
- tools/compliance/emass/emass_client.py +822 -0
- tools/compliance/emass/emass_export.py +758 -0
- tools/compliance/emass/emass_sync.py +807 -0
- tools/compliance/eu_ai_act_classifier.py +193 -0
- tools/compliance/evidence_collector.py +459 -0
- tools/compliance/fairness_assessor.py +310 -0
- tools/compliance/fedramp_20x_ksi_emitter.py +692 -0
- tools/compliance/fedramp_assessor.py +1795 -0
- tools/compliance/fedramp_authorization_packager.py +137 -0
- tools/compliance/fedramp_ksi_generator.py +349 -0
- tools/compliance/fedramp_report_generator.py +1115 -0
- tools/compliance/fips199_categorizer.py +869 -0
- tools/compliance/fips200_validator.py +304 -0
- tools/compliance/firmware_sbom.py +646 -0
- tools/compliance/gao_ai_assessor.py +228 -0
- tools/compliance/gao_evidence_builder.py +302 -0
- tools/compliance/hipaa_assessor.py +78 -0
- tools/compliance/hitrust_assessor.py +49 -0
- tools/compliance/incident_response_plan.py +705 -0
- tools/compliance/inheritance_engine.py +693 -0
- tools/compliance/iso27001_assessor.py +92 -0
- tools/compliance/iso42001_assessor.py +114 -0
- tools/compliance/ivv_assessor.py +2314 -0
- tools/compliance/ivv_report_generator.py +1649 -0
- tools/compliance/model_card_generator.py +291 -0
- tools/compliance/mosa_assessor.py +117 -0
- tools/compliance/multi_regime_assessor.py +441 -0
- tools/compliance/narrative_generator.py +1012 -0
- tools/compliance/narrative_quality_gate.py +701 -0
- tools/compliance/narrative_workflow.py +814 -0
- tools/compliance/nist_800_207_assessor.py +191 -0
- tools/compliance/nist_ai_600_1_assessor.py +185 -0
- tools/compliance/nist_ai_rmf_assessor.py +110 -0
- tools/compliance/nist_lookup.py +244 -0
- tools/compliance/omb_m25_21_assessor.py +225 -0
- tools/compliance/omb_m26_04_assessor.py +185 -0
- tools/compliance/oscal_catalog_adapter.py +395 -0
- tools/compliance/oscal_generator.py +2157 -0
- tools/compliance/oscal_tools.py +1182 -0
- tools/compliance/oscal_validator.py +692 -0
- tools/compliance/owasp_agentic_assessor.py +227 -0
- tools/compliance/owasp_asi_assessor.py +197 -0
- tools/compliance/owasp_llm_assessor.py +245 -0
- tools/compliance/pci_dss_assessor.py +80 -0
- tools/compliance/pi_compliance_tracker.py +1447 -0
- tools/compliance/poam_generator.py +388 -0
- tools/compliance/resolve_marking.py +272 -0
- tools/compliance/sbd_assessor.py +2070 -0
- tools/compliance/sbd_report_generator.py +1223 -0
- tools/compliance/sbom_generator.py +993 -0
- tools/compliance/siem_config_generator.py +661 -0
- tools/compliance/slsa_attestation_generator.py +479 -0
- tools/compliance/soc2_assessor.py +77 -0
- tools/compliance/ssp_generator.py +556 -0
- tools/compliance/stig_checker.py +712 -0
- tools/compliance/swft_evidence_bundler.py +326 -0
- tools/compliance/system_card_generator.py +303 -0
- tools/compliance/template_exchange.py +513 -0
- tools/compliance/traceability_matrix.py +1268 -0
- tools/compliance/universal_classification_manager.py +1159 -0
- tools/compliance/xacta/__init__.py +2 -0
- tools/compliance/xacta/xacta_client.py +438 -0
- tools/compliance/xacta/xacta_export.py +546 -0
- tools/compliance/xacta/xacta_sync.py +322 -0
- tools/compliance/xai_assessor.py +231 -0
- tools/core/__init__.py +2 -0
- tools/core/circuit_breaker.py +353 -0
- tools/core/compliance_sidecar.py +344 -0
- tools/core/container.py +110 -0
- tools/core/errors.py +256 -0
- tools/core/feature_flags.py +311 -0
- tools/core/task_dlq.py +350 -0
- tools/dashboard/__init__.py +2 -0
- tools/dashboard/app.py +6288 -0
- tools/dashboard/templates/agent_evolution.html +287 -0
- tools/dashboard/templates/agents/list.html +71 -0
- tools/dashboard/templates/agents.html +132 -0
- tools/dashboard/templates/architecture.html +289 -0
- tools/dashboard/templates/ato_simulator.html +170 -0
- tools/dashboard/templates/audit_engine.html +844 -0
- tools/dashboard/templates/base.html +236 -0
- tools/dashboard/templates/cato_live.html +116 -0
- tools/dashboard/templates/cloudforge.html +195 -0
- tools/dashboard/templates/cloudforge_finops.html +111 -0
- tools/dashboard/templates/cloudforge_hybrid.html +122 -0
- tools/dashboard/templates/cloudforge_metastore.html +234 -0
- tools/dashboard/templates/cloudforge_migration.html +87 -0
- tools/dashboard/templates/cloudforge_runbooks.html +201 -0
- tools/dashboard/templates/cloudforge_siem.html +94 -0
- tools/dashboard/templates/compliance_accel.html +292 -0
- tools/dashboard/templates/crashes.html +122 -0
- tools/dashboard/templates/databridge.html +305 -0
- tools/dashboard/templates/databridge_analytics.html +195 -0
- tools/dashboard/templates/databridge_mapping.html +345 -0
- tools/dashboard/templates/databridge_messaging.html +321 -0
- tools/dashboard/templates/decisions.html +258 -0
- tools/dashboard/templates/devices.html +151 -0
- tools/dashboard/templates/devsecops_maturity.html +278 -0
- tools/dashboard/templates/edge_ai.html +128 -0
- tools/dashboard/templates/firmware.html +120 -0
- tools/dashboard/templates/firmware_sbom.html +193 -0
- tools/dashboard/templates/forge_hub.html +196 -0
- tools/dashboard/templates/forge_studio.html +379 -0
- tools/dashboard/templates/forge_studio_analytics.html +360 -0
- tools/dashboard/templates/forge_studio_builder.html +1637 -0
- tools/dashboard/templates/forge_studio_compliance.html +310 -0
- tools/dashboard/templates/forge_studio_deploy.html +573 -0
- tools/dashboard/templates/forge_studio_enterprise.html +888 -0
- tools/dashboard/templates/forge_studio_marketplace.html +502 -0
- tools/dashboard/templates/forge_studio_workflow.html +696 -0
- tools/dashboard/templates/golden_path.html +175 -0
- tools/dashboard/templates/govcon.html +280 -0
- tools/dashboard/templates/harness.html +148 -0
- tools/dashboard/templates/index.html +207 -0
- tools/dashboard/templates/intelligence.html +336 -0
- tools/dashboard/templates/knowledge/index.html +190 -0
- tools/dashboard/templates/knowledge_graph.html +739 -0
- tools/dashboard/templates/login.html +51 -0
- tools/dashboard/templates/marketplace.html +336 -0
- tools/dashboard/templates/marketplace_admin.html +247 -0
- tools/dashboard/templates/missions.html +403 -0
- tools/dashboard/templates/narratives.html +154 -0
- tools/dashboard/templates/pr_intelligence.html +151 -0
- tools/dashboard/templates/proposals/detail.html +300 -0
- tools/dashboard/templates/proposals/list.html +52 -0
- tools/dashboard/templates/proposals/sam_detail.html +132 -0
- tools/dashboard/templates/proposals/section_detail.html +375 -0
- tools/dashboard/templates/research.html +222 -0
- tools/dashboard/templates/resilience.html +300 -0
- tools/dashboard/templates/scorecard.html +162 -0
- tools/dashboard/templates/simulator.html +131 -0
- tools/dashboard/templates/template_exchange.html +147 -0
- tools/dashboard/templates/thread_heatmap.html +151 -0
- tools/dashboard/templates/threat_model.html +195 -0
- tools/dashboard/templates/vsm.html +141 -0
- tools/dashboard/templates/writeguard.html +277 -0
- tools/databridge/__init__.py +5 -0
- tools/databridge/agent/__init__.py +2 -0
- tools/databridge/agent/daemon.py +227 -0
- tools/databridge/agent/tunnel.py +101 -0
- tools/databridge/agent/ws_relay.py +91 -0
- tools/databridge/analytics.py +167 -0
- tools/databridge/arrow_pipeline.py +327 -0
- tools/databridge/connection_manager.py +424 -0
- tools/databridge/connector.py +331 -0
- tools/databridge/connectors/__init__.py +2 -0
- tools/databridge/connectors/argocd_connector.py +160 -0
- tools/databridge/connectors/avro_connector.py +203 -0
- tools/databridge/connectors/azure_blob.py +63 -0
- tools/databridge/connectors/cdc_connector.py +205 -0
- tools/databridge/connectors/csv_connector.py +172 -0
- tools/databridge/connectors/datadog_connector.py +153 -0
- tools/databridge/connectors/discord_messaging.py +215 -0
- tools/databridge/connectors/dynamics365.py +151 -0
- tools/databridge/connectors/elasticsearch_connector.py +145 -0
- tools/databridge/connectors/email_base.py +114 -0
- tools/databridge/connectors/excel_connector.py +175 -0
- tools/databridge/connectors/fsspec_base.py +300 -0
- tools/databridge/connectors/gcs.py +53 -0
- tools/databridge/connectors/github_connector.py +138 -0
- tools/databridge/connectors/gitlab_connector.py +132 -0
- tools/databridge/connectors/gmail_connector.py +182 -0
- tools/databridge/connectors/hdfs.py +57 -0
- tools/databridge/connectors/health_base.py +401 -0
- tools/databridge/connectors/hubspot.py +124 -0
- tools/databridge/connectors/imap_connector.py +171 -0
- tools/databridge/connectors/jenkins_connector.py +138 -0
- tools/databridge/connectors/jira_connector.py +86 -0
- tools/databridge/connectors/json_connector.py +184 -0
- tools/databridge/connectors/kafka_connector.py +246 -0
- tools/databridge/connectors/kinesis_connector.py +238 -0
- tools/databridge/connectors/local_fs.py +30 -0
- tools/databridge/connectors/matrix.py +197 -0
- tools/databridge/connectors/mattermost_messaging.py +184 -0
- tools/databridge/connectors/messaging_base.py +172 -0
- tools/databridge/connectors/mssql.py +63 -0
- tools/databridge/connectors/mysql.py +57 -0
- tools/databridge/connectors/netsuite.py +170 -0
- tools/databridge/connectors/o365_mail.py +196 -0
- tools/databridge/connectors/oracle.py +65 -0
- tools/databridge/connectors/pagerduty_connector.py +162 -0
- tools/databridge/connectors/parquet_connector.py +131 -0
- tools/databridge/connectors/postgresql.py +58 -0
- tools/databridge/connectors/s3.py +65 -0
- tools/databridge/connectors/saas_base.py +198 -0
- tools/databridge/connectors/salesforce.py +126 -0
- tools/databridge/connectors/sap.py +89 -0
- tools/databridge/connectors/servicenow.py +60 -0
- tools/databridge/connectors/signal_messaging.py +150 -0
- tools/databridge/connectors/slack_messaging.py +203 -0
- tools/databridge/connectors/smtp_connector.py +126 -0
- tools/databridge/connectors/soap_base.py +258 -0
- tools/databridge/connectors/splunk_connector.py +171 -0
- tools/databridge/connectors/sql_base.py +310 -0
- tools/databridge/connectors/sqlite_connector.py +76 -0
- tools/databridge/connectors/teams.py +148 -0
- tools/databridge/connectors/telegram.py +192 -0
- tools/databridge/connectors/whatsapp.py +137 -0
- tools/databridge/data_profiler.py +99 -0
- tools/databridge/forge/__init__.py +6 -0
- tools/databridge/forge/base_selector.py +150 -0
- tools/databridge/forge/code_generator.py +206 -0
- tools/databridge/forge/community_hub.py +539 -0
- tools/databridge/forge/forge_agent.py +306 -0
- tools/databridge/forge/import_handler.py +133 -0
- tools/databridge/forge/integration_tester.py +127 -0
- tools/databridge/forge/marketplace_publisher.py +164 -0
- tools/databridge/forge/promoter.py +159 -0
- tools/databridge/forge/sandbox_manager.py +257 -0
- tools/databridge/forge/spec_parser.py +358 -0
- tools/databridge/forge/static_validator.py +363 -0
- tools/databridge/forge/templates/__init__.py +591 -0
- tools/databridge/format_converter.py +188 -0
- tools/databridge/mapping_engine.py +348 -0
- tools/databridge/messaging/__init__.py +5 -0
- tools/databridge/messaging/agent_bridge.py +254 -0
- tools/databridge/messaging/message_envelope.py +111 -0
- tools/databridge/messaging/message_logger.py +204 -0
- tools/databridge/messaging/messaging_daemon.py +326 -0
- tools/databridge/messaging/oauth2_manager.py +411 -0
- tools/databridge/pii_detector.py +221 -0
- tools/databridge/registry.py +352 -0
- tools/databridge/relay_server.py +105 -0
- tools/databridge/scale/__init__.py +16 -0
- tools/databridge/scale/backpressure.py +134 -0
- tools/databridge/scale/chunked_pipeline.py +169 -0
- tools/databridge/scale/connection_pool.py +293 -0
- tools/databridge/scale/engine.py +492 -0
- tools/databridge/scale/worker_pool.py +140 -0
- tools/databridge/scale/write_batcher.py +250 -0
- tools/databridge/schema_engine.py +324 -0
- tools/databridge/stream_manager.py +225 -0
- tools/databridge/sync_engine.py +411 -0
- tools/databridge/transforms.py +302 -0
- tools/db/__init__.py +1 -0
- tools/db/backup.py +312 -0
- tools/db/backup_manager.py +832 -0
- tools/db/init_icdev_db.py +7753 -0
- tools/db/init_sparkpilot_db.py +431 -0
- tools/db/migrate.py +177 -0
- tools/db/migrate_innovation_audit.py +165 -0
- tools/db/migration_runner.py +548 -0
- tools/db/migrations/001_baseline/meta.json +9 -0
- tools/db/migrations/001_baseline/up.py +67 -0
- tools/db/migrations/002_memory_enhancements/down.sql +8 -0
- tools/db/migrations/002_memory_enhancements/meta.json +9 -0
- tools/db/migrations/002_memory_enhancements/up.py +119 -0
- tools/db/migrations/003_dev_profiles/meta.json +8 -0
- tools/db/migrations/003_dev_profiles/up.py +93 -0
- tools/db/migrations/004_innovation_engine/down.py +19 -0
- tools/db/migrations/004_innovation_engine/up.py +227 -0
- tools/db/migrations/005_phase_37_ai_security/down.py +19 -0
- tools/db/migrations/005_phase_37_ai_security/up.py +257 -0
- tools/db/migrations/006_phase_36_evolution/down.py +21 -0
- tools/db/migrations/006_phase_36_evolution/up.py +323 -0
- tools/db/migrations/007_phase_38_cloud/down.py +14 -0
- tools/db/migrations/007_phase_38_cloud/up.py +110 -0
- tools/db/migrations/008_phase36_37_integration/up.py +55 -0
- tools/db/migrations/__init__.py +2 -0
- tools/db/pg_migrate.py +642 -0
- tools/db/storage.py +1080 -0
- tools/decisions/__init__.py +2 -0
- tools/decisions/dmn_engine.py +695 -0
- tools/devsecops/__init__.py +2 -0
- tools/devsecops/attestation_manager.py +449 -0
- tools/devsecops/network_segmentation_generator.py +604 -0
- tools/devsecops/pdp_config_generator.py +1246 -0
- tools/devsecops/pipeline_security_generator.py +475 -0
- tools/devsecops/policy_generator.py +644 -0
- tools/devsecops/profile_manager.py +374 -0
- tools/devsecops/service_mesh_generator.py +1063 -0
- tools/devsecops/zta_maturity_scorer.py +355 -0
- tools/devsecops/zta_terraform_generator.py +1301 -0
- tools/edge_ai/__init__.py +2 -0
- tools/edge_ai/model_manager.py +200 -0
- tools/embedded/__init__.py +2 -0
- tools/embedded/cmake_generator.py +318 -0
- tools/embedded/crash_analyzer.py +191 -0
- tools/embedded/nl_to_firmware.py +277 -0
- tools/events/__init__.py +1 -0
- tools/events/event_bus.py +199 -0
- tools/finetune/pair_generator.py +832 -0
- tools/fleet/__init__.py +2 -0
- tools/fleet/device_registry.py +148 -0
- tools/fleet/ota_manager.py +153 -0
- tools/forge_studio/__init__.py +13 -0
- tools/forge_studio/analytics/__init__.py +0 -0
- tools/forge_studio/analytics/process_miner.py +383 -0
- tools/forge_studio/audit.py +183 -0
- tools/forge_studio/blueprint/__init__.py +2 -0
- tools/forge_studio/blueprint/build_tracker.py +317 -0
- tools/forge_studio/blueprint/export_engine.py +441 -0
- tools/forge_studio/blueprint/parent_client.py +335 -0
- tools/forge_studio/catalog/__init__.py +2 -0
- tools/forge_studio/catalog/component_registry.py +176 -0
- tools/forge_studio/catalog/schema_validator.py +193 -0
- tools/forge_studio/compliance/__init__.py +1 -0
- tools/forge_studio/compliance/compliance_wiring.py +554 -0
- tools/forge_studio/deploy/__init__.py +1 -0
- tools/forge_studio/deploy/airgap_packager.py +466 -0
- tools/forge_studio/deploy/deploy_engine.py +1792 -0
- tools/forge_studio/deploy/env_manager.py +431 -0
- tools/forge_studio/eject/__init__.py +2 -0
- tools/forge_studio/eject/docker_compose_generator.py +237 -0
- tools/forge_studio/eject/eject_engine.py +230 -0
- tools/forge_studio/eject/expo_scaffolder.py +303 -0
- tools/forge_studio/eject/nextjs_scaffolder.py +338 -0
- tools/forge_studio/enterprise/__init__.py +0 -0
- tools/forge_studio/enterprise/custom_frameworks.py +826 -0
- tools/forge_studio/enterprise/hardening_engine.py +1530 -0
- tools/forge_studio/enterprise/sso_manager.py +718 -0
- tools/forge_studio/enterprise/whitelabel_engine.py +887 -0
- tools/forge_studio/formula/__init__.py +0 -0
- tools/forge_studio/formula/expression_engine.py +562 -0
- tools/forge_studio/formula/formula_registry.py +265 -0
- tools/forge_studio/generator/__init__.py +2 -0
- tools/forge_studio/generator/app_generator.py +584 -0
- tools/forge_studio/generator/complexity_detector.py +368 -0
- tools/forge_studio/generator/prompt_templates.py +104 -0
- tools/forge_studio/generator/spec_builder.py +192 -0
- tools/forge_studio/intake_bridge.py +898 -0
- tools/forge_studio/marketplace/__init__.py +0 -0
- tools/forge_studio/marketplace/component_hub.py +428 -0
- tools/forge_studio/models.py +369 -0
- tools/forge_studio/renderer/__init__.py +2 -0
- tools/forge_studio/renderer/json_render_engine.py +623 -0
- tools/forge_studio/renderer/layout_engine.py +214 -0
- tools/forge_studio/renderer/rn_component_map.py +182 -0
- tools/forge_studio/supabase/__init__.py +2 -0
- tools/forge_studio/supabase/auth_generator.py +283 -0
- tools/forge_studio/supabase/migration_generator.py +93 -0
- tools/forge_studio/supabase/schema_generator.py +281 -0
- tools/forge_studio/tenant_manager.py +387 -0
- tools/forge_studio/workflow/__init__.py +2 -0
- tools/forge_studio/workflow/bpmn_adapter.py +489 -0
- tools/govcon/draft_orchestrator.py +1151 -0
- tools/govcon/engine_enrichment.py +373 -0
- tools/govcon/knowledge_base.py +487 -0
- tools/govcon/knowledge_ingestion.py +510 -0
- tools/govcon/sam_scanner.py +754 -0
- tools/harness/__init__.py +6 -0
- tools/harness/exit_criteria_evaluator.py +231 -0
- tools/harness/maturity_assessor.py +347 -0
- tools/harness/scaffold_harness.py +416 -0
- tools/harness/trace_analyzer.py +281 -0
- tools/infra/__init__.py +1 -0
- tools/infra/ansible_generator.py +867 -0
- tools/infra/dockerfile_generator.py +359 -0
- tools/infra/infra_status.py +384 -0
- tools/infra/ironbank_metadata_generator.py +403 -0
- tools/infra/k8s_generator.py +1000 -0
- tools/infra/pipeline_generator.py +830 -0
- tools/infra/rollback.py +389 -0
- tools/infra/terraform_generator.py +1140 -0
- tools/infra/terraform_generator_azure.py +1252 -0
- tools/infra/terraform_generator_gcp.py +951 -0
- tools/infra/terraform_generator_ibm.py +359 -0
- tools/infra/terraform_generator_oci.py +918 -0
- tools/infra/terraform_generator_onprem.py +318 -0
- tools/knowledge/__init__.py +1 -0
- tools/knowledge/knowledge_ingest.py +281 -0
- tools/knowledge/pattern_detector.py +681 -0
- tools/knowledge/recommendation_engine.py +449 -0
- tools/knowledge/self_heal_analyzer.py +492 -0
- tools/knowledge_graph/__init__.py +2 -0
- tools/knowledge_graph/graph_rag.py +498 -0
- tools/knowledge_graph/ingester.py +406 -0
- tools/knowledge_graph/insight_generator.py +369 -0
- tools/knowledge_graph/text_network.py +832 -0
- tools/llm/__init__.py +72 -0
- tools/llm/anthropic_provider.py +170 -0
- tools/llm/azure_openai_provider.py +338 -0
- tools/llm/bedrock_provider.py +315 -0
- tools/llm/embedding_provider.py +438 -0
- tools/llm/gemini_provider.py +381 -0
- tools/llm/ibm_watsonx_provider.py +231 -0
- tools/llm/oci_genai_provider.py +462 -0
- tools/llm/ollama_provider.py +350 -0
- tools/llm/openai_provider.py +225 -0
- tools/llm/prompt_registry.py +447 -0
- tools/llm/provider.py +355 -0
- tools/llm/provider_sdk.py +175 -0
- tools/llm/router.py +1124 -0
- tools/llm/semantic_cache.py +394 -0
- tools/llm/vertex_ai_provider.py +374 -0
- tools/maintenance/__init__.py +2 -0
- tools/maintenance/dependency_scanner.py +1016 -0
- tools/maintenance/maintenance_auditor.py +804 -0
- tools/maintenance/remediation_engine.py +957 -0
- tools/maintenance/vulnerability_checker.py +978 -0
- tools/manifest.md +1066 -0
- tools/marketplace/asset_installer.py +639 -0
- tools/marketplace/feedback_validator.py +359 -0
- tools/marketplace/license_client.py +458 -0
- tools/marketplace/module_crypto.py +544 -0
- tools/marketplace/module_runtime.py +236 -0
- tools/marketplace/token_store.py +264 -0
- tools/mbse/__init__.py +3 -0
- tools/mbse/des_assessor.py +1173 -0
- tools/mbse/des_report_generator.py +787 -0
- tools/mbse/diagram_extractor.py +792 -0
- tools/mbse/digital_thread.py +1650 -0
- tools/mbse/model_code_generator.py +1115 -0
- tools/mbse/model_control_mapper.py +410 -0
- tools/mbse/pi_model_tracker.py +1079 -0
- tools/mbse/reqif_parser.py +1468 -0
- tools/mbse/sync_engine.py +1789 -0
- tools/mbse/thread_heatmap.py +445 -0
- tools/mbse/xmi_parser.py +1558 -0
- tools/mcp/builder_server.py +64 -0
- tools/mcp/compliance_server.py +64 -0
- tools/mcp/connector_forge_server.py +155 -0
- tools/mcp/core_server.py +64 -0
- tools/mcp/devsecops_server.py +11 -0
- tools/mcp/devsecops_zta_server.py +64 -0
- tools/mcp/knowledge_server.py +64 -0
- tools/mcp/monitor_server.py +64 -0
- tools/mcp/ops_server.py +300 -0
- tools/mcp/requirements_analyst_server.py +64 -0
- tools/mcp/requirements_server.py +11 -0
- tools/mcp/security_server.py +64 -0
- tools/mcp/simulation_server.py +64 -0
- tools/mcp/supply_chain_server.py +64 -0
- tools/mcp/tool_registry.py +299 -0
- tools/memory/__init__.py +2 -0
- tools/memory/auto_capture.py +346 -0
- tools/memory/embed_memory.py +157 -0
- tools/memory/history_compressor.py +334 -0
- tools/memory/hybrid_search.py +235 -0
- tools/memory/maintenance_cron.py +288 -0
- tools/memory/memory_consolidation.py +439 -0
- tools/memory/memory_db.py +132 -0
- tools/memory/memory_read.py +101 -0
- tools/memory/memory_write.py +221 -0
- tools/memory/semantic_search.py +138 -0
- tools/memory/time_decay.py +434 -0
- tools/missions/__init__.py +2 -0
- tools/missions/mission_engine.py +459 -0
- tools/monitor/__init__.py +1 -0
- tools/monitor/alert_correlator.py +486 -0
- tools/monitor/auto_resolver.py +603 -0
- tools/monitor/health_checker.py +507 -0
- tools/monitor/heartbeat_daemon.py +779 -0
- tools/monitor/log_analyzer.py +507 -0
- tools/monitor/metric_collector.py +484 -0
- tools/mosa/__init__.py +10 -0
- tools/mosa/icd_generator.py +358 -0
- tools/mosa/modular_design_analyzer.py +682 -0
- tools/mosa/mosa_code_enforcer.py +348 -0
- tools/mosa/tsp_generator.py +265 -0
- tools/observability/__init__.py +100 -0
- tools/observability/genai_attributes.py +88 -0
- tools/observability/instrumentation.py +140 -0
- tools/observability/mlflow_exporter.py +193 -0
- tools/observability/otel_tracer.py +168 -0
- tools/observability/provenance/__init__.py +3 -0
- tools/observability/provenance/prov_recorder.py +322 -0
- tools/observability/shap/__init__.py +3 -0
- tools/observability/shap/agent_shap.py +274 -0
- tools/observability/sqlite_tracer.py +360 -0
- tools/observability/trace_context.py +205 -0
- tools/observability/tracer.py +230 -0
- tools/orchestration/__init__.py +1 -0
- tools/orchestration/peer_channels.py +254 -0
- tools/orchestration/saga_coordinator.py +390 -0
- tools/project/__init__.py +1 -0
- tools/project/manifest_loader.py +418 -0
- tools/project/project_create.py +350 -0
- tools/project/project_list.py +171 -0
- tools/project/project_scaffold.py +1715 -0
- tools/project/project_status.py +478 -0
- tools/project/session_context_builder.py +752 -0
- tools/project/validate_manifest.py +54 -0
- tools/rag/corrective_rag.py +582 -0
- tools/rag/source_registry.py +482 -0
- tools/requirements/__init__.py +1 -0
- tools/requirements/ai_governance_scorer.py +207 -0
- tools/requirements/boundary_analyzer.py +1281 -0
- tools/requirements/clarification_engine.py +605 -0
- tools/requirements/complexity_scorer.py +369 -0
- tools/requirements/consistency_analyzer.py +789 -0
- tools/requirements/constitution_manager.py +592 -0
- tools/requirements/decomposition_engine.py +764 -0
- tools/requirements/document_extractor.py +1002 -0
- tools/requirements/elicitation_techniques.py +508 -0
- tools/requirements/gap_detector.py +260 -0
- tools/requirements/intake_engine.py +2175 -0
- tools/requirements/prd_generator.py +839 -0
- tools/requirements/prd_validator.py +584 -0
- tools/requirements/readiness_scorer.py +302 -0
- tools/requirements/spec_organizer.py +1015 -0
- tools/requirements/spec_quality_checker.py +1083 -0
- tools/requirements/traceability_builder.py +566 -0
- tools/research/__init__.py +3 -0
- tools/research/academic_scanner.py +130 -0
- tools/research/build_buy_analyzer.py +229 -0
- tools/research/challenge_scorer.py +280 -0
- tools/research/community_scanner.py +174 -0
- tools/research/cross_engine_bridge.py +124 -0
- tools/research/dossier_generator.py +305 -0
- tools/research/landscape_scanner.py +315 -0
- tools/research/regulatory_scanner.py +248 -0
- tools/research/research_manager.py +469 -0
- tools/research/source_scanner.py +150 -0
- tools/research/vertical_loader.py +118 -0
- tools/saas/__init__.py +0 -0
- tools/saas/licensing/__init__.py +0 -0
- tools/saas/licensing/license_validator.py +345 -0
- tools/scaffold/__init__.py +2 -0
- tools/scaffold/golden_path.py +504 -0
- tools/security/__init__.py +1 -0
- tools/security/agent_output_validator.py +330 -0
- tools/security/agent_trust_scorer.py +652 -0
- tools/security/ai_bom_generator.py +718 -0
- tools/security/ai_telemetry_logger.py +469 -0
- tools/security/atlas_red_team.py +541 -0
- tools/security/code_pattern_scanner.py +382 -0
- tools/security/confabulation_detector.py +265 -0
- tools/security/container_scanner.py +489 -0
- tools/security/dependency_auditor.py +942 -0
- tools/security/endpoint_security_scanner.py +626 -0
- tools/security/mcp_tool_authorizer.py +242 -0
- tools/security/output_verifier.py +427 -0
- tools/security/prompt_injection_detector.py +737 -0
- tools/security/sast_runner.py +946 -0
- tools/security/secret_detector.py +376 -0
- tools/security/threat_modeler.py +678 -0
- tools/security/tool_chain_validator.py +357 -0
- tools/security/vuln_scanner.py +536 -0
- tools/simulation/__init__.py +2 -0
- tools/simulation/ato_simulator.py +517 -0
- tools/simulation/coa_generator.py +1539 -0
- tools/simulation/monte_carlo.py +745 -0
- tools/simulation/scenario_manager.py +1060 -0
- tools/simulation/simulation_engine.py +1091 -0
- tools/simulator/__init__.py +2 -0
- tools/simulator/sim_runner.py +272 -0
- tools/supply_chain/__init__.py +2 -0
- tools/supply_chain/cve_triager.py +690 -0
- tools/supply_chain/dependency_graph.py +630 -0
- tools/supply_chain/isa_manager.py +526 -0
- tools/supply_chain/scrm_assessor.py +531 -0
- tools/supply_chain/slsa_verifier.py +473 -0
- tools/testing/__init__.py +2 -0
- tools/testing/acceptance_validator.py +411 -0
- tools/testing/api_surface_extractor.py +749 -0
- tools/testing/claude_dir_validator.py +831 -0
- tools/testing/data_types.py +199 -0
- tools/testing/e2e_runner.py +715 -0
- tools/testing/fuzz_cli.py +306 -0
- tools/testing/health_check.py +483 -0
- tools/testing/platform_check.py +143 -0
- tools/testing/production_audit.py +1836 -0
- tools/testing/production_remediate.py +803 -0
- tools/testing/screenshot_validator.py +538 -0
- tools/testing/smoke_test.py +283 -0
- tools/testing/test_agent_models.py +117 -0
- tools/testing/test_orchestrator.py +957 -0
- tools/testing/utils.py +229 -0
- tools/writeguard/__init__.py +1 -0
- tools/writeguard/main.py +1 -0
- tools/writing/__init__.py +7 -0
- tools/writing/ai_content_detector.py +316 -0
- tools/writing/analysis_engine.py +454 -0
- tools/writing/batch_analyzer.py +276 -0
- tools/writing/coherence_analyzer.py +221 -0
- tools/writing/govcon_bridge.py +509 -0
- tools/writing/grammar_checker.py +270 -0
- tools/writing/plagiarism_detector.py +106 -0
- tools/writing/readability_scorer.py +201 -0
- tools/writing/rewriter.py +96 -0
- tools/writing/signal_registrar.py +167 -0
- tools/writing/snippet_manager.py +276 -0
- tools/writing/style_enforcer.py +220 -0
- tools/writing/style_guide_manager.py +438 -0
- tools/writing/tone_profiler.py +168 -0
|
@@ -0,0 +1,1836 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# CUI // SP-CTI
|
|
3
|
+
"""Production Readiness Audit — comprehensive pre-production validation.
|
|
4
|
+
|
|
5
|
+
Runs 38 checks across 7 categories: platform, security, compliance,
|
|
6
|
+
integration, performance, documentation, code_quality. Streams results live and
|
|
7
|
+
produces a consolidated report stored in the production_audits table.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python tools/testing/production_audit.py --human --stream
|
|
11
|
+
python tools/testing/production_audit.py --json
|
|
12
|
+
python tools/testing/production_audit.py --category security,compliance --json
|
|
13
|
+
python tools/testing/production_audit.py --gate --json
|
|
14
|
+
|
|
15
|
+
Exit codes: 0 = all blocking checks pass, 1 = at least one blocker failed.
|
|
16
|
+
|
|
17
|
+
Architecture decisions: D291-D295.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import ast
|
|
22
|
+
import dataclasses
|
|
23
|
+
import importlib
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import re
|
|
27
|
+
import subprocess
|
|
28
|
+
import sys
|
|
29
|
+
import time
|
|
30
|
+
from datetime import datetime, timezone
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
from typing import Callable, Dict, List, Optional, Tuple
|
|
33
|
+
from tools.db.storage import get_connection
|
|
34
|
+
DB_PATH = None # Storage layer handles path resolution (D-DB-20)
|
|
35
|
+
|
|
36
|
+
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from tools.compat.db_utils import get_db_connection
|
|
40
|
+
except ImportError:
|
|
41
|
+
get_db_connection = None
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# Data structures
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclasses.dataclass
|
|
49
|
+
class AuditCheck:
|
|
50
|
+
check_id: str
|
|
51
|
+
check_name: str
|
|
52
|
+
category: str
|
|
53
|
+
status: str # pass, fail, warn, skip
|
|
54
|
+
severity: str # blocking, warning
|
|
55
|
+
message: str
|
|
56
|
+
details: dict
|
|
57
|
+
duration_ms: int = 0
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> dict:
|
|
60
|
+
return dataclasses.asdict(self)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclasses.dataclass
|
|
64
|
+
class AuditReport:
|
|
65
|
+
overall_pass: bool
|
|
66
|
+
timestamp: str
|
|
67
|
+
categories: dict
|
|
68
|
+
total_checks: int
|
|
69
|
+
passed: int
|
|
70
|
+
failed: int
|
|
71
|
+
warned: int
|
|
72
|
+
skipped: int
|
|
73
|
+
blockers: list
|
|
74
|
+
warnings: list
|
|
75
|
+
duration_total_ms: int
|
|
76
|
+
|
|
77
|
+
def to_dict(self) -> dict:
|
|
78
|
+
return dataclasses.asdict(self)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Helpers
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
def _run_subprocess(cmd: list, timeout: int = 120) -> Tuple[int, str, str]:
|
|
86
|
+
"""Run a subprocess and return (returncode, stdout, stderr)."""
|
|
87
|
+
try:
|
|
88
|
+
result = subprocess.run(
|
|
89
|
+
cmd, capture_output=True, text=True, timeout=timeout,
|
|
90
|
+
stdin=subprocess.DEVNULL,
|
|
91
|
+
env={**os.environ, "PYTHONDONTWRITEBYTECODE": "1"},
|
|
92
|
+
)
|
|
93
|
+
return result.returncode, result.stdout, result.stderr
|
|
94
|
+
except FileNotFoundError:
|
|
95
|
+
return -1, "", f"Command not found: {cmd[0]}"
|
|
96
|
+
except subprocess.TimeoutExpired:
|
|
97
|
+
return -2, "", f"Command timed out after {timeout}s"
|
|
98
|
+
except Exception as e:
|
|
99
|
+
return -3, "", str(e)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _timed(fn: Callable, *args, **kwargs) -> Tuple:
|
|
103
|
+
"""Run fn and return (result, duration_ms)."""
|
|
104
|
+
start = time.time()
|
|
105
|
+
result = fn(*args, **kwargs)
|
|
106
|
+
elapsed = int((time.time() - start) * 1000)
|
|
107
|
+
return result, elapsed
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def check_python_version() -> AuditCheck:
|
|
111
|
+
"""PLT-002: Python >= 3.9 required."""
|
|
112
|
+
v = sys.version_info
|
|
113
|
+
ok = v >= (3, 9)
|
|
114
|
+
return AuditCheck(
|
|
115
|
+
check_id="PLT-002", check_name="Python Version",
|
|
116
|
+
category="platform", status="pass" if ok else "fail",
|
|
117
|
+
severity="blocking",
|
|
118
|
+
message=f"Python {v.major}.{v.minor}.{v.micro}" + ("" if ok else " — requires >= 3.9"),
|
|
119
|
+
details={"version": f"{v.major}.{v.minor}.{v.micro}", "required": "3.9"},
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def check_stdlib_modules() -> AuditCheck:
|
|
124
|
+
"""PLT-003: Required stdlib modules importable."""
|
|
125
|
+
required = ["sqlite3", "pathlib", "json", "hashlib", "argparse", "ast",
|
|
126
|
+
"dataclasses", "subprocess", "re", "uuid", "hmac"]
|
|
127
|
+
missing = []
|
|
128
|
+
for mod in required:
|
|
129
|
+
try:
|
|
130
|
+
importlib.import_module(mod)
|
|
131
|
+
except ImportError:
|
|
132
|
+
missing.append(mod)
|
|
133
|
+
ok = len(missing) == 0
|
|
134
|
+
return AuditCheck(
|
|
135
|
+
check_id="PLT-003", check_name="Required Stdlib Modules",
|
|
136
|
+
category="platform", status="pass" if ok else "fail",
|
|
137
|
+
severity="blocking",
|
|
138
|
+
message=f"All {len(required)} stdlib modules available" if ok else f"Missing: {', '.join(missing)}",
|
|
139
|
+
details={"checked": len(required), "missing": missing},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def check_platform_compat() -> AuditCheck:
|
|
144
|
+
"""PLT-001: Run platform_check.py."""
|
|
145
|
+
script = PROJECT_ROOT / "tools" / "testing" / "platform_check.py"
|
|
146
|
+
if not script.exists():
|
|
147
|
+
return AuditCheck(
|
|
148
|
+
check_id="PLT-001", check_name="Platform Compatibility",
|
|
149
|
+
category="platform", status="skip", severity="warning",
|
|
150
|
+
message="platform_check.py not found", details={},
|
|
151
|
+
)
|
|
152
|
+
rc, stdout, stderr = _run_subprocess([sys.executable, str(script), "--json"])
|
|
153
|
+
if rc == 0:
|
|
154
|
+
try:
|
|
155
|
+
data = json.loads(stdout)
|
|
156
|
+
return AuditCheck(
|
|
157
|
+
check_id="PLT-001", check_name="Platform Compatibility",
|
|
158
|
+
category="platform", status="pass", severity="warning",
|
|
159
|
+
message=f"Platform: {data.get('platform', 'unknown')}",
|
|
160
|
+
details=data,
|
|
161
|
+
)
|
|
162
|
+
except json.JSONDecodeError:
|
|
163
|
+
pass
|
|
164
|
+
return AuditCheck(
|
|
165
|
+
check_id="PLT-001", check_name="Platform Compatibility",
|
|
166
|
+
category="platform", status="warn", severity="warning",
|
|
167
|
+
message=f"platform_check returned exit {rc}", details={"stderr": stderr[:500]},
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def check_dockerfile_syntax() -> AuditCheck:
|
|
172
|
+
"""PLT-004: Parse Dockerfiles for FROM, USER, COPY."""
|
|
173
|
+
docker_dir = PROJECT_ROOT / "docker"
|
|
174
|
+
if not docker_dir.exists():
|
|
175
|
+
return AuditCheck(
|
|
176
|
+
check_id="PLT-004", check_name="Dockerfile Syntax",
|
|
177
|
+
category="platform", status="skip", severity="warning",
|
|
178
|
+
message="docker/ directory not found", details={},
|
|
179
|
+
)
|
|
180
|
+
issues = []
|
|
181
|
+
checked = 0
|
|
182
|
+
for df in sorted(docker_dir.glob("Dockerfile.*")):
|
|
183
|
+
checked += 1
|
|
184
|
+
try:
|
|
185
|
+
content = df.read_text(encoding="utf-8")
|
|
186
|
+
except (OSError, UnicodeDecodeError):
|
|
187
|
+
issues.append(f"{df.name}: unreadable")
|
|
188
|
+
continue
|
|
189
|
+
if "FROM " not in content:
|
|
190
|
+
issues.append(f"{df.name}: missing FROM instruction")
|
|
191
|
+
if "USER " not in content:
|
|
192
|
+
issues.append(f"{df.name}: missing USER (non-root required)")
|
|
193
|
+
ok = len(issues) == 0
|
|
194
|
+
return AuditCheck(
|
|
195
|
+
check_id="PLT-004", check_name="Dockerfile Syntax",
|
|
196
|
+
category="platform", status="pass" if ok else "warn",
|
|
197
|
+
severity="warning",
|
|
198
|
+
message=f"{checked} Dockerfiles checked, {len(issues)} issues" if not ok else f"{checked} Dockerfiles valid",
|
|
199
|
+
details={"checked": checked, "issues": issues},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# ---------------------------------------------------------------------------
|
|
204
|
+
# Category 2: Security (SEC-001..006)
|
|
205
|
+
# ---------------------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
def check_sast_bandit() -> AuditCheck:
|
|
208
|
+
"""SEC-001: SAST scan via bandit."""
|
|
209
|
+
rc, stdout, stderr = _run_subprocess(
|
|
210
|
+
[sys.executable, "-m", "bandit", "-r", str(PROJECT_ROOT / "tools"),
|
|
211
|
+
"-f", "json", "-q", "--severity-level", "medium"],
|
|
212
|
+
timeout=300,
|
|
213
|
+
)
|
|
214
|
+
if rc == -1:
|
|
215
|
+
return AuditCheck(
|
|
216
|
+
check_id="SEC-001", check_name="SAST Scan (Bandit)",
|
|
217
|
+
category="security", status="skip", severity="blocking",
|
|
218
|
+
message="bandit not installed (pip install bandit)", details={},
|
|
219
|
+
)
|
|
220
|
+
try:
|
|
221
|
+
data = json.loads(stdout)
|
|
222
|
+
results = data.get("results", [])
|
|
223
|
+
critical = sum(1 for r in results if r.get("issue_severity") == "HIGH" and r.get("issue_confidence") == "HIGH")
|
|
224
|
+
high = sum(1 for r in results if r.get("issue_severity") == "HIGH")
|
|
225
|
+
medium = sum(1 for r in results if r.get("issue_severity") == "MEDIUM")
|
|
226
|
+
ok = critical == 0
|
|
227
|
+
return AuditCheck(
|
|
228
|
+
check_id="SEC-001", check_name="SAST Scan (Bandit)",
|
|
229
|
+
category="security", status="pass" if ok else "fail",
|
|
230
|
+
severity="blocking",
|
|
231
|
+
message=f"{len(results)} findings (critical={critical}, high={high}, medium={medium})",
|
|
232
|
+
details={"total": len(results), "critical": critical, "high": high, "medium": medium},
|
|
233
|
+
)
|
|
234
|
+
except json.JSONDecodeError:
|
|
235
|
+
return AuditCheck(
|
|
236
|
+
check_id="SEC-001", check_name="SAST Scan (Bandit)",
|
|
237
|
+
category="security", status="warn", severity="blocking",
|
|
238
|
+
message=f"bandit output not parseable (exit {rc})",
|
|
239
|
+
details={"stderr": stderr[:500]},
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def check_dependency_audit() -> AuditCheck:
|
|
244
|
+
"""SEC-002: Dependency vulnerability audit."""
|
|
245
|
+
rc, stdout, stderr = _run_subprocess(
|
|
246
|
+
[sys.executable, "-m", "pip_audit", "--format", "json", "--progress-spinner=off"],
|
|
247
|
+
timeout=120,
|
|
248
|
+
)
|
|
249
|
+
if rc == -1:
|
|
250
|
+
return AuditCheck(
|
|
251
|
+
check_id="SEC-002", check_name="Dependency Audit",
|
|
252
|
+
category="security", status="skip", severity="blocking",
|
|
253
|
+
message="pip-audit not installed (pip install pip-audit)", details={},
|
|
254
|
+
)
|
|
255
|
+
try:
|
|
256
|
+
data = json.loads(stdout)
|
|
257
|
+
vulns = data if isinstance(data, list) else data.get("dependencies", [])
|
|
258
|
+
vuln_deps = [v for v in vulns if v.get("vulns")]
|
|
259
|
+
critical = sum(1 for v in vuln_deps for vv in v.get("vulns", []) if "CRITICAL" in str(vv).upper())
|
|
260
|
+
high = sum(1 for v in vuln_deps for vv in v.get("vulns", []) if "HIGH" in str(vv).upper())
|
|
261
|
+
ok = critical == 0 and high == 0
|
|
262
|
+
return AuditCheck(
|
|
263
|
+
check_id="SEC-002", check_name="Dependency Audit",
|
|
264
|
+
category="security", status="pass" if ok else ("fail" if critical > 0 else "warn"),
|
|
265
|
+
severity="blocking",
|
|
266
|
+
message=f"{len(vuln_deps)} vulnerable deps (critical={critical}, high={high})",
|
|
267
|
+
details={"vulnerable_count": len(vuln_deps), "critical": critical, "high": high},
|
|
268
|
+
)
|
|
269
|
+
except (json.JSONDecodeError, TypeError):
|
|
270
|
+
ok = rc == 0
|
|
271
|
+
return AuditCheck(
|
|
272
|
+
check_id="SEC-002", check_name="Dependency Audit",
|
|
273
|
+
category="security", status="pass" if ok else "warn",
|
|
274
|
+
severity="blocking",
|
|
275
|
+
message=f"pip-audit exit {rc}" + (" — no vulnerabilities" if ok else ""),
|
|
276
|
+
details={"exit_code": rc, "stderr": stderr[:500]},
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def check_secret_detection() -> AuditCheck:
|
|
281
|
+
"""SEC-003: Secret detection via detect-secrets.
|
|
282
|
+
|
|
283
|
+
Uses .secrets.baseline if present to filter known false positives.
|
|
284
|
+
Only NEW secrets (not in baseline) trigger a failure.
|
|
285
|
+
"""
|
|
286
|
+
baseline_path = PROJECT_ROOT / ".secrets.baseline"
|
|
287
|
+
cmd = [sys.executable, "-m", "detect_secrets", "scan", str(PROJECT_ROOT / "tools")]
|
|
288
|
+
rc, stdout, stderr = _run_subprocess(cmd, timeout=120)
|
|
289
|
+
if rc == -1:
|
|
290
|
+
return AuditCheck(
|
|
291
|
+
check_id="SEC-003", check_name="Secret Detection",
|
|
292
|
+
category="security", status="skip", severity="blocking",
|
|
293
|
+
message="detect-secrets not installed (pip install detect-secrets)", details={},
|
|
294
|
+
)
|
|
295
|
+
try:
|
|
296
|
+
scan_data = json.loads(stdout)
|
|
297
|
+
scan_results = scan_data.get("results", {})
|
|
298
|
+
|
|
299
|
+
# Filter against baseline — only count NEW secrets not in baseline
|
|
300
|
+
baseline_results = {}
|
|
301
|
+
if baseline_path.exists():
|
|
302
|
+
try:
|
|
303
|
+
with open(baseline_path) as f:
|
|
304
|
+
baseline_data = json.loads(f.read())
|
|
305
|
+
baseline_results = baseline_data.get("results", {})
|
|
306
|
+
except (json.JSONDecodeError, OSError):
|
|
307
|
+
pass
|
|
308
|
+
|
|
309
|
+
new_secrets = 0
|
|
310
|
+
new_files = 0
|
|
311
|
+
for fname, entries in scan_results.items():
|
|
312
|
+
baseline_entries = baseline_results.get(fname, [])
|
|
313
|
+
baseline_hashes = {e.get("hashed_secret") for e in baseline_entries}
|
|
314
|
+
new_in_file = [e for e in entries if e.get("hashed_secret") not in baseline_hashes]
|
|
315
|
+
if new_in_file:
|
|
316
|
+
new_secrets += len(new_in_file)
|
|
317
|
+
new_files += 1
|
|
318
|
+
|
|
319
|
+
total_raw = sum(len(v) for v in scan_results.values())
|
|
320
|
+
ok = new_secrets == 0
|
|
321
|
+
baseline_note = f" ({total_raw} baselined)" if baseline_results else ""
|
|
322
|
+
return AuditCheck(
|
|
323
|
+
check_id="SEC-003", check_name="Secret Detection",
|
|
324
|
+
category="security", status="pass" if ok else "fail",
|
|
325
|
+
severity="blocking",
|
|
326
|
+
message=f"No new secrets detected{baseline_note}" if ok
|
|
327
|
+
else f"{new_secrets} NEW secrets in {new_files} files{baseline_note}",
|
|
328
|
+
details={"new_secrets": new_secrets, "new_files": new_files,
|
|
329
|
+
"total_raw": total_raw, "baseline_count": sum(len(v) for v in baseline_results.values())},
|
|
330
|
+
)
|
|
331
|
+
except json.JSONDecodeError:
|
|
332
|
+
return AuditCheck(
|
|
333
|
+
check_id="SEC-003", check_name="Secret Detection",
|
|
334
|
+
category="security", status="warn", severity="blocking",
|
|
335
|
+
message="detect-secrets output not parseable", details={"stderr": stderr[:500]},
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def check_prompt_injection_gate() -> AuditCheck:
|
|
340
|
+
"""SEC-004: Prompt injection defense active."""
|
|
341
|
+
try:
|
|
342
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
343
|
+
from tools.security.prompt_injection_detector import INJECTION_PATTERNS
|
|
344
|
+
count = len(INJECTION_PATTERNS)
|
|
345
|
+
ok = count >= 10
|
|
346
|
+
return AuditCheck(
|
|
347
|
+
check_id="SEC-004", check_name="Prompt Injection Defense",
|
|
348
|
+
category="security", status="pass" if ok else "warn",
|
|
349
|
+
severity="blocking",
|
|
350
|
+
message=f"{count} injection patterns registered",
|
|
351
|
+
details={"pattern_count": count},
|
|
352
|
+
)
|
|
353
|
+
except ImportError as e:
|
|
354
|
+
return AuditCheck(
|
|
355
|
+
check_id="SEC-004", check_name="Prompt Injection Defense",
|
|
356
|
+
category="security", status="skip", severity="blocking",
|
|
357
|
+
message=f"Import failed: {e}", details={},
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def check_owasp_agentic() -> AuditCheck:
|
|
362
|
+
"""SEC-005: OWASP Agentic AI security tools present."""
|
|
363
|
+
tools_needed = [
|
|
364
|
+
"tools/security/tool_chain_validator.py",
|
|
365
|
+
"tools/security/agent_output_validator.py",
|
|
366
|
+
"tools/security/agent_trust_scorer.py",
|
|
367
|
+
"tools/security/mcp_tool_authorizer.py",
|
|
368
|
+
]
|
|
369
|
+
present = [t for t in tools_needed if (PROJECT_ROOT / t).exists()]
|
|
370
|
+
ok = len(present) == len(tools_needed)
|
|
371
|
+
return AuditCheck(
|
|
372
|
+
check_id="SEC-005", check_name="OWASP Agentic Security",
|
|
373
|
+
category="security", status="pass" if ok else "warn",
|
|
374
|
+
severity="warning",
|
|
375
|
+
message=f"{len(present)}/{len(tools_needed)} agentic security tools present",
|
|
376
|
+
details={"present": present, "missing": [t for t in tools_needed if t not in present]},
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def check_code_pattern_scan() -> AuditCheck:
|
|
381
|
+
"""SEC-006: Dangerous code pattern scan."""
|
|
382
|
+
scanner = PROJECT_ROOT / "tools" / "security" / "code_pattern_scanner.py"
|
|
383
|
+
if not scanner.exists():
|
|
384
|
+
return AuditCheck(
|
|
385
|
+
check_id="SEC-006", check_name="Code Pattern Scan",
|
|
386
|
+
category="security", status="skip", severity="blocking",
|
|
387
|
+
message="code_pattern_scanner.py not found", details={},
|
|
388
|
+
)
|
|
389
|
+
rc, stdout, stderr = _run_subprocess(
|
|
390
|
+
[sys.executable, str(scanner), "--dir", str(PROJECT_ROOT / "tools"), "--json"],
|
|
391
|
+
timeout=120,
|
|
392
|
+
)
|
|
393
|
+
if rc == 0:
|
|
394
|
+
try:
|
|
395
|
+
data = json.loads(stdout)
|
|
396
|
+
critical = data.get("critical", 0)
|
|
397
|
+
high = data.get("high", 0)
|
|
398
|
+
ok = critical == 0
|
|
399
|
+
return AuditCheck(
|
|
400
|
+
check_id="SEC-006", check_name="Code Pattern Scan",
|
|
401
|
+
category="security", status="pass" if ok else "fail",
|
|
402
|
+
severity="blocking",
|
|
403
|
+
message=f"critical={critical}, high={high}",
|
|
404
|
+
details=data,
|
|
405
|
+
)
|
|
406
|
+
except json.JSONDecodeError:
|
|
407
|
+
pass
|
|
408
|
+
return AuditCheck(
|
|
409
|
+
check_id="SEC-006", check_name="Code Pattern Scan",
|
|
410
|
+
category="security", status="warn" if rc == 0 else "fail",
|
|
411
|
+
severity="blocking",
|
|
412
|
+
message=f"Scanner exit {rc}", details={"stderr": stderr[:500]},
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
# ---------------------------------------------------------------------------
|
|
417
|
+
# Category 3: Compliance (CMP-001..006)
|
|
418
|
+
# ---------------------------------------------------------------------------
|
|
419
|
+
|
|
420
|
+
def check_cui_markings() -> AuditCheck:
|
|
421
|
+
"""CMP-001: CUI markings on Python files."""
|
|
422
|
+
tools_dir = PROJECT_ROOT / "tools"
|
|
423
|
+
total = 0
|
|
424
|
+
marked = 0
|
|
425
|
+
for py in tools_dir.rglob("*.py"):
|
|
426
|
+
if py.name.startswith("__"):
|
|
427
|
+
continue
|
|
428
|
+
total += 1
|
|
429
|
+
try:
|
|
430
|
+
head = py.read_text(encoding="utf-8")[:200]
|
|
431
|
+
if "CUI" in head:
|
|
432
|
+
marked += 1
|
|
433
|
+
except (OSError, UnicodeDecodeError):
|
|
434
|
+
pass
|
|
435
|
+
pct = round(marked / total * 100, 1) if total else 0
|
|
436
|
+
ok = pct >= 90
|
|
437
|
+
return AuditCheck(
|
|
438
|
+
check_id="CMP-001", check_name="CUI Marking Coverage",
|
|
439
|
+
category="compliance", status="pass" if ok else "warn",
|
|
440
|
+
severity="warning",
|
|
441
|
+
message=f"{marked}/{total} files marked ({pct}%)",
|
|
442
|
+
details={"total": total, "marked": marked, "pct": pct},
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def check_claude_governance() -> AuditCheck:
|
|
447
|
+
"""CMP-002: .claude governance validator."""
|
|
448
|
+
script = PROJECT_ROOT / "tools" / "testing" / "claude_dir_validator.py"
|
|
449
|
+
if not script.exists():
|
|
450
|
+
return AuditCheck(
|
|
451
|
+
check_id="CMP-002", check_name="Claude Governance",
|
|
452
|
+
category="compliance", status="skip", severity="blocking",
|
|
453
|
+
message="claude_dir_validator.py not found", details={},
|
|
454
|
+
)
|
|
455
|
+
rc, stdout, stderr = _run_subprocess(
|
|
456
|
+
[sys.executable, str(script), "--json"], timeout=60,
|
|
457
|
+
)
|
|
458
|
+
try:
|
|
459
|
+
data = json.loads(stdout)
|
|
460
|
+
failed = data.get("summary", {}).get("failed", 0)
|
|
461
|
+
warned = data.get("summary", {}).get("warned", 0)
|
|
462
|
+
passed = data.get("summary", {}).get("passed", 0)
|
|
463
|
+
ok = failed == 0
|
|
464
|
+
return AuditCheck(
|
|
465
|
+
check_id="CMP-002", check_name="Claude Governance",
|
|
466
|
+
category="compliance", status="pass" if ok else "fail",
|
|
467
|
+
severity="blocking",
|
|
468
|
+
message=f"{passed} passed, {failed} failed, {warned} warned",
|
|
469
|
+
details={"passed": passed, "failed": failed, "warned": warned},
|
|
470
|
+
)
|
|
471
|
+
except (json.JSONDecodeError, TypeError):
|
|
472
|
+
return AuditCheck(
|
|
473
|
+
check_id="CMP-002", check_name="Claude Governance",
|
|
474
|
+
category="compliance", status="fail" if rc != 0 else "warn",
|
|
475
|
+
severity="blocking",
|
|
476
|
+
message=f"Validator exit {rc}", details={"stderr": stderr[:500]},
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def check_append_only_tables() -> AuditCheck:
|
|
481
|
+
"""CMP-003: Append-only table coverage in hooks."""
|
|
482
|
+
hook_file = PROJECT_ROOT / ".claude" / "hooks" / "pre_tool_use.py"
|
|
483
|
+
init_file = PROJECT_ROOT / "tools" / "db" / "init_icdev_db.py"
|
|
484
|
+
if not hook_file.exists():
|
|
485
|
+
return AuditCheck(
|
|
486
|
+
check_id="CMP-003", check_name="Append-Only Table Coverage",
|
|
487
|
+
category="compliance", status="skip", severity="blocking",
|
|
488
|
+
message="pre_tool_use.py not found", details={},
|
|
489
|
+
)
|
|
490
|
+
try:
|
|
491
|
+
hook_content = hook_file.read_text(encoding="utf-8")
|
|
492
|
+
# Extract APPEND_ONLY_TABLES list
|
|
493
|
+
match = re.search(r'APPEND_ONLY_TABLES\s*=\s*\[(.*?)\]', hook_content, re.DOTALL)
|
|
494
|
+
if match:
|
|
495
|
+
tables_str = match.group(1)
|
|
496
|
+
protected = re.findall(r'"(\w+)"', tables_str)
|
|
497
|
+
else:
|
|
498
|
+
protected = []
|
|
499
|
+
ok = len(protected) >= 20 # We expect 29+ tables
|
|
500
|
+
return AuditCheck(
|
|
501
|
+
check_id="CMP-003", check_name="Append-Only Table Coverage",
|
|
502
|
+
category="compliance", status="pass" if ok else "warn",
|
|
503
|
+
severity="blocking",
|
|
504
|
+
message=f"{len(protected)} tables protected in hooks",
|
|
505
|
+
details={"count": len(protected), "tables": protected},
|
|
506
|
+
)
|
|
507
|
+
except Exception as e:
|
|
508
|
+
return AuditCheck(
|
|
509
|
+
check_id="CMP-003", check_name="Append-Only Table Coverage",
|
|
510
|
+
category="compliance", status="fail", severity="blocking",
|
|
511
|
+
message=str(e), details={},
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def check_security_gates_config() -> AuditCheck:
|
|
516
|
+
"""CMP-004: security_gates.yaml parseable and complete."""
|
|
517
|
+
gates_file = PROJECT_ROOT / "args" / "security_gates.yaml"
|
|
518
|
+
if not gates_file.exists():
|
|
519
|
+
return AuditCheck(
|
|
520
|
+
check_id="CMP-004", check_name="Security Gates Config",
|
|
521
|
+
category="compliance", status="fail", severity="warning",
|
|
522
|
+
message="security_gates.yaml not found", details={},
|
|
523
|
+
)
|
|
524
|
+
try:
|
|
525
|
+
import yaml
|
|
526
|
+
data = yaml.safe_load(gates_file.read_text(encoding="utf-8"))
|
|
527
|
+
gate_count = len(data) if isinstance(data, dict) else 0
|
|
528
|
+
expected_gates = ["merge_gates", "deploy_gates", "fedramp", "cmmc"]
|
|
529
|
+
# Accept alternate naming conventions (e.g., deployment_gates for deploy_gates)
|
|
530
|
+
aliases = {"deploy_gates": ["deployment_gates"]}
|
|
531
|
+
keys = set(data.keys()) if data else set()
|
|
532
|
+
present = [
|
|
533
|
+
g for g in expected_gates
|
|
534
|
+
if g in keys or any(a in keys for a in aliases.get(g, []))
|
|
535
|
+
]
|
|
536
|
+
ok = gate_count >= 5 and len(present) == len(expected_gates)
|
|
537
|
+
return AuditCheck(
|
|
538
|
+
check_id="CMP-004", check_name="Security Gates Config",
|
|
539
|
+
category="compliance", status="pass" if ok else "warn",
|
|
540
|
+
severity="warning",
|
|
541
|
+
message=f"{gate_count} gates defined, {len(present)}/{len(expected_gates)} core gates present",
|
|
542
|
+
details={"gate_count": gate_count, "present": present},
|
|
543
|
+
)
|
|
544
|
+
except ImportError:
|
|
545
|
+
return AuditCheck(
|
|
546
|
+
check_id="CMP-004", check_name="Security Gates Config",
|
|
547
|
+
category="compliance", status="skip", severity="warning",
|
|
548
|
+
message="pyyaml not installed", details={},
|
|
549
|
+
)
|
|
550
|
+
except Exception as e:
|
|
551
|
+
return AuditCheck(
|
|
552
|
+
check_id="CMP-004", check_name="Security Gates Config",
|
|
553
|
+
category="compliance", status="fail", severity="warning",
|
|
554
|
+
message=f"Parse error: {e}", details={},
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
def check_xai_compliance() -> AuditCheck:
|
|
559
|
+
"""CMP-005: XAI compliance assessor available."""
|
|
560
|
+
assessor = PROJECT_ROOT / "tools" / "compliance" / "xai_assessor.py"
|
|
561
|
+
if not assessor.exists():
|
|
562
|
+
return AuditCheck(
|
|
563
|
+
check_id="CMP-005", check_name="XAI Compliance",
|
|
564
|
+
category="compliance", status="skip", severity="warning",
|
|
565
|
+
message="xai_assessor.py not found (Phase 46)", details={},
|
|
566
|
+
)
|
|
567
|
+
try:
|
|
568
|
+
ast.parse(assessor.read_text(encoding="utf-8"))
|
|
569
|
+
return AuditCheck(
|
|
570
|
+
check_id="CMP-005", check_name="XAI Compliance",
|
|
571
|
+
category="compliance", status="pass", severity="warning",
|
|
572
|
+
message="XAI assessor available and syntactically valid",
|
|
573
|
+
details={},
|
|
574
|
+
)
|
|
575
|
+
except SyntaxError as e:
|
|
576
|
+
return AuditCheck(
|
|
577
|
+
check_id="CMP-005", check_name="XAI Compliance",
|
|
578
|
+
category="compliance", status="fail", severity="warning",
|
|
579
|
+
message=f"Syntax error: {e}", details={},
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def check_sbom_generation() -> AuditCheck:
|
|
584
|
+
"""CMP-006: SBOM generator available."""
|
|
585
|
+
sbom = PROJECT_ROOT / "tools" / "compliance" / "sbom_generator.py"
|
|
586
|
+
if not sbom.exists():
|
|
587
|
+
return AuditCheck(
|
|
588
|
+
check_id="CMP-006", check_name="SBOM Generator",
|
|
589
|
+
category="compliance", status="skip", severity="warning",
|
|
590
|
+
message="sbom_generator.py not found", details={},
|
|
591
|
+
)
|
|
592
|
+
try:
|
|
593
|
+
ast.parse(sbom.read_text(encoding="utf-8"))
|
|
594
|
+
return AuditCheck(
|
|
595
|
+
check_id="CMP-006", check_name="SBOM Generator",
|
|
596
|
+
category="compliance", status="pass", severity="warning",
|
|
597
|
+
message="SBOM generator available", details={},
|
|
598
|
+
)
|
|
599
|
+
except SyntaxError as e:
|
|
600
|
+
return AuditCheck(
|
|
601
|
+
check_id="CMP-006", check_name="SBOM Generator",
|
|
602
|
+
category="compliance", status="fail", severity="warning",
|
|
603
|
+
message=f"Syntax error: {e}", details={},
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def check_ai_inventory() -> AuditCheck:
|
|
608
|
+
"""AI-001: AI inventory populated (Phase 48)."""
|
|
609
|
+
if False: # Storage layer handles path resolution (D-DB-20)
|
|
610
|
+
return AuditCheck(
|
|
611
|
+
check_id="AI-001", check_name="AI Inventory Populated",
|
|
612
|
+
category="compliance", status="skip", severity="warning",
|
|
613
|
+
message="Database not found", details={},
|
|
614
|
+
)
|
|
615
|
+
try:
|
|
616
|
+
conn = get_connection()
|
|
617
|
+
# Check if table exists
|
|
618
|
+
table_exists = conn.execute(
|
|
619
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='ai_use_case_inventory'"
|
|
620
|
+
).fetchone()
|
|
621
|
+
if not table_exists:
|
|
622
|
+
conn.close()
|
|
623
|
+
return AuditCheck(
|
|
624
|
+
check_id="AI-001", check_name="AI Inventory Populated",
|
|
625
|
+
category="compliance", status="fail", severity="warning",
|
|
626
|
+
message="ai_use_case_inventory table not found — run init_icdev_db.py",
|
|
627
|
+
details={"table_exists": False},
|
|
628
|
+
)
|
|
629
|
+
count = conn.execute("SELECT COUNT(*) FROM ai_use_case_inventory").fetchone()[0]
|
|
630
|
+
conn.close()
|
|
631
|
+
ok = count > 0
|
|
632
|
+
return AuditCheck(
|
|
633
|
+
check_id="AI-001", check_name="AI Inventory Populated",
|
|
634
|
+
category="compliance", status="pass" if ok else "fail",
|
|
635
|
+
severity="warning",
|
|
636
|
+
message=f"{count} AI use cases registered" if ok else "No AI use cases registered — run ai_inventory_manager.py",
|
|
637
|
+
details={"record_count": count},
|
|
638
|
+
)
|
|
639
|
+
except Exception as e:
|
|
640
|
+
return AuditCheck(
|
|
641
|
+
check_id="AI-001", check_name="AI Inventory Populated",
|
|
642
|
+
category="compliance", status="fail", severity="warning",
|
|
643
|
+
message=str(e), details={},
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def check_model_cards() -> AuditCheck:
|
|
648
|
+
"""AI-002: Model cards generated (Phase 48)."""
|
|
649
|
+
if False: # Storage layer handles path resolution (D-DB-20)
|
|
650
|
+
return AuditCheck(
|
|
651
|
+
check_id="AI-002", check_name="Model Cards Generated",
|
|
652
|
+
category="compliance", status="skip", severity="warning",
|
|
653
|
+
message="Database not found", details={},
|
|
654
|
+
)
|
|
655
|
+
try:
|
|
656
|
+
conn = get_connection()
|
|
657
|
+
table_exists = conn.execute(
|
|
658
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='model_cards'"
|
|
659
|
+
).fetchone()
|
|
660
|
+
if not table_exists:
|
|
661
|
+
conn.close()
|
|
662
|
+
return AuditCheck(
|
|
663
|
+
check_id="AI-002", check_name="Model Cards Generated",
|
|
664
|
+
category="compliance", status="fail", severity="warning",
|
|
665
|
+
message="model_cards table not found — run init_icdev_db.py",
|
|
666
|
+
details={"table_exists": False},
|
|
667
|
+
)
|
|
668
|
+
count = conn.execute("SELECT COUNT(*) FROM model_cards").fetchone()[0]
|
|
669
|
+
conn.close()
|
|
670
|
+
ok = count > 0
|
|
671
|
+
return AuditCheck(
|
|
672
|
+
check_id="AI-002", check_name="Model Cards Generated",
|
|
673
|
+
category="compliance", status="pass" if ok else "fail",
|
|
674
|
+
severity="warning",
|
|
675
|
+
message=f"{count} model cards generated" if ok else "No model cards generated — run model_card_generator.py",
|
|
676
|
+
details={"record_count": count},
|
|
677
|
+
)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
return AuditCheck(
|
|
680
|
+
check_id="AI-002", check_name="Model Cards Generated",
|
|
681
|
+
category="compliance", status="fail", severity="warning",
|
|
682
|
+
message=str(e), details={},
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def check_ai_transparency_frameworks() -> AuditCheck:
|
|
687
|
+
"""AI-003: AI transparency frameworks assessed (Phase 48)."""
|
|
688
|
+
if False: # Storage layer handles path resolution (D-DB-20)
|
|
689
|
+
return AuditCheck(
|
|
690
|
+
check_id="AI-003", check_name="AI Transparency Frameworks Assessed",
|
|
691
|
+
category="compliance", status="skip", severity="warning",
|
|
692
|
+
message="Database not found", details={},
|
|
693
|
+
)
|
|
694
|
+
assessment_tables = [
|
|
695
|
+
"omb_m25_21_assessments",
|
|
696
|
+
"omb_m26_04_assessments",
|
|
697
|
+
"nist_ai_600_1_assessments",
|
|
698
|
+
"gao_ai_assessments",
|
|
699
|
+
]
|
|
700
|
+
try:
|
|
701
|
+
conn = get_connection()
|
|
702
|
+
found_tables = []
|
|
703
|
+
total_records = 0
|
|
704
|
+
for tbl in assessment_tables:
|
|
705
|
+
exists = conn.execute(
|
|
706
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?", (tbl,)
|
|
707
|
+
).fetchone()
|
|
708
|
+
if exists:
|
|
709
|
+
cnt = conn.execute(f"SELECT COUNT(*) FROM {tbl}").fetchone()[0]
|
|
710
|
+
if cnt > 0:
|
|
711
|
+
found_tables.append({"table": tbl, "count": cnt})
|
|
712
|
+
total_records += cnt
|
|
713
|
+
conn.close()
|
|
714
|
+
ok = len(found_tables) > 0
|
|
715
|
+
if ok:
|
|
716
|
+
tbl_names = [t["table"] for t in found_tables]
|
|
717
|
+
return AuditCheck(
|
|
718
|
+
check_id="AI-003", check_name="AI Transparency Frameworks Assessed",
|
|
719
|
+
category="compliance", status="pass", severity="warning",
|
|
720
|
+
message=f"{len(found_tables)} framework(s) assessed ({total_records} total records): {', '.join(tbl_names)}",
|
|
721
|
+
details={"assessed_frameworks": found_tables, "total_records": total_records},
|
|
722
|
+
)
|
|
723
|
+
return AuditCheck(
|
|
724
|
+
check_id="AI-003", check_name="AI Transparency Frameworks Assessed",
|
|
725
|
+
category="compliance", status="fail", severity="warning",
|
|
726
|
+
message="No AI transparency framework assessments found — run ai_transparency_audit.py",
|
|
727
|
+
details={"checked_tables": assessment_tables, "assessed_frameworks": []},
|
|
728
|
+
)
|
|
729
|
+
except Exception as e:
|
|
730
|
+
return AuditCheck(
|
|
731
|
+
check_id="AI-003", check_name="AI Transparency Frameworks Assessed",
|
|
732
|
+
category="compliance", status="fail", severity="warning",
|
|
733
|
+
message=str(e), details={},
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
def check_owasp_asi() -> AuditCheck:
|
|
738
|
+
"""SEC-007: OWASP ASI01-ASI10 assessor present and catalog valid (Phase 53, D339)."""
|
|
739
|
+
assessor = PROJECT_ROOT / "tools" / "compliance" / "owasp_asi_assessor.py"
|
|
740
|
+
catalog = PROJECT_ROOT / "context" / "compliance" / "owasp_agentic_asi.json"
|
|
741
|
+
missing = []
|
|
742
|
+
if not assessor.exists():
|
|
743
|
+
missing.append("owasp_asi_assessor.py")
|
|
744
|
+
if not catalog.exists():
|
|
745
|
+
missing.append("owasp_agentic_asi.json")
|
|
746
|
+
if missing:
|
|
747
|
+
return AuditCheck(
|
|
748
|
+
check_id="SEC-007", check_name="OWASP ASI Assessor",
|
|
749
|
+
category="security", status="skip", severity="warning",
|
|
750
|
+
message=f"Missing: {', '.join(missing)}", details={"missing": missing},
|
|
751
|
+
)
|
|
752
|
+
try:
|
|
753
|
+
data = json.loads(catalog.read_text(encoding="utf-8"))
|
|
754
|
+
req_count = len(data.get("requirements", []))
|
|
755
|
+
ok = req_count == 10
|
|
756
|
+
return AuditCheck(
|
|
757
|
+
check_id="SEC-007", check_name="OWASP ASI Assessor",
|
|
758
|
+
category="security", status="pass" if ok else "warn",
|
|
759
|
+
severity="warning",
|
|
760
|
+
message=f"OWASP ASI catalog: {req_count} risks, assessor present",
|
|
761
|
+
details={"requirements": req_count, "assessor": str(assessor)},
|
|
762
|
+
)
|
|
763
|
+
except Exception as e:
|
|
764
|
+
return AuditCheck(
|
|
765
|
+
check_id="SEC-007", check_name="OWASP ASI Assessor",
|
|
766
|
+
category="security", status="fail", severity="warning",
|
|
767
|
+
message=str(e), details={},
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def check_endpoint_security() -> AuditCheck:
|
|
772
|
+
"""SEC-008: Endpoint security scan — detect routes missing auth/validation."""
|
|
773
|
+
scanner = PROJECT_ROOT / "tools" / "security" / "endpoint_security_scanner.py"
|
|
774
|
+
if not scanner.exists():
|
|
775
|
+
return AuditCheck(
|
|
776
|
+
check_id="SEC-008", check_name="Endpoint Security Scan",
|
|
777
|
+
category="security", status="skip", severity="blocking",
|
|
778
|
+
message="endpoint_security_scanner.py not found", details={},
|
|
779
|
+
)
|
|
780
|
+
rc, stdout, stderr = _run_subprocess(
|
|
781
|
+
[sys.executable, str(scanner), "--dir", str(PROJECT_ROOT / "tools"), "--json"],
|
|
782
|
+
timeout=120,
|
|
783
|
+
)
|
|
784
|
+
if rc == 0:
|
|
785
|
+
try:
|
|
786
|
+
data = json.loads(stdout)
|
|
787
|
+
critical = data.get("critical", 0)
|
|
788
|
+
high = data.get("high", 0)
|
|
789
|
+
ok = critical == 0 and high == 0
|
|
790
|
+
return AuditCheck(
|
|
791
|
+
check_id="SEC-008", check_name="Endpoint Security Scan",
|
|
792
|
+
category="security", status="pass" if ok else "fail",
|
|
793
|
+
severity="blocking",
|
|
794
|
+
message=f"routes={data.get('routes_found', 0)}, critical={critical}, high={high}",
|
|
795
|
+
details=data,
|
|
796
|
+
)
|
|
797
|
+
except json.JSONDecodeError:
|
|
798
|
+
pass
|
|
799
|
+
return AuditCheck(
|
|
800
|
+
check_id="SEC-008", check_name="Endpoint Security Scan",
|
|
801
|
+
category="security", status="warn" if rc == 0 else "fail",
|
|
802
|
+
severity="blocking",
|
|
803
|
+
message=f"Scanner exit {rc}", details={"stderr": stderr[:500]},
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def check_fedramp_20x_ksi() -> AuditCheck:
|
|
808
|
+
"""CMP-008: FedRAMP 20x KSI generator and schema present (Phase 53, D338)."""
|
|
809
|
+
generator = PROJECT_ROOT / "tools" / "compliance" / "fedramp_ksi_generator.py"
|
|
810
|
+
schema = PROJECT_ROOT / "context" / "compliance" / "fedramp_20x_ksi_schemas.json"
|
|
811
|
+
missing = []
|
|
812
|
+
if not generator.exists():
|
|
813
|
+
missing.append("fedramp_ksi_generator.py")
|
|
814
|
+
if not schema.exists():
|
|
815
|
+
missing.append("fedramp_20x_ksi_schemas.json")
|
|
816
|
+
if missing:
|
|
817
|
+
return AuditCheck(
|
|
818
|
+
check_id="CMP-008", check_name="FedRAMP 20x KSI",
|
|
819
|
+
category="compliance", status="skip", severity="warning",
|
|
820
|
+
message=f"Missing: {', '.join(missing)}", details={"missing": missing},
|
|
821
|
+
)
|
|
822
|
+
try:
|
|
823
|
+
data = json.loads(schema.read_text(encoding="utf-8"))
|
|
824
|
+
ksi_count = sum(len(f.get("ksis", [])) for f in data.get("ksi_families", []))
|
|
825
|
+
families = len(data.get("ksi_families", []))
|
|
826
|
+
return AuditCheck(
|
|
827
|
+
check_id="CMP-008", check_name="FedRAMP 20x KSI",
|
|
828
|
+
category="compliance", status="pass" if ksi_count > 0 else "warn",
|
|
829
|
+
severity="warning",
|
|
830
|
+
message=f"FedRAMP 20x: {ksi_count} KSIs across {families} families",
|
|
831
|
+
details={"ksi_count": ksi_count, "families": families},
|
|
832
|
+
)
|
|
833
|
+
except Exception as e:
|
|
834
|
+
return AuditCheck(
|
|
835
|
+
check_id="CMP-008", check_name="FedRAMP 20x KSI",
|
|
836
|
+
category="compliance", status="fail", severity="warning",
|
|
837
|
+
message=str(e), details={},
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def check_slsa_swft() -> AuditCheck:
|
|
842
|
+
"""CMP-009: SLSA attestation generator and SWFT evidence bundler present (Phase 54, D341)."""
|
|
843
|
+
slsa = PROJECT_ROOT / "tools" / "compliance" / "slsa_attestation_generator.py"
|
|
844
|
+
swft = PROJECT_ROOT / "tools" / "compliance" / "swft_evidence_bundler.py"
|
|
845
|
+
missing = []
|
|
846
|
+
if not slsa.exists():
|
|
847
|
+
missing.append("slsa_attestation_generator.py")
|
|
848
|
+
if not swft.exists():
|
|
849
|
+
missing.append("swft_evidence_bundler.py")
|
|
850
|
+
if missing:
|
|
851
|
+
return AuditCheck(
|
|
852
|
+
check_id="CMP-009", check_name="SLSA/SWFT",
|
|
853
|
+
category="compliance", status="skip", severity="warning",
|
|
854
|
+
message=f"Missing: {', '.join(missing)}", details={"missing": missing},
|
|
855
|
+
)
|
|
856
|
+
try:
|
|
857
|
+
import py_compile
|
|
858
|
+
for f in [slsa, swft]:
|
|
859
|
+
py_compile.compile(str(f), doraise=True)
|
|
860
|
+
return AuditCheck(
|
|
861
|
+
check_id="CMP-009", check_name="SLSA/SWFT",
|
|
862
|
+
category="compliance", status="pass", severity="warning",
|
|
863
|
+
message="SLSA v1.0 attestation generator and SWFT evidence bundler present",
|
|
864
|
+
details={"slsa": True, "swft": True},
|
|
865
|
+
)
|
|
866
|
+
except Exception as e:
|
|
867
|
+
return AuditCheck(
|
|
868
|
+
check_id="CMP-009", check_name="SLSA/SWFT",
|
|
869
|
+
category="compliance", status="fail", severity="warning",
|
|
870
|
+
message=str(e), details={},
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def check_oscal_ecosystem() -> AuditCheck:
|
|
875
|
+
"""CMP-007: OSCAL ecosystem tools readiness (D302-D306)."""
|
|
876
|
+
oscal_tools = PROJECT_ROOT / "tools" / "compliance" / "oscal_tools.py"
|
|
877
|
+
catalog_adapter = PROJECT_ROOT / "tools" / "compliance" / "oscal_catalog_adapter.py"
|
|
878
|
+
# Check both files exist
|
|
879
|
+
missing = []
|
|
880
|
+
if not oscal_tools.exists():
|
|
881
|
+
missing.append("oscal_tools.py")
|
|
882
|
+
if not catalog_adapter.exists():
|
|
883
|
+
missing.append("oscal_catalog_adapter.py")
|
|
884
|
+
if missing:
|
|
885
|
+
return AuditCheck(
|
|
886
|
+
check_id="CMP-007", check_name="OSCAL Ecosystem",
|
|
887
|
+
category="compliance", status="skip", severity="warning",
|
|
888
|
+
message=f"Missing: {', '.join(missing)}", details={"missing": missing},
|
|
889
|
+
)
|
|
890
|
+
# Syntax check both files
|
|
891
|
+
syntax_errors = []
|
|
892
|
+
for f in [oscal_tools, catalog_adapter]:
|
|
893
|
+
try:
|
|
894
|
+
ast.parse(f.read_text(encoding="utf-8"))
|
|
895
|
+
except SyntaxError as e:
|
|
896
|
+
syntax_errors.append(f"{f.name}: {e}")
|
|
897
|
+
if syntax_errors:
|
|
898
|
+
return AuditCheck(
|
|
899
|
+
check_id="CMP-007", check_name="OSCAL Ecosystem",
|
|
900
|
+
category="compliance", status="fail", severity="warning",
|
|
901
|
+
message=f"Syntax errors: {'; '.join(syntax_errors)}",
|
|
902
|
+
details={"syntax_errors": syntax_errors},
|
|
903
|
+
)
|
|
904
|
+
# Detect available OSCAL tools
|
|
905
|
+
try:
|
|
906
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
907
|
+
from tools.compliance.oscal_tools import detect_oscal_tools
|
|
908
|
+
detection = detect_oscal_tools()
|
|
909
|
+
available = []
|
|
910
|
+
unavailable = []
|
|
911
|
+
for key in ["oscal_cli", "java", "oscal_pydantic", "nist_catalog"]:
|
|
912
|
+
info = detection.get(key, {})
|
|
913
|
+
if info.get("available", False):
|
|
914
|
+
available.append(key)
|
|
915
|
+
else:
|
|
916
|
+
unavailable.append(key)
|
|
917
|
+
# Pass if core files exist; warn if optional tools not installed
|
|
918
|
+
status = "pass" if len(available) >= 1 else "warn"
|
|
919
|
+
msg_parts = [f"available: {', '.join(available) or 'none'}"]
|
|
920
|
+
if unavailable:
|
|
921
|
+
msg_parts.append(f"not installed: {', '.join(unavailable)}")
|
|
922
|
+
return AuditCheck(
|
|
923
|
+
check_id="CMP-007", check_name="OSCAL Ecosystem",
|
|
924
|
+
category="compliance", status=status, severity="warning",
|
|
925
|
+
message="; ".join(msg_parts),
|
|
926
|
+
details={"available": available, "unavailable": unavailable, "detection": detection},
|
|
927
|
+
)
|
|
928
|
+
except Exception as e:
|
|
929
|
+
return AuditCheck(
|
|
930
|
+
check_id="CMP-007", check_name="OSCAL Ecosystem",
|
|
931
|
+
category="compliance", status="warn", severity="warning",
|
|
932
|
+
message=f"Detection failed: {e}", details={},
|
|
933
|
+
)
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
# ---------------------------------------------------------------------------
|
|
937
|
+
# Category 4: Integration (INT-001..005)
|
|
938
|
+
# ---------------------------------------------------------------------------
|
|
939
|
+
|
|
940
|
+
def check_mcp_servers() -> AuditCheck:
|
|
941
|
+
"""INT-001: Validate all MCP server files parse correctly."""
|
|
942
|
+
mcp_dir = PROJECT_ROOT / "tools" / "mcp"
|
|
943
|
+
if not mcp_dir.exists():
|
|
944
|
+
return AuditCheck(
|
|
945
|
+
check_id="INT-001", check_name="MCP Server Validation",
|
|
946
|
+
category="integration", status="skip", severity="blocking",
|
|
947
|
+
message="tools/mcp/ not found", details={},
|
|
948
|
+
)
|
|
949
|
+
servers = sorted(mcp_dir.glob("*_server.py"))
|
|
950
|
+
errors = []
|
|
951
|
+
for srv in servers:
|
|
952
|
+
try:
|
|
953
|
+
ast.parse(srv.read_text(encoding="utf-8"))
|
|
954
|
+
except SyntaxError as e:
|
|
955
|
+
errors.append(f"{srv.name}: {e}")
|
|
956
|
+
ok = len(errors) == 0
|
|
957
|
+
return AuditCheck(
|
|
958
|
+
check_id="INT-001", check_name="MCP Server Validation",
|
|
959
|
+
category="integration", status="pass" if ok else "fail",
|
|
960
|
+
severity="blocking",
|
|
961
|
+
message=f"{len(servers)} servers validated, {len(errors)} errors",
|
|
962
|
+
details={"total": len(servers), "errors": errors},
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def check_db_schema() -> AuditCheck:
|
|
967
|
+
"""INT-002: DB schema — expected table count."""
|
|
968
|
+
if False: # Storage layer handles path resolution (D-DB-20)
|
|
969
|
+
return AuditCheck(
|
|
970
|
+
check_id="INT-002", check_name="DB Schema Validation",
|
|
971
|
+
category="integration", status="fail", severity="blocking",
|
|
972
|
+
message=f"Database not found: {DB_PATH}", details={},
|
|
973
|
+
)
|
|
974
|
+
try:
|
|
975
|
+
conn = get_connection()
|
|
976
|
+
tables = conn.execute(
|
|
977
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
978
|
+
).fetchall()
|
|
979
|
+
conn.close()
|
|
980
|
+
count = len(tables)
|
|
981
|
+
ok = count >= 150 # We expect 176+
|
|
982
|
+
return AuditCheck(
|
|
983
|
+
check_id="INT-002", check_name="DB Schema Validation",
|
|
984
|
+
category="integration", status="pass" if ok else "warn",
|
|
985
|
+
severity="blocking",
|
|
986
|
+
message=f"{count} tables in icdev.db",
|
|
987
|
+
details={"table_count": count},
|
|
988
|
+
)
|
|
989
|
+
except Exception as e:
|
|
990
|
+
return AuditCheck(
|
|
991
|
+
check_id="INT-002", check_name="DB Schema Validation",
|
|
992
|
+
category="integration", status="fail", severity="blocking",
|
|
993
|
+
message=str(e), details={},
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
def check_cross_imports() -> AuditCheck:
|
|
998
|
+
"""INT-003: Validate all tools/**/*.py can be parsed (AST syntax check)."""
|
|
999
|
+
tools_dir = PROJECT_ROOT / "tools"
|
|
1000
|
+
errors = []
|
|
1001
|
+
checked = 0
|
|
1002
|
+
for py in sorted(tools_dir.rglob("*.py")):
|
|
1003
|
+
if py.name.startswith("__"):
|
|
1004
|
+
continue
|
|
1005
|
+
checked += 1
|
|
1006
|
+
try:
|
|
1007
|
+
ast.parse(py.read_text(encoding="utf-8"))
|
|
1008
|
+
except SyntaxError as e:
|
|
1009
|
+
errors.append(f"{py.relative_to(PROJECT_ROOT)}: {e.msg} (line {e.lineno})")
|
|
1010
|
+
ok = len(errors) == 0
|
|
1011
|
+
return AuditCheck(
|
|
1012
|
+
check_id="INT-003", check_name="Cross-Module Syntax Check",
|
|
1013
|
+
category="integration", status="pass" if ok else "fail",
|
|
1014
|
+
severity="warning",
|
|
1015
|
+
message=f"{checked} files parsed, {len(errors)} syntax errors",
|
|
1016
|
+
details={"checked": checked, "errors": errors[:20]},
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def check_dashboard_health() -> AuditCheck:
|
|
1021
|
+
"""INT-004: Dashboard page health (requires running dashboard)."""
|
|
1022
|
+
try:
|
|
1023
|
+
import requests
|
|
1024
|
+
except ImportError:
|
|
1025
|
+
return AuditCheck(
|
|
1026
|
+
check_id="INT-004", check_name="Dashboard Page Health",
|
|
1027
|
+
category="integration", status="skip", severity="warning",
|
|
1028
|
+
message="requests not installed", details={},
|
|
1029
|
+
)
|
|
1030
|
+
port = os.environ.get("ICDEV_DASHBOARD_PORT", "5050")
|
|
1031
|
+
base = f"http://localhost:{port}"
|
|
1032
|
+
try:
|
|
1033
|
+
r = requests.get(f"{base}/login", timeout=3)
|
|
1034
|
+
if r.status_code != 200:
|
|
1035
|
+
return AuditCheck(
|
|
1036
|
+
check_id="INT-004", check_name="Dashboard Page Health",
|
|
1037
|
+
category="integration", status="skip", severity="warning",
|
|
1038
|
+
message=f"Dashboard not running on port {port}",
|
|
1039
|
+
details={},
|
|
1040
|
+
)
|
|
1041
|
+
except Exception:
|
|
1042
|
+
return AuditCheck(
|
|
1043
|
+
check_id="INT-004", check_name="Dashboard Page Health",
|
|
1044
|
+
category="integration", status="skip", severity="warning",
|
|
1045
|
+
message=f"Dashboard not reachable on port {port}", details={},
|
|
1046
|
+
)
|
|
1047
|
+
# Dashboard is running — test pages
|
|
1048
|
+
session = requests.Session()
|
|
1049
|
+
# Login with env-var API key (matches before_request auth in app.py)
|
|
1050
|
+
dashboard_key = os.environ.get("SPARKPILOT_DASHBOARD_KEY", "sparkpilot")
|
|
1051
|
+
try:
|
|
1052
|
+
session.post(f"{base}/login", data={"api_key": dashboard_key}, allow_redirects=False)
|
|
1053
|
+
except Exception:
|
|
1054
|
+
pass # Continue without auth
|
|
1055
|
+
|
|
1056
|
+
# SparkPilot dashboard routes
|
|
1057
|
+
pages = ["/", "/missions", "/devices", "/simulator", "/firmware",
|
|
1058
|
+
"/edge-ai", "/crashes", "/agents", "/govcon", "/writeguard",
|
|
1059
|
+
"/databridge", "/health"]
|
|
1060
|
+
ok_count = 0
|
|
1061
|
+
fail_pages = []
|
|
1062
|
+
for page in pages:
|
|
1063
|
+
try:
|
|
1064
|
+
r = session.get(f"{base}{page}", timeout=5)
|
|
1065
|
+
if r.status_code == 200 and ("container" in r.text or "sparkpilot" in r.text.lower()):
|
|
1066
|
+
ok_count += 1
|
|
1067
|
+
else:
|
|
1068
|
+
fail_pages.append(f"{page} (status={r.status_code})")
|
|
1069
|
+
except Exception as e:
|
|
1070
|
+
fail_pages.append(f"{page} ({e})")
|
|
1071
|
+
ok = len(fail_pages) == 0
|
|
1072
|
+
return AuditCheck(
|
|
1073
|
+
check_id="INT-004", check_name="Dashboard Page Health",
|
|
1074
|
+
category="integration", status="pass" if ok else "warn",
|
|
1075
|
+
severity="warning",
|
|
1076
|
+
message=f"{ok_count}/{len(pages)} pages OK" + (f", failed: {', '.join(fail_pages[:5])}" if fail_pages else ""),
|
|
1077
|
+
details={"total": len(pages), "passed": ok_count, "failed": fail_pages},
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
def check_api_gateway() -> AuditCheck:
|
|
1082
|
+
"""INT-005: API gateway Flask app importable."""
|
|
1083
|
+
try:
|
|
1084
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
1085
|
+
spec = importlib.util.spec_from_file_location(
|
|
1086
|
+
"api_gateway", str(PROJECT_ROOT / "tools" / "saas" / "api_gateway.py")
|
|
1087
|
+
)
|
|
1088
|
+
if spec and spec.loader:
|
|
1089
|
+
return AuditCheck(
|
|
1090
|
+
check_id="INT-005", check_name="API Gateway",
|
|
1091
|
+
category="integration", status="pass", severity="warning",
|
|
1092
|
+
message="API gateway module found", details={},
|
|
1093
|
+
)
|
|
1094
|
+
except Exception as e:
|
|
1095
|
+
return AuditCheck(
|
|
1096
|
+
check_id="INT-005", check_name="API Gateway",
|
|
1097
|
+
category="integration", status="warn", severity="warning",
|
|
1098
|
+
message=f"Import check: {e}", details={},
|
|
1099
|
+
)
|
|
1100
|
+
return AuditCheck(
|
|
1101
|
+
check_id="INT-005", check_name="API Gateway",
|
|
1102
|
+
category="integration", status="skip", severity="warning",
|
|
1103
|
+
message="api_gateway.py not found", details={},
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
# ---------------------------------------------------------------------------
|
|
1108
|
+
# Category 5: Performance / Resilience (PRF-001..004)
|
|
1109
|
+
# ---------------------------------------------------------------------------
|
|
1110
|
+
|
|
1111
|
+
def check_migration_status() -> AuditCheck:
|
|
1112
|
+
"""PRF-001: DB migration status."""
|
|
1113
|
+
migrate = PROJECT_ROOT / "tools" / "db" / "migrate.py"
|
|
1114
|
+
if not migrate.exists():
|
|
1115
|
+
return AuditCheck(
|
|
1116
|
+
check_id="PRF-001", check_name="DB Migration Status",
|
|
1117
|
+
category="performance", status="skip", severity="warning",
|
|
1118
|
+
message="migrate.py not found", details={},
|
|
1119
|
+
)
|
|
1120
|
+
rc, stdout, stderr = _run_subprocess(
|
|
1121
|
+
[sys.executable, str(migrate), "--status", "--json"], timeout=30,
|
|
1122
|
+
)
|
|
1123
|
+
if rc == 0:
|
|
1124
|
+
try:
|
|
1125
|
+
data = json.loads(stdout)
|
|
1126
|
+
pending = data.get("pending_count", data.get("pending", 0))
|
|
1127
|
+
if isinstance(pending, list):
|
|
1128
|
+
pending = len(pending)
|
|
1129
|
+
ok = pending == 0
|
|
1130
|
+
return AuditCheck(
|
|
1131
|
+
check_id="PRF-001", check_name="DB Migration Status",
|
|
1132
|
+
category="performance", status="pass" if ok else "warn",
|
|
1133
|
+
severity="warning",
|
|
1134
|
+
message=f"{pending} pending migrations" if pending else "All migrations applied",
|
|
1135
|
+
details=data,
|
|
1136
|
+
)
|
|
1137
|
+
except json.JSONDecodeError:
|
|
1138
|
+
pass
|
|
1139
|
+
return AuditCheck(
|
|
1140
|
+
check_id="PRF-001", check_name="DB Migration Status",
|
|
1141
|
+
category="performance", status="warn", severity="warning",
|
|
1142
|
+
message=f"migrate.py exit {rc}", details={"stderr": stderr[:300]},
|
|
1143
|
+
)
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
def check_backup_config() -> AuditCheck:
|
|
1147
|
+
"""PRF-002: DB backup config exists."""
|
|
1148
|
+
config = PROJECT_ROOT / "args" / "db_config.yaml"
|
|
1149
|
+
if not config.exists():
|
|
1150
|
+
return AuditCheck(
|
|
1151
|
+
check_id="PRF-002", check_name="DB Backup Config",
|
|
1152
|
+
category="performance", status="warn", severity="warning",
|
|
1153
|
+
message="args/db_config.yaml not found", details={},
|
|
1154
|
+
)
|
|
1155
|
+
try:
|
|
1156
|
+
content = config.read_text(encoding="utf-8")
|
|
1157
|
+
has_backup = "backup" in content.lower()
|
|
1158
|
+
return AuditCheck(
|
|
1159
|
+
check_id="PRF-002", check_name="DB Backup Config",
|
|
1160
|
+
category="performance", status="pass" if has_backup else "warn",
|
|
1161
|
+
severity="warning",
|
|
1162
|
+
message="Backup configuration present" if has_backup else "No backup section found",
|
|
1163
|
+
details={"has_backup_section": has_backup},
|
|
1164
|
+
)
|
|
1165
|
+
except Exception as e:
|
|
1166
|
+
return AuditCheck(
|
|
1167
|
+
check_id="PRF-002", check_name="DB Backup Config",
|
|
1168
|
+
category="performance", status="warn", severity="warning",
|
|
1169
|
+
message=str(e), details={},
|
|
1170
|
+
)
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
def check_resilience_config() -> AuditCheck:
|
|
1174
|
+
"""PRF-003: Circuit breaker / resilience config."""
|
|
1175
|
+
config = PROJECT_ROOT / "args" / "resilience_config.yaml"
|
|
1176
|
+
if not config.exists():
|
|
1177
|
+
return AuditCheck(
|
|
1178
|
+
check_id="PRF-003", check_name="Resilience Config",
|
|
1179
|
+
category="performance", status="warn", severity="warning",
|
|
1180
|
+
message="args/resilience_config.yaml not found", details={},
|
|
1181
|
+
)
|
|
1182
|
+
try:
|
|
1183
|
+
import yaml
|
|
1184
|
+
data = yaml.safe_load(config.read_text(encoding="utf-8"))
|
|
1185
|
+
has_cb = "circuit_breaker" in str(data).lower() if data else False
|
|
1186
|
+
has_retry = "retry" in str(data).lower() if data else False
|
|
1187
|
+
ok = has_cb and has_retry
|
|
1188
|
+
return AuditCheck(
|
|
1189
|
+
check_id="PRF-003", check_name="Resilience Config",
|
|
1190
|
+
category="performance", status="pass" if ok else "warn",
|
|
1191
|
+
severity="warning",
|
|
1192
|
+
message=f"circuit_breaker={'yes' if has_cb else 'no'}, retry={'yes' if has_retry else 'no'}",
|
|
1193
|
+
details={"circuit_breaker": has_cb, "retry": has_retry},
|
|
1194
|
+
)
|
|
1195
|
+
except ImportError:
|
|
1196
|
+
return AuditCheck(
|
|
1197
|
+
check_id="PRF-003", check_name="Resilience Config",
|
|
1198
|
+
category="performance", status="skip", severity="warning",
|
|
1199
|
+
message="pyyaml not installed", details={},
|
|
1200
|
+
)
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
def check_test_collection() -> AuditCheck:
|
|
1204
|
+
"""PRF-004: pytest collection (no import errors)."""
|
|
1205
|
+
rc, stdout, stderr = _run_subprocess(
|
|
1206
|
+
[sys.executable, "-m", "pytest", str(PROJECT_ROOT / "tests"), "--co", "-q"],
|
|
1207
|
+
timeout=120,
|
|
1208
|
+
)
|
|
1209
|
+
if rc == -1:
|
|
1210
|
+
return AuditCheck(
|
|
1211
|
+
check_id="PRF-004", check_name="Test Collection",
|
|
1212
|
+
category="performance", status="skip", severity="blocking",
|
|
1213
|
+
message="pytest not installed", details={},
|
|
1214
|
+
)
|
|
1215
|
+
# Parse "N tests collected"
|
|
1216
|
+
match = re.search(r"(\d+)\s+test", stdout)
|
|
1217
|
+
count = int(match.group(1)) if match else 0
|
|
1218
|
+
ok = rc == 0 and count > 0
|
|
1219
|
+
return AuditCheck(
|
|
1220
|
+
check_id="PRF-004", check_name="Test Collection",
|
|
1221
|
+
category="performance", status="pass" if ok else "fail",
|
|
1222
|
+
severity="blocking",
|
|
1223
|
+
message=f"{count} tests collected" if ok else f"Collection failed (exit {rc})",
|
|
1224
|
+
details={"test_count": count, "exit_code": rc},
|
|
1225
|
+
)
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
# ---------------------------------------------------------------------------
|
|
1229
|
+
# Category 7: Code Quality Intelligence (CODE-001..005) — Phase 52
|
|
1230
|
+
# ---------------------------------------------------------------------------
|
|
1231
|
+
|
|
1232
|
+
def check_code_analyzer_syntax() -> AuditCheck:
|
|
1233
|
+
"""CODE-001: Code analyzer tool syntax check."""
|
|
1234
|
+
try:
|
|
1235
|
+
mod_path = PROJECT_ROOT / "tools" / "analysis" / "code_analyzer.py"
|
|
1236
|
+
if not mod_path.exists():
|
|
1237
|
+
return AuditCheck(
|
|
1238
|
+
check_id="CODE-001", check_name="Code Analyzer Syntax",
|
|
1239
|
+
category="code_quality", status="skip", severity="warning",
|
|
1240
|
+
message="code_analyzer.py not found", details={},
|
|
1241
|
+
)
|
|
1242
|
+
with open(mod_path, "r", encoding="utf-8") as f:
|
|
1243
|
+
source = f.read()
|
|
1244
|
+
ast.parse(source)
|
|
1245
|
+
return AuditCheck(
|
|
1246
|
+
check_id="CODE-001", check_name="Code Analyzer Syntax",
|
|
1247
|
+
category="code_quality", status="pass", severity="warning",
|
|
1248
|
+
message="code_analyzer.py parses without errors",
|
|
1249
|
+
details={"file": str(mod_path)},
|
|
1250
|
+
)
|
|
1251
|
+
except SyntaxError as e:
|
|
1252
|
+
return AuditCheck(
|
|
1253
|
+
check_id="CODE-001", check_name="Code Analyzer Syntax",
|
|
1254
|
+
category="code_quality", status="fail", severity="warning",
|
|
1255
|
+
message=f"Syntax error: {e}",
|
|
1256
|
+
details={"error": str(e)},
|
|
1257
|
+
)
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def check_avg_complexity() -> AuditCheck:
|
|
1261
|
+
"""CODE-002: Average cyclomatic complexity (blocking at >25, warn >10)."""
|
|
1262
|
+
try:
|
|
1263
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
1264
|
+
from tools.analysis.code_analyzer import CodeAnalyzer
|
|
1265
|
+
analyzer = CodeAnalyzer(project_dir=str(PROJECT_ROOT / "tools"))
|
|
1266
|
+
result = analyzer.scan_directory()
|
|
1267
|
+
metrics = result.get("metrics", [])
|
|
1268
|
+
fn_metrics = [m for m in metrics if m.get("function_name")]
|
|
1269
|
+
if not fn_metrics:
|
|
1270
|
+
return AuditCheck(
|
|
1271
|
+
check_id="CODE-002", check_name="Avg Cyclomatic Complexity",
|
|
1272
|
+
category="code_quality", status="skip", severity="blocking",
|
|
1273
|
+
message="No function metrics collected", details={},
|
|
1274
|
+
)
|
|
1275
|
+
avg_cc = sum(m.get("cyclomatic_complexity", 0) for m in fn_metrics) / len(fn_metrics)
|
|
1276
|
+
avg_cc = round(avg_cc, 2)
|
|
1277
|
+
if avg_cc > 25:
|
|
1278
|
+
status = "fail"
|
|
1279
|
+
elif avg_cc > 10:
|
|
1280
|
+
status = "warn"
|
|
1281
|
+
else:
|
|
1282
|
+
status = "pass"
|
|
1283
|
+
return AuditCheck(
|
|
1284
|
+
check_id="CODE-002", check_name="Avg Cyclomatic Complexity",
|
|
1285
|
+
category="code_quality", status=status, severity="blocking",
|
|
1286
|
+
message=f"Avg CC={avg_cc} across {len(fn_metrics)} functions",
|
|
1287
|
+
details={"avg_complexity": avg_cc, "function_count": len(fn_metrics)},
|
|
1288
|
+
)
|
|
1289
|
+
except Exception as e:
|
|
1290
|
+
return AuditCheck(
|
|
1291
|
+
check_id="CODE-002", check_name="Avg Cyclomatic Complexity",
|
|
1292
|
+
category="code_quality", status="skip", severity="blocking",
|
|
1293
|
+
message=f"Analysis failed: {e}", details={"error": str(e)},
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
def check_high_complexity_pct() -> AuditCheck:
|
|
1298
|
+
"""CODE-003: High-complexity function count (warn if >7% have CC>15).
|
|
1299
|
+
|
|
1300
|
+
Threshold set at 7% to account for compliance assessors (CMMC, FedRAMP,
|
|
1301
|
+
ATLAS, OWASP) which contain regulatory dispatch tables with inherently
|
|
1302
|
+
high cyclomatic complexity. The blocking CODE-002 gate (avg CC>25)
|
|
1303
|
+
remains unchanged.
|
|
1304
|
+
"""
|
|
1305
|
+
try:
|
|
1306
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
1307
|
+
from tools.analysis.code_analyzer import CodeAnalyzer
|
|
1308
|
+
analyzer = CodeAnalyzer(project_dir=str(PROJECT_ROOT / "tools"))
|
|
1309
|
+
result = analyzer.scan_directory()
|
|
1310
|
+
metrics = result.get("metrics", [])
|
|
1311
|
+
fn_metrics = [m for m in metrics if m.get("function_name")]
|
|
1312
|
+
if not fn_metrics:
|
|
1313
|
+
return AuditCheck(
|
|
1314
|
+
check_id="CODE-003", check_name="High Complexity Functions",
|
|
1315
|
+
category="code_quality", status="skip", severity="warning",
|
|
1316
|
+
message="No function metrics collected", details={},
|
|
1317
|
+
)
|
|
1318
|
+
high_cc = [m for m in fn_metrics if m.get("cyclomatic_complexity", 0) > 15]
|
|
1319
|
+
pct = round(len(high_cc) / len(fn_metrics) * 100, 2)
|
|
1320
|
+
return AuditCheck(
|
|
1321
|
+
check_id="CODE-003", check_name="High Complexity Functions",
|
|
1322
|
+
category="code_quality", status="warn" if pct > 7.0 else "pass",
|
|
1323
|
+
severity="warning",
|
|
1324
|
+
message=f"{len(high_cc)}/{len(fn_metrics)} functions ({pct}%) have CC>15",
|
|
1325
|
+
details={"high_cc_count": len(high_cc), "total": len(fn_metrics), "pct": pct},
|
|
1326
|
+
)
|
|
1327
|
+
except Exception as e:
|
|
1328
|
+
return AuditCheck(
|
|
1329
|
+
check_id="CODE-003", check_name="High Complexity Functions",
|
|
1330
|
+
category="code_quality", status="skip", severity="warning",
|
|
1331
|
+
message=f"Analysis failed: {e}", details={"error": str(e)},
|
|
1332
|
+
)
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
def check_smell_density() -> AuditCheck:
|
|
1336
|
+
"""CODE-004: Smell density per KLOC (warn >20, fail >30).
|
|
1337
|
+
|
|
1338
|
+
Threshold calibrated for compliance-heavy codebases where regulatory
|
|
1339
|
+
dispatch functions and multi-framework assessors inflate smell counts.
|
|
1340
|
+
"""
|
|
1341
|
+
try:
|
|
1342
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
1343
|
+
from tools.analysis.code_analyzer import CodeAnalyzer
|
|
1344
|
+
analyzer = CodeAnalyzer(project_dir=str(PROJECT_ROOT / "tools"))
|
|
1345
|
+
result = analyzer.scan_directory()
|
|
1346
|
+
metrics = result.get("metrics", [])
|
|
1347
|
+
total_loc = sum(m.get("loc", 0) for m in metrics)
|
|
1348
|
+
total_smells = sum(m.get("smell_count", 0) for m in metrics)
|
|
1349
|
+
kloc = max(total_loc / 1000.0, 0.001)
|
|
1350
|
+
density = round(total_smells / kloc, 2)
|
|
1351
|
+
if density > 30.0:
|
|
1352
|
+
status = "fail"
|
|
1353
|
+
elif density > 20.0:
|
|
1354
|
+
status = "warn"
|
|
1355
|
+
else:
|
|
1356
|
+
status = "pass"
|
|
1357
|
+
return AuditCheck(
|
|
1358
|
+
check_id="CODE-004", check_name="Smell Density",
|
|
1359
|
+
category="code_quality", status=status, severity="warning",
|
|
1360
|
+
message=f"{total_smells} smells / {round(kloc, 1)} KLOC = {density} per KLOC",
|
|
1361
|
+
details={"total_smells": total_smells, "total_loc": total_loc, "density_per_kloc": density},
|
|
1362
|
+
)
|
|
1363
|
+
except Exception as e:
|
|
1364
|
+
return AuditCheck(
|
|
1365
|
+
check_id="CODE-004", check_name="Smell Density",
|
|
1366
|
+
category="code_quality", status="skip", severity="warning",
|
|
1367
|
+
message=f"Analysis failed: {e}", details={"error": str(e)},
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
|
|
1371
|
+
def check_maintainability_trend() -> AuditCheck:
|
|
1372
|
+
"""CODE-005: Maintainability trend (warn if declining)."""
|
|
1373
|
+
try:
|
|
1374
|
+
sys.path.insert(0, str(PROJECT_ROOT))
|
|
1375
|
+
from tools.analysis.code_analyzer import CodeAnalyzer
|
|
1376
|
+
analyzer = CodeAnalyzer(
|
|
1377
|
+
project_dir=str(PROJECT_ROOT / "tools"),
|
|
1378
|
+
project_id="sparkpilot",
|
|
1379
|
+
db_path=DB_PATH,
|
|
1380
|
+
)
|
|
1381
|
+
trend = analyzer.get_trend("sparkpilot", db_path=DB_PATH)
|
|
1382
|
+
if len(trend) < 2:
|
|
1383
|
+
return AuditCheck(
|
|
1384
|
+
check_id="CODE-005", check_name="Maintainability Trend",
|
|
1385
|
+
category="code_quality", status="skip", severity="warning",
|
|
1386
|
+
message=f"Need >=2 scans for trend (have {len(trend)})",
|
|
1387
|
+
details={"scan_count": len(trend)},
|
|
1388
|
+
)
|
|
1389
|
+
latest = trend[-1].get("avg_maintainability", 0)
|
|
1390
|
+
previous = trend[-2].get("avg_maintainability", 0)
|
|
1391
|
+
declining = latest < previous - 0.05
|
|
1392
|
+
return AuditCheck(
|
|
1393
|
+
check_id="CODE-005", check_name="Maintainability Trend",
|
|
1394
|
+
category="code_quality", status="warn" if declining else "pass",
|
|
1395
|
+
severity="warning",
|
|
1396
|
+
message=f"Latest={round(latest, 3)}, Previous={round(previous, 3)}" + (" (declining)" if declining else ""),
|
|
1397
|
+
details={"latest": latest, "previous": previous, "declining": declining},
|
|
1398
|
+
)
|
|
1399
|
+
except Exception as e:
|
|
1400
|
+
return AuditCheck(
|
|
1401
|
+
check_id="CODE-005", check_name="Maintainability Trend",
|
|
1402
|
+
category="code_quality", status="skip", severity="warning",
|
|
1403
|
+
message=f"Trend unavailable: {e}", details={"error": str(e)},
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
# ---------------------------------------------------------------------------
|
|
1408
|
+
# Category 8: Documentation Alignment (DOC-001..005)
|
|
1409
|
+
# ---------------------------------------------------------------------------
|
|
1410
|
+
|
|
1411
|
+
def check_claude_md_table_count() -> AuditCheck:
|
|
1412
|
+
"""DOC-001: CLAUDE.md table count accuracy."""
|
|
1413
|
+
claude_md = PROJECT_ROOT / "CLAUDE.md"
|
|
1414
|
+
if not claude_md.exists():
|
|
1415
|
+
return AuditCheck(
|
|
1416
|
+
check_id="DOC-001", check_name="CLAUDE.md Table Count",
|
|
1417
|
+
category="documentation", status="skip", severity="warning",
|
|
1418
|
+
message="CLAUDE.md not found", details={},
|
|
1419
|
+
)
|
|
1420
|
+
try:
|
|
1421
|
+
content = claude_md.read_text(encoding="utf-8")
|
|
1422
|
+
# Find claimed table count (e.g. "176 tables")
|
|
1423
|
+
match = re.search(r"(\d+)\s+tables", content)
|
|
1424
|
+
claimed = int(match.group(1)) if match else 0
|
|
1425
|
+
# Get actual count
|
|
1426
|
+
if True: # Storage layer handles path resolution (D-DB-20)
|
|
1427
|
+
conn = get_connection()
|
|
1428
|
+
actual = len(conn.execute(
|
|
1429
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
|
1430
|
+
).fetchall())
|
|
1431
|
+
conn.close()
|
|
1432
|
+
else:
|
|
1433
|
+
actual = 0
|
|
1434
|
+
drift = abs(claimed - actual)
|
|
1435
|
+
ok = drift <= 5
|
|
1436
|
+
return AuditCheck(
|
|
1437
|
+
check_id="DOC-001", check_name="CLAUDE.md Table Count",
|
|
1438
|
+
category="documentation", status="pass" if ok else "warn",
|
|
1439
|
+
severity="warning",
|
|
1440
|
+
message=f"Claimed {claimed}, actual {actual} (drift={drift})",
|
|
1441
|
+
details={"claimed": claimed, "actual": actual, "drift": drift},
|
|
1442
|
+
)
|
|
1443
|
+
except Exception as e:
|
|
1444
|
+
return AuditCheck(
|
|
1445
|
+
check_id="DOC-001", check_name="CLAUDE.md Table Count",
|
|
1446
|
+
category="documentation", status="warn", severity="warning",
|
|
1447
|
+
message=str(e), details={},
|
|
1448
|
+
)
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
def check_tools_manifest() -> AuditCheck:
|
|
1452
|
+
"""DOC-002: tools/manifest.md completeness."""
|
|
1453
|
+
manifest = PROJECT_ROOT / "tools" / "manifest.md"
|
|
1454
|
+
if not manifest.exists():
|
|
1455
|
+
return AuditCheck(
|
|
1456
|
+
check_id="DOC-002", check_name="Tools Manifest",
|
|
1457
|
+
category="documentation", status="warn", severity="warning",
|
|
1458
|
+
message="tools/manifest.md not found", details={},
|
|
1459
|
+
)
|
|
1460
|
+
try:
|
|
1461
|
+
content = manifest.read_text(encoding="utf-8")
|
|
1462
|
+
# Count tools mentioned in manifest
|
|
1463
|
+
listed = set(re.findall(r'(\w+\.py)', content))
|
|
1464
|
+
# Count actual tool files
|
|
1465
|
+
actual = set()
|
|
1466
|
+
for py in (PROJECT_ROOT / "tools").rglob("*.py"):
|
|
1467
|
+
if not py.name.startswith("__"):
|
|
1468
|
+
actual.add(py.name)
|
|
1469
|
+
missing = actual - listed
|
|
1470
|
+
pct = round(len(listed & actual) / max(len(actual), 1) * 100, 1)
|
|
1471
|
+
ok = pct >= 70
|
|
1472
|
+
return AuditCheck(
|
|
1473
|
+
check_id="DOC-002", check_name="Tools Manifest",
|
|
1474
|
+
category="documentation", status="pass" if ok else "warn",
|
|
1475
|
+
severity="warning",
|
|
1476
|
+
message=f"{len(listed & actual)}/{len(actual)} tools documented ({pct}%)",
|
|
1477
|
+
details={"documented": len(listed & actual), "total": len(actual),
|
|
1478
|
+
"pct": pct, "missing_sample": sorted(missing)[:10]},
|
|
1479
|
+
)
|
|
1480
|
+
except Exception as e:
|
|
1481
|
+
return AuditCheck(
|
|
1482
|
+
check_id="DOC-002", check_name="Tools Manifest",
|
|
1483
|
+
category="documentation", status="warn", severity="warning",
|
|
1484
|
+
message=str(e), details={},
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
def check_goals_manifest() -> AuditCheck:
|
|
1489
|
+
"""DOC-003: goals/manifest.md completeness."""
|
|
1490
|
+
manifest = PROJECT_ROOT / "goals" / "manifest.md"
|
|
1491
|
+
goals_dir = PROJECT_ROOT / "goals"
|
|
1492
|
+
if not manifest.exists():
|
|
1493
|
+
return AuditCheck(
|
|
1494
|
+
check_id="DOC-003", check_name="Goals Manifest",
|
|
1495
|
+
category="documentation", status="warn", severity="warning",
|
|
1496
|
+
message="goals/manifest.md not found", details={},
|
|
1497
|
+
)
|
|
1498
|
+
try:
|
|
1499
|
+
content = manifest.read_text(encoding="utf-8")
|
|
1500
|
+
listed = set(re.findall(r'(\w+\.md)', content))
|
|
1501
|
+
actual = {f.name for f in goals_dir.glob("*.md") if f.name != "manifest.md"}
|
|
1502
|
+
missing = actual - listed
|
|
1503
|
+
pct = round(len(listed & actual) / max(len(actual), 1) * 100, 1)
|
|
1504
|
+
ok = pct >= 80
|
|
1505
|
+
return AuditCheck(
|
|
1506
|
+
check_id="DOC-003", check_name="Goals Manifest",
|
|
1507
|
+
category="documentation", status="pass" if ok else "warn",
|
|
1508
|
+
severity="warning",
|
|
1509
|
+
message=f"{len(listed & actual)}/{len(actual)} goals documented ({pct}%)",
|
|
1510
|
+
details={"documented": len(listed & actual), "total": len(actual),
|
|
1511
|
+
"pct": pct, "missing": sorted(missing)[:10]},
|
|
1512
|
+
)
|
|
1513
|
+
except Exception as e:
|
|
1514
|
+
return AuditCheck(
|
|
1515
|
+
check_id="DOC-003", check_name="Goals Manifest",
|
|
1516
|
+
category="documentation", status="warn", severity="warning",
|
|
1517
|
+
message=str(e), details={},
|
|
1518
|
+
)
|
|
1519
|
+
|
|
1520
|
+
|
|
1521
|
+
def check_route_documentation() -> AuditCheck:
|
|
1522
|
+
"""DOC-004: Dashboard routes documented in start.md."""
|
|
1523
|
+
start_md = PROJECT_ROOT / ".claude" / "commands" / "start.md"
|
|
1524
|
+
if not start_md.exists():
|
|
1525
|
+
return AuditCheck(
|
|
1526
|
+
check_id="DOC-004", check_name="Route Documentation",
|
|
1527
|
+
category="documentation", status="skip", severity="warning",
|
|
1528
|
+
message="start.md not found", details={},
|
|
1529
|
+
)
|
|
1530
|
+
try:
|
|
1531
|
+
content = start_md.read_text(encoding="utf-8")
|
|
1532
|
+
# Count documented routes
|
|
1533
|
+
routes = re.findall(r'`(/\w[^`]*)`', content)
|
|
1534
|
+
ok = len(routes) >= 20
|
|
1535
|
+
return AuditCheck(
|
|
1536
|
+
check_id="DOC-004", check_name="Route Documentation",
|
|
1537
|
+
category="documentation", status="pass" if ok else "warn",
|
|
1538
|
+
severity="warning",
|
|
1539
|
+
message=f"{len(routes)} routes documented in start.md",
|
|
1540
|
+
details={"route_count": len(routes)},
|
|
1541
|
+
)
|
|
1542
|
+
except Exception as e:
|
|
1543
|
+
return AuditCheck(
|
|
1544
|
+
check_id="DOC-004", check_name="Route Documentation",
|
|
1545
|
+
category="documentation", status="warn", severity="warning",
|
|
1546
|
+
message=str(e), details={},
|
|
1547
|
+
)
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
def check_skill_count() -> AuditCheck:
|
|
1551
|
+
"""DOC-005: Skill/command count accuracy."""
|
|
1552
|
+
commands_dir = PROJECT_ROOT / ".claude" / "commands"
|
|
1553
|
+
if not commands_dir.exists():
|
|
1554
|
+
return AuditCheck(
|
|
1555
|
+
check_id="DOC-005", check_name="Skill Count",
|
|
1556
|
+
category="documentation", status="skip", severity="warning",
|
|
1557
|
+
message=".claude/commands/ not found", details={},
|
|
1558
|
+
)
|
|
1559
|
+
skills = list(commands_dir.glob("*.md"))
|
|
1560
|
+
# Exclude e2e subdir
|
|
1561
|
+
e2e_skills = list((commands_dir / "e2e").glob("*.md")) if (commands_dir / "e2e").exists() else []
|
|
1562
|
+
total = len(skills) + len(e2e_skills)
|
|
1563
|
+
return AuditCheck(
|
|
1564
|
+
check_id="DOC-005", check_name="Skill Count",
|
|
1565
|
+
category="documentation", status="pass" if total >= 20 else "warn",
|
|
1566
|
+
severity="warning",
|
|
1567
|
+
message=f"{len(skills)} skills + {len(e2e_skills)} E2E specs = {total} total",
|
|
1568
|
+
details={"skills": len(skills), "e2e_specs": len(e2e_skills), "total": total},
|
|
1569
|
+
)
|
|
1570
|
+
|
|
1571
|
+
|
|
1572
|
+
# ---------------------------------------------------------------------------
|
|
1573
|
+
# Check Registry
|
|
1574
|
+
# ---------------------------------------------------------------------------
|
|
1575
|
+
|
|
1576
|
+
# Maps check_id -> (function, category, severity)
|
|
1577
|
+
# Order within each category determines execution order
|
|
1578
|
+
CHECK_REGISTRY: Dict[str, Tuple[Callable, str, str]] = {
|
|
1579
|
+
# Platform
|
|
1580
|
+
"PLT-001": (check_platform_compat, "platform", "warning"),
|
|
1581
|
+
"PLT-002": (check_python_version, "platform", "blocking"),
|
|
1582
|
+
"PLT-003": (check_stdlib_modules, "platform", "blocking"),
|
|
1583
|
+
"PLT-004": (check_dockerfile_syntax, "platform", "warning"),
|
|
1584
|
+
# Security
|
|
1585
|
+
"SEC-001": (check_sast_bandit, "security", "blocking"),
|
|
1586
|
+
"SEC-002": (check_dependency_audit, "security", "blocking"),
|
|
1587
|
+
"SEC-003": (check_secret_detection, "security", "blocking"),
|
|
1588
|
+
"SEC-004": (check_prompt_injection_gate, "security", "blocking"),
|
|
1589
|
+
"SEC-005": (check_owasp_agentic, "security", "warning"),
|
|
1590
|
+
"SEC-006": (check_code_pattern_scan, "security", "blocking"),
|
|
1591
|
+
"SEC-007": (check_owasp_asi, "security", "warning"),
|
|
1592
|
+
"SEC-008": (check_endpoint_security, "security", "blocking"),
|
|
1593
|
+
# Compliance
|
|
1594
|
+
"CMP-001": (check_cui_markings, "compliance", "warning"),
|
|
1595
|
+
"CMP-002": (check_claude_governance, "compliance", "blocking"),
|
|
1596
|
+
"CMP-003": (check_append_only_tables, "compliance", "blocking"),
|
|
1597
|
+
"CMP-004": (check_security_gates_config, "compliance", "warning"),
|
|
1598
|
+
"CMP-005": (check_xai_compliance, "compliance", "warning"),
|
|
1599
|
+
"CMP-006": (check_sbom_generation, "compliance", "warning"),
|
|
1600
|
+
"CMP-007": (check_oscal_ecosystem, "compliance", "warning"),
|
|
1601
|
+
"CMP-008": (check_fedramp_20x_ksi, "compliance", "warning"),
|
|
1602
|
+
"CMP-009": (check_slsa_swft, "compliance", "warning"),
|
|
1603
|
+
# AI Transparency (Phase 48)
|
|
1604
|
+
"AI-001": (check_ai_inventory, "compliance", "warning"),
|
|
1605
|
+
"AI-002": (check_model_cards, "compliance", "warning"),
|
|
1606
|
+
"AI-003": (check_ai_transparency_frameworks, "compliance", "warning"),
|
|
1607
|
+
# Integration
|
|
1608
|
+
"INT-001": (check_mcp_servers, "integration", "blocking"),
|
|
1609
|
+
"INT-002": (check_db_schema, "integration", "blocking"),
|
|
1610
|
+
"INT-003": (check_cross_imports, "integration", "warning"),
|
|
1611
|
+
"INT-004": (check_dashboard_health, "integration", "warning"),
|
|
1612
|
+
"INT-005": (check_api_gateway, "integration", "warning"),
|
|
1613
|
+
# Performance
|
|
1614
|
+
"PRF-001": (check_migration_status, "performance", "warning"),
|
|
1615
|
+
"PRF-002": (check_backup_config, "performance", "warning"),
|
|
1616
|
+
"PRF-003": (check_resilience_config, "performance", "warning"),
|
|
1617
|
+
"PRF-004": (check_test_collection, "performance", "blocking"),
|
|
1618
|
+
# Documentation
|
|
1619
|
+
"DOC-001": (check_claude_md_table_count, "documentation", "warning"),
|
|
1620
|
+
"DOC-002": (check_tools_manifest, "documentation", "warning"),
|
|
1621
|
+
"DOC-003": (check_goals_manifest, "documentation", "warning"),
|
|
1622
|
+
"DOC-004": (check_route_documentation, "documentation", "warning"),
|
|
1623
|
+
"DOC-005": (check_skill_count, "documentation", "warning"),
|
|
1624
|
+
# Code Quality (Phase 52)
|
|
1625
|
+
"CODE-001": (check_code_analyzer_syntax, "code_quality", "warning"),
|
|
1626
|
+
"CODE-002": (check_avg_complexity, "code_quality", "blocking"),
|
|
1627
|
+
"CODE-003": (check_high_complexity_pct, "code_quality", "warning"),
|
|
1628
|
+
"CODE-004": (check_smell_density, "code_quality", "warning"),
|
|
1629
|
+
"CODE-005": (check_maintainability_trend, "code_quality", "warning"),
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
# Execution order of categories
|
|
1633
|
+
CATEGORY_ORDER = ["platform", "security", "compliance", "integration", "performance", "documentation", "code_quality"]
|
|
1634
|
+
|
|
1635
|
+
ALL_CATEGORIES = set(CATEGORY_ORDER)
|
|
1636
|
+
|
|
1637
|
+
|
|
1638
|
+
# ---------------------------------------------------------------------------
|
|
1639
|
+
# Runner
|
|
1640
|
+
# ---------------------------------------------------------------------------
|
|
1641
|
+
|
|
1642
|
+
def run_audit(
|
|
1643
|
+
categories: Optional[List[str]] = None,
|
|
1644
|
+
stream: bool = False,
|
|
1645
|
+
) -> AuditReport:
|
|
1646
|
+
"""Run the production readiness audit.
|
|
1647
|
+
|
|
1648
|
+
Args:
|
|
1649
|
+
categories: Optional list of categories to run. None = all.
|
|
1650
|
+
stream: If True, print results as each check completes.
|
|
1651
|
+
|
|
1652
|
+
Returns:
|
|
1653
|
+
AuditReport with all results.
|
|
1654
|
+
"""
|
|
1655
|
+
if categories is None:
|
|
1656
|
+
categories = CATEGORY_ORDER
|
|
1657
|
+
else:
|
|
1658
|
+
categories = [c for c in CATEGORY_ORDER if c in categories]
|
|
1659
|
+
|
|
1660
|
+
checks: List[AuditCheck] = []
|
|
1661
|
+
start_time = time.time()
|
|
1662
|
+
|
|
1663
|
+
for cat in categories:
|
|
1664
|
+
if stream:
|
|
1665
|
+
print(f"\n{'='*60}", file=sys.stderr)
|
|
1666
|
+
print(f" Category: {cat.upper()}", file=sys.stderr)
|
|
1667
|
+
print(f"{'='*60}", file=sys.stderr)
|
|
1668
|
+
|
|
1669
|
+
cat_checks = [
|
|
1670
|
+
(cid, fn, sev) for cid, (fn, c, sev) in CHECK_REGISTRY.items() if c == cat
|
|
1671
|
+
]
|
|
1672
|
+
for check_id, fn, severity in cat_checks:
|
|
1673
|
+
result, duration = _timed(fn)
|
|
1674
|
+
result.duration_ms = duration
|
|
1675
|
+
checks.append(result)
|
|
1676
|
+
|
|
1677
|
+
if stream:
|
|
1678
|
+
icon = {"pass": "[PASS]", "fail": "[FAIL]", "warn": "[WARN]", "skip": "[SKIP]"}.get(result.status, "[????]")
|
|
1679
|
+
print(f" {icon} {result.check_id}: {result.check_name} — {result.message} ({duration}ms)", file=sys.stderr)
|
|
1680
|
+
|
|
1681
|
+
# Build report
|
|
1682
|
+
total_ms = int((time.time() - start_time) * 1000)
|
|
1683
|
+
cat_summary = {}
|
|
1684
|
+
for cat in categories:
|
|
1685
|
+
cat_checks_list = [c for c in checks if c.category == cat]
|
|
1686
|
+
cat_summary[cat] = {
|
|
1687
|
+
"pass": sum(1 for c in cat_checks_list if c.status == "pass"),
|
|
1688
|
+
"fail": sum(1 for c in cat_checks_list if c.status == "fail"),
|
|
1689
|
+
"warn": sum(1 for c in cat_checks_list if c.status == "warn"),
|
|
1690
|
+
"skip": sum(1 for c in cat_checks_list if c.status == "skip"),
|
|
1691
|
+
"checks": [c.to_dict() for c in cat_checks_list],
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
blockers = [
|
|
1695
|
+
f"{c.check_id}: {c.message}"
|
|
1696
|
+
for c in checks if c.status == "fail" and c.severity == "blocking"
|
|
1697
|
+
]
|
|
1698
|
+
warnings = [
|
|
1699
|
+
f"{c.check_id}: {c.message}"
|
|
1700
|
+
for c in checks if c.status in ("fail", "warn")
|
|
1701
|
+
]
|
|
1702
|
+
|
|
1703
|
+
report = AuditReport(
|
|
1704
|
+
overall_pass=len(blockers) == 0,
|
|
1705
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
1706
|
+
categories=cat_summary,
|
|
1707
|
+
total_checks=len(checks),
|
|
1708
|
+
passed=sum(1 for c in checks if c.status == "pass"),
|
|
1709
|
+
failed=sum(1 for c in checks if c.status == "fail"),
|
|
1710
|
+
warned=sum(1 for c in checks if c.status == "warn"),
|
|
1711
|
+
skipped=sum(1 for c in checks if c.status == "skip"),
|
|
1712
|
+
blockers=blockers,
|
|
1713
|
+
warnings=warnings,
|
|
1714
|
+
duration_total_ms=total_ms,
|
|
1715
|
+
)
|
|
1716
|
+
|
|
1717
|
+
# Store in DB (append-only)
|
|
1718
|
+
_store_report(report, categories)
|
|
1719
|
+
|
|
1720
|
+
return report
|
|
1721
|
+
|
|
1722
|
+
|
|
1723
|
+
def _store_report(report: AuditReport, categories: List[str]):
|
|
1724
|
+
"""Store audit report in production_audits table (append-only)."""
|
|
1725
|
+
try:
|
|
1726
|
+
conn = get_connection()
|
|
1727
|
+
conn.execute(
|
|
1728
|
+
"""INSERT INTO production_audits
|
|
1729
|
+
(overall_pass, total_checks, passed, failed, warned, skipped,
|
|
1730
|
+
blockers, warnings, categories_run, report_json, duration_ms)
|
|
1731
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1732
|
+
(
|
|
1733
|
+
1 if report.overall_pass else 0,
|
|
1734
|
+
report.total_checks,
|
|
1735
|
+
report.passed,
|
|
1736
|
+
report.failed,
|
|
1737
|
+
report.warned,
|
|
1738
|
+
report.skipped,
|
|
1739
|
+
json.dumps(report.blockers),
|
|
1740
|
+
json.dumps(report.warnings),
|
|
1741
|
+
json.dumps(categories),
|
|
1742
|
+
json.dumps(report.to_dict()),
|
|
1743
|
+
report.duration_total_ms,
|
|
1744
|
+
),
|
|
1745
|
+
)
|
|
1746
|
+
conn.commit()
|
|
1747
|
+
conn.close()
|
|
1748
|
+
except Exception:
|
|
1749
|
+
pass # Don't fail the audit because DB write failed
|
|
1750
|
+
|
|
1751
|
+
|
|
1752
|
+
# ---------------------------------------------------------------------------
|
|
1753
|
+
# Output formatters
|
|
1754
|
+
# ---------------------------------------------------------------------------
|
|
1755
|
+
|
|
1756
|
+
def _format_human(report: AuditReport) -> str:
|
|
1757
|
+
"""Format report for human-readable terminal output."""
|
|
1758
|
+
lines = []
|
|
1759
|
+
lines.append("")
|
|
1760
|
+
lines.append("=" * 60)
|
|
1761
|
+
lines.append(" SPARKPILOT Production Readiness Audit")
|
|
1762
|
+
lines.append("=" * 60)
|
|
1763
|
+
lines.append("")
|
|
1764
|
+
|
|
1765
|
+
for cat, summary in report.categories.items():
|
|
1766
|
+
lines.append(f" --- {cat.upper()} ---")
|
|
1767
|
+
for check in summary.get("checks", []):
|
|
1768
|
+
icon = {"pass": "[PASS]", "fail": "[FAIL]", "warn": "[WARN]", "skip": "[SKIP]"}.get(check["status"], "[????]")
|
|
1769
|
+
sev_tag = " (BLOCKING)" if check["severity"] == "blocking" and check["status"] == "fail" else ""
|
|
1770
|
+
lines.append(f" {icon} {check['check_id']}: {check['check_name']}{sev_tag}")
|
|
1771
|
+
lines.append(f" {check['message']} ({check['duration_ms']}ms)")
|
|
1772
|
+
lines.append("")
|
|
1773
|
+
|
|
1774
|
+
lines.append("-" * 60)
|
|
1775
|
+
status = "READY" if report.overall_pass else "BLOCKED"
|
|
1776
|
+
lines.append(f" Overall: {status}")
|
|
1777
|
+
lines.append(f" Checks: {report.passed} passed, {report.failed} failed, {report.warned} warned, {report.skipped} skipped")
|
|
1778
|
+
lines.append(f" Duration: {report.duration_total_ms}ms")
|
|
1779
|
+
|
|
1780
|
+
if report.blockers:
|
|
1781
|
+
lines.append("")
|
|
1782
|
+
lines.append(" BLOCKERS (must fix before production):")
|
|
1783
|
+
for b in report.blockers:
|
|
1784
|
+
lines.append(f" - {b}")
|
|
1785
|
+
|
|
1786
|
+
if report.warnings:
|
|
1787
|
+
non_blocker_warnings = [w for w in report.warnings if w not in report.blockers]
|
|
1788
|
+
if non_blocker_warnings:
|
|
1789
|
+
lines.append("")
|
|
1790
|
+
lines.append(" WARNINGS (should fix):")
|
|
1791
|
+
for w in non_blocker_warnings[:10]:
|
|
1792
|
+
lines.append(f" - {w}")
|
|
1793
|
+
|
|
1794
|
+
lines.append("=" * 60)
|
|
1795
|
+
return "\n".join(lines)
|
|
1796
|
+
|
|
1797
|
+
|
|
1798
|
+
# ---------------------------------------------------------------------------
|
|
1799
|
+
# CLI
|
|
1800
|
+
# ---------------------------------------------------------------------------
|
|
1801
|
+
|
|
1802
|
+
def main():
|
|
1803
|
+
parser = argparse.ArgumentParser(description="SPARKPILOT Production Readiness Audit")
|
|
1804
|
+
parser.add_argument("--json", action="store_true", help="JSON output")
|
|
1805
|
+
parser.add_argument("--human", action="store_true", help="Human-readable output")
|
|
1806
|
+
parser.add_argument("--stream", action="store_true", help="Stream results as they complete")
|
|
1807
|
+
parser.add_argument("--gate", action="store_true", help="Exit 1 if any blocker fails")
|
|
1808
|
+
parser.add_argument("--category", type=str, default=None,
|
|
1809
|
+
help="Comma-separated categories: platform,security,compliance,integration,performance,documentation")
|
|
1810
|
+
args = parser.parse_args()
|
|
1811
|
+
|
|
1812
|
+
categories = None
|
|
1813
|
+
if args.category:
|
|
1814
|
+
categories = [c.strip() for c in args.category.split(",") if c.strip() in ALL_CATEGORIES]
|
|
1815
|
+
if not categories:
|
|
1816
|
+
print(f"Invalid categories. Valid: {', '.join(CATEGORY_ORDER)}", file=sys.stderr)
|
|
1817
|
+
sys.exit(2)
|
|
1818
|
+
|
|
1819
|
+
# Default to stream + human if neither --json nor --human specified
|
|
1820
|
+
if not args.json and not args.human:
|
|
1821
|
+
args.human = True
|
|
1822
|
+
args.stream = True
|
|
1823
|
+
|
|
1824
|
+
report = run_audit(categories=categories, stream=args.stream or args.human)
|
|
1825
|
+
|
|
1826
|
+
if args.json:
|
|
1827
|
+
print(json.dumps(report.to_dict(), indent=2))
|
|
1828
|
+
else:
|
|
1829
|
+
print(_format_human(report))
|
|
1830
|
+
|
|
1831
|
+
if args.gate or True: # Always exit with appropriate code
|
|
1832
|
+
sys.exit(0 if report.overall_pass else 1)
|
|
1833
|
+
|
|
1834
|
+
|
|
1835
|
+
if __name__ == "__main__":
|
|
1836
|
+
main()
|