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,2070 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# CUI // SP-CTI
|
|
3
|
+
"""SbD assessment tool per CISA Secure by Design and DoDI 5000.87.
|
|
4
|
+
|
|
5
|
+
Loads SbD requirements from cisa_sbd_requirements.json, performs automated checks
|
|
6
|
+
where possible, stores results in sbd_assessments table, evaluates SbD gates,
|
|
7
|
+
applies CUI markings, and logs audit events."""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
from datetime import datetime, timezone
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from tools.db.storage import get_connection
|
|
17
|
+
DB_PATH = None # Storage layer handles path resolution (D-DB-20)
|
|
18
|
+
|
|
19
|
+
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
20
|
+
SBD_REQUIREMENTS_PATH = BASE_DIR / "context" / "compliance" / "cisa_sbd_requirements.json"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# -----------------------------------------------------------------
|
|
24
|
+
# Database helpers
|
|
25
|
+
# -----------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_project(conn, project_id):
|
|
29
|
+
"""Load project data from the projects table."""
|
|
30
|
+
row = conn.execute(
|
|
31
|
+
"SELECT * FROM projects WHERE id = ?", (project_id,)
|
|
32
|
+
).fetchone()
|
|
33
|
+
if not row:
|
|
34
|
+
raise ValueError(f"Project '{project_id}' not found.")
|
|
35
|
+
return dict(row)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# -----------------------------------------------------------------
|
|
39
|
+
# Configuration helpers
|
|
40
|
+
# -----------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
def _load_cui_config():
|
|
43
|
+
"""Load CUI marking configuration."""
|
|
44
|
+
try:
|
|
45
|
+
sys.path.insert(0, str(BASE_DIR / "tools" / "compliance"))
|
|
46
|
+
from cui_marker import load_cui_config
|
|
47
|
+
return load_cui_config()
|
|
48
|
+
except ImportError:
|
|
49
|
+
return {
|
|
50
|
+
"document_header": (
|
|
51
|
+
"////////////////////////////////////////////////////////////////////\n"
|
|
52
|
+
"CONTROLLED UNCLASSIFIED INFORMATION (CUI) // SP-CTI\n"
|
|
53
|
+
"Distribution: Distribution D -- Authorized DoD Personnel Only\n"
|
|
54
|
+
"////////////////////////////////////////////////////////////////////"
|
|
55
|
+
),
|
|
56
|
+
"document_footer": (
|
|
57
|
+
"////////////////////////////////////////////////////////////////////\n"
|
|
58
|
+
"CUI // SP-CTI | Department of Defense\n"
|
|
59
|
+
"////////////////////////////////////////////////////////////////////"
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _load_sbd_requirements():
|
|
65
|
+
"""Load SbD requirements from the JSON catalog."""
|
|
66
|
+
if not SBD_REQUIREMENTS_PATH.exists():
|
|
67
|
+
raise FileNotFoundError(
|
|
68
|
+
f"SbD requirements file not found: {SBD_REQUIREMENTS_PATH}\n"
|
|
69
|
+
"Expected: context/compliance/cisa_sbd_requirements.json"
|
|
70
|
+
)
|
|
71
|
+
with open(SBD_REQUIREMENTS_PATH, "r", encoding="utf-8") as f:
|
|
72
|
+
return json.load(f)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _log_audit_event(conn, project_id, action, details, file_path=None):
|
|
76
|
+
"""Log an audit trail event (append-only, NIST AU compliant)."""
|
|
77
|
+
try:
|
|
78
|
+
conn.execute(
|
|
79
|
+
"""INSERT INTO audit_trail
|
|
80
|
+
(project_id, event_type, actor, action, details,
|
|
81
|
+
affected_files, classification)
|
|
82
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
|
83
|
+
(
|
|
84
|
+
project_id,
|
|
85
|
+
"sbd_assessed",
|
|
86
|
+
"icdev-compliance-engine",
|
|
87
|
+
action,
|
|
88
|
+
json.dumps(details),
|
|
89
|
+
json.dumps([str(file_path)] if file_path else []),
|
|
90
|
+
"CUI",
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
conn.commit()
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print(f"Warning: Could not log audit event: {e}", file=sys.stderr)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Directories to skip during file scanning (child app copies, build
|
|
99
|
+
# artifacts, VCS, virtual envs). Pruning these avoids walking thousands
|
|
100
|
+
# of duplicated files under .tmp/children* which caused timeouts.
|
|
101
|
+
_SKIP_DIRS = {
|
|
102
|
+
".tmp", ".git", "node_modules", "__pycache__", ".aws-sam",
|
|
103
|
+
".venv", "venv", ".tox", ".mypy_cache", ".pytest_cache",
|
|
104
|
+
"dist", "build", ".eggs", "htmlcov",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# -----------------------------------------------------------------
|
|
109
|
+
# Auto-check helper: walk project files matching extensions
|
|
110
|
+
# -----------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
def _scan_files(project_dir, extensions, patterns, threshold=1):
|
|
113
|
+
"""Scan project files for regex patterns.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
project_dir: Root directory to walk.
|
|
117
|
+
extensions: Tuple of file extensions to include (e.g. ('.py', '.md')).
|
|
118
|
+
patterns: List of regex patterns to search for.
|
|
119
|
+
threshold: Minimum number of files with matches to consider satisfied.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Tuple of (matched_files, total_scanned).
|
|
123
|
+
"""
|
|
124
|
+
matched_files = []
|
|
125
|
+
total_scanned = 0
|
|
126
|
+
for root, dirs, files in os.walk(project_dir):
|
|
127
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
128
|
+
for fname in files:
|
|
129
|
+
if not fname.endswith(extensions):
|
|
130
|
+
continue
|
|
131
|
+
fpath = os.path.join(root, fname)
|
|
132
|
+
total_scanned += 1
|
|
133
|
+
try:
|
|
134
|
+
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
|
135
|
+
content = f.read()
|
|
136
|
+
for pattern in patterns:
|
|
137
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
138
|
+
matched_files.append(fpath)
|
|
139
|
+
break
|
|
140
|
+
except Exception:
|
|
141
|
+
continue
|
|
142
|
+
return matched_files, total_scanned
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _dir_or_file_exists(project_dir, dir_names=None, glob_patterns=None):
|
|
146
|
+
"""Check if specific directories or file globs exist under project_dir.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
project_dir: Root directory to search.
|
|
150
|
+
dir_names: List of directory names to look for.
|
|
151
|
+
glob_patterns: List of glob patterns to match files.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
List of found paths.
|
|
155
|
+
"""
|
|
156
|
+
found = []
|
|
157
|
+
project_path = Path(project_dir)
|
|
158
|
+
|
|
159
|
+
if dir_names:
|
|
160
|
+
for dname in dir_names:
|
|
161
|
+
candidate = project_path / dname
|
|
162
|
+
if candidate.is_dir():
|
|
163
|
+
found.append(str(candidate))
|
|
164
|
+
# Also check one level deeper (e.g. infra/terraform/)
|
|
165
|
+
for child in project_path.rglob(dname):
|
|
166
|
+
if child.is_dir() and str(child) not in found:
|
|
167
|
+
found.append(str(child))
|
|
168
|
+
|
|
169
|
+
if glob_patterns:
|
|
170
|
+
for gp in glob_patterns:
|
|
171
|
+
for match in project_path.rglob(gp):
|
|
172
|
+
if str(match) not in found:
|
|
173
|
+
found.append(str(match))
|
|
174
|
+
|
|
175
|
+
return found
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# -----------------------------------------------------------------
|
|
179
|
+
# Auto-check functions (20 checks)
|
|
180
|
+
# Each returns a dict:
|
|
181
|
+
# {"status": "satisfied"|"not_satisfied"|"partially_satisfied"
|
|
182
|
+
# |"not_applicable",
|
|
183
|
+
# "evidence": "description of what was found",
|
|
184
|
+
# "details": "specifics"}
|
|
185
|
+
# -----------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
def _check_mfa_patterns(project_dir):
|
|
188
|
+
"""SBD-01: Scan auth code for MFA/2FA/TOTP/FIDO multi-factor patterns."""
|
|
189
|
+
auth_patterns = [
|
|
190
|
+
r"\bMFA\b|multi.factor|MultiFactor",
|
|
191
|
+
r"\b2FA\b|two.factor|TwoFactor",
|
|
192
|
+
r"\bTOTP\b|totp|time.based.one.time",
|
|
193
|
+
r"\bFIDO\b|fido2|WebAuthn|webauthn",
|
|
194
|
+
r"authenticator|Authenticator",
|
|
195
|
+
r"otp_secret|otp_verify|verify_otp",
|
|
196
|
+
]
|
|
197
|
+
extensions = (".py", ".js", ".ts", ".java", ".yaml", ".yml")
|
|
198
|
+
matched, total = _scan_files(project_dir, extensions, auth_patterns)
|
|
199
|
+
|
|
200
|
+
if total == 0:
|
|
201
|
+
return {
|
|
202
|
+
"status": "not_satisfied",
|
|
203
|
+
"evidence": "No source or config files found to assess.",
|
|
204
|
+
"details": "Project directory lacks applicable files.",
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if matched:
|
|
208
|
+
return {
|
|
209
|
+
"status": "satisfied",
|
|
210
|
+
"evidence": (
|
|
211
|
+
f"MFA/multi-factor authentication patterns found in "
|
|
212
|
+
f"{len(matched)} file(s)."
|
|
213
|
+
),
|
|
214
|
+
"details": "; ".join(os.path.basename(f) for f in matched[:5]),
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# Check if any auth-related code exists at all
|
|
218
|
+
auth_code_patterns = [
|
|
219
|
+
r"login|authenticate|auth|password|credential",
|
|
220
|
+
]
|
|
221
|
+
auth_files, _ = _scan_files(project_dir, extensions, auth_code_patterns)
|
|
222
|
+
if not auth_files:
|
|
223
|
+
return {
|
|
224
|
+
"status": "not_satisfied",
|
|
225
|
+
"evidence": "No authentication code detected in project.",
|
|
226
|
+
"details": "No auth-related patterns found; cannot verify MFA support.",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
"status": "not_satisfied",
|
|
231
|
+
"evidence": (
|
|
232
|
+
f"Authentication code found in {len(auth_files)} file(s) but no "
|
|
233
|
+
"MFA/2FA/TOTP/FIDO patterns detected."
|
|
234
|
+
),
|
|
235
|
+
"details": "Multi-factor authentication is required but not implemented.",
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _check_default_passwords(project_dir):
|
|
240
|
+
"""SBD-02: Scan for hardcoded/default passwords (inverse check).
|
|
241
|
+
|
|
242
|
+
Returns satisfied if NO default passwords are found.
|
|
243
|
+
"""
|
|
244
|
+
bad_patterns = [
|
|
245
|
+
r"password\s*=\s*[\"'][^\"']+[\"']",
|
|
246
|
+
r"default_password",
|
|
247
|
+
r"admin.*password|password.*admin",
|
|
248
|
+
r"password123|passw0rd|qwerty|letmein",
|
|
249
|
+
r"changeme|change_me|CHANGEME",
|
|
250
|
+
]
|
|
251
|
+
extensions = (".py", ".js", ".ts", ".yaml", ".yml", ".json", ".conf", ".env")
|
|
252
|
+
matched, total = _scan_files(project_dir, extensions, bad_patterns)
|
|
253
|
+
|
|
254
|
+
if total == 0:
|
|
255
|
+
return {
|
|
256
|
+
"status": "not_satisfied",
|
|
257
|
+
"evidence": "No source or config files found to assess.",
|
|
258
|
+
"details": "Project directory lacks applicable files.",
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if matched:
|
|
262
|
+
return {
|
|
263
|
+
"status": "not_satisfied",
|
|
264
|
+
"evidence": (
|
|
265
|
+
f"Default/hardcoded password patterns detected in "
|
|
266
|
+
f"{len(matched)} file(s)."
|
|
267
|
+
),
|
|
268
|
+
"details": (
|
|
269
|
+
"Files with potential default passwords: "
|
|
270
|
+
+ "; ".join(os.path.basename(f) for f in matched[:5])
|
|
271
|
+
),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
"status": "satisfied",
|
|
276
|
+
"evidence": (
|
|
277
|
+
f"Scanned {total} file(s) -- no default or hardcoded password "
|
|
278
|
+
"patterns detected."
|
|
279
|
+
),
|
|
280
|
+
"details": "No instances of hardcoded passwords, changeme, or default credentials.",
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _check_memory_safe_language(project_dir):
|
|
285
|
+
"""SBD-03: Assess ratio of memory-safe to memory-unsafe language files."""
|
|
286
|
+
safe_extensions = (".py", ".java", ".go", ".rs", ".cs", ".rb", ".kt")
|
|
287
|
+
unsafe_extensions = (".c", ".cpp", ".cc", ".cxx", ".h", ".hpp")
|
|
288
|
+
|
|
289
|
+
safe_count = 0
|
|
290
|
+
unsafe_count = 0
|
|
291
|
+
|
|
292
|
+
for root, dirs, files in os.walk(project_dir):
|
|
293
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
294
|
+
for fname in files:
|
|
295
|
+
lower = fname.lower()
|
|
296
|
+
if any(lower.endswith(ext) for ext in safe_extensions):
|
|
297
|
+
safe_count += 1
|
|
298
|
+
elif any(lower.endswith(ext) for ext in unsafe_extensions):
|
|
299
|
+
unsafe_count += 1
|
|
300
|
+
|
|
301
|
+
total = safe_count + unsafe_count
|
|
302
|
+
if total == 0:
|
|
303
|
+
return {
|
|
304
|
+
"status": "not_satisfied",
|
|
305
|
+
"evidence": "No recognized programming language files found.",
|
|
306
|
+
"details": "Cannot determine memory safety profile.",
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
safe_ratio = safe_count / total
|
|
310
|
+
if safe_ratio > 0.9:
|
|
311
|
+
return {
|
|
312
|
+
"status": "satisfied",
|
|
313
|
+
"evidence": (
|
|
314
|
+
f"Memory-safe languages: {safe_count}/{total} files "
|
|
315
|
+
f"({safe_ratio:.0%})."
|
|
316
|
+
),
|
|
317
|
+
"details": (
|
|
318
|
+
f"Safe: {safe_count} | Unsafe: {unsafe_count}. "
|
|
319
|
+
"Exceeds 90% threshold."
|
|
320
|
+
),
|
|
321
|
+
}
|
|
322
|
+
elif safe_ratio > 0.5:
|
|
323
|
+
return {
|
|
324
|
+
"status": "partially_satisfied",
|
|
325
|
+
"evidence": (
|
|
326
|
+
f"Memory-safe languages: {safe_count}/{total} files "
|
|
327
|
+
f"({safe_ratio:.0%})."
|
|
328
|
+
),
|
|
329
|
+
"details": (
|
|
330
|
+
f"Safe: {safe_count} | Unsafe: {unsafe_count}. "
|
|
331
|
+
"Between 50-90%. Must exceed 90% for full satisfaction."
|
|
332
|
+
),
|
|
333
|
+
}
|
|
334
|
+
else:
|
|
335
|
+
return {
|
|
336
|
+
"status": "not_satisfied",
|
|
337
|
+
"evidence": (
|
|
338
|
+
f"Memory-safe languages: {safe_count}/{total} files "
|
|
339
|
+
f"({safe_ratio:.0%})."
|
|
340
|
+
),
|
|
341
|
+
"details": (
|
|
342
|
+
f"Safe: {safe_count} | Unsafe: {unsafe_count}. "
|
|
343
|
+
"At or below 50%. Significant memory safety risk."
|
|
344
|
+
),
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _check_memory_safety_tooling(project_dir):
|
|
349
|
+
"""SBD-04: Scan build configs for memory-safety analysis tooling."""
|
|
350
|
+
# First check if there is any memory-unsafe code
|
|
351
|
+
unsafe_extensions = (".c", ".cpp", ".cc", ".cxx", ".h", ".hpp")
|
|
352
|
+
has_unsafe = False
|
|
353
|
+
for root, dirs, files in os.walk(project_dir):
|
|
354
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
355
|
+
for fname in files:
|
|
356
|
+
if any(fname.lower().endswith(ext) for ext in unsafe_extensions):
|
|
357
|
+
has_unsafe = True
|
|
358
|
+
break
|
|
359
|
+
if has_unsafe:
|
|
360
|
+
break
|
|
361
|
+
|
|
362
|
+
if not has_unsafe:
|
|
363
|
+
return {
|
|
364
|
+
"status": "not_applicable",
|
|
365
|
+
"evidence": "No memory-unsafe language files (C/C++) found in project.",
|
|
366
|
+
"details": "Memory safety tooling check is not applicable.",
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
tooling_patterns = [
|
|
370
|
+
r"AddressSanitizer|ASAN|asan",
|
|
371
|
+
r"-fsanitize\s*=",
|
|
372
|
+
r"Valgrind|valgrind",
|
|
373
|
+
r"\bMSAN\b|MemorySanitizer",
|
|
374
|
+
r"\bTSAN\b|ThreadSanitizer",
|
|
375
|
+
r"\bUBSAN\b|UndefinedBehaviorSanitizer",
|
|
376
|
+
r"clang.tidy|cppcheck|coverity",
|
|
377
|
+
]
|
|
378
|
+
# Build config extensions plus common build system files
|
|
379
|
+
extensions = (".cfg", ".ini", ".yaml", ".yml", ".toml")
|
|
380
|
+
matched, total = _scan_files(project_dir, extensions, tooling_patterns)
|
|
381
|
+
|
|
382
|
+
# Also check Makefile and CMakeLists.txt specifically
|
|
383
|
+
build_files = _dir_or_file_exists(
|
|
384
|
+
project_dir,
|
|
385
|
+
glob_patterns=["Makefile", "makefile", "CMakeLists.txt", "*.cmake"],
|
|
386
|
+
)
|
|
387
|
+
build_matches = []
|
|
388
|
+
for bf_path in build_files:
|
|
389
|
+
try:
|
|
390
|
+
with open(bf_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
391
|
+
content = f.read()
|
|
392
|
+
for pattern in tooling_patterns:
|
|
393
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
394
|
+
build_matches.append(bf_path)
|
|
395
|
+
break
|
|
396
|
+
except Exception:
|
|
397
|
+
continue
|
|
398
|
+
|
|
399
|
+
all_matched = list(set(matched + build_matches))
|
|
400
|
+
if all_matched:
|
|
401
|
+
return {
|
|
402
|
+
"status": "satisfied",
|
|
403
|
+
"evidence": (
|
|
404
|
+
f"Memory safety tooling patterns found in "
|
|
405
|
+
f"{len(all_matched)} file(s)."
|
|
406
|
+
),
|
|
407
|
+
"details": "; ".join(os.path.basename(f) for f in all_matched[:5]),
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
"status": "not_satisfied",
|
|
412
|
+
"evidence": (
|
|
413
|
+
"Memory-unsafe code present but no memory safety tooling detected."
|
|
414
|
+
),
|
|
415
|
+
"details": (
|
|
416
|
+
"Expected: AddressSanitizer, -fsanitize, Valgrind, MSAN, TSAN, "
|
|
417
|
+
"or equivalent tooling in build configurations."
|
|
418
|
+
),
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def _check_patch_cadence(project_dir):
|
|
423
|
+
"""SBD-05: Look for automated dependency update tooling."""
|
|
424
|
+
found = _dir_or_file_exists(
|
|
425
|
+
project_dir,
|
|
426
|
+
glob_patterns=[
|
|
427
|
+
"dependabot.yml",
|
|
428
|
+
"dependabot.yaml",
|
|
429
|
+
"renovate.json",
|
|
430
|
+
".renovaterc",
|
|
431
|
+
".renovaterc.json",
|
|
432
|
+
],
|
|
433
|
+
)
|
|
434
|
+
# Also check .github/dependabot.yml path
|
|
435
|
+
github_dependabot = _dir_or_file_exists(
|
|
436
|
+
project_dir,
|
|
437
|
+
dir_names=[".github"],
|
|
438
|
+
)
|
|
439
|
+
if github_dependabot:
|
|
440
|
+
dep_files = _dir_or_file_exists(
|
|
441
|
+
project_dir,
|
|
442
|
+
glob_patterns=[".github/dependabot.yml", ".github/dependabot.yaml"],
|
|
443
|
+
)
|
|
444
|
+
found.extend(dep_files)
|
|
445
|
+
|
|
446
|
+
# Check for pip-compile, poetry.lock freshness indicators
|
|
447
|
+
lock_files = _dir_or_file_exists(
|
|
448
|
+
project_dir,
|
|
449
|
+
glob_patterns=[
|
|
450
|
+
"requirements*.txt",
|
|
451
|
+
"poetry.lock",
|
|
452
|
+
"Pipfile.lock",
|
|
453
|
+
"package-lock.json",
|
|
454
|
+
"yarn.lock",
|
|
455
|
+
"pnpm-lock.yaml",
|
|
456
|
+
],
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
all_found = list(set(found))
|
|
460
|
+
if all_found:
|
|
461
|
+
return {
|
|
462
|
+
"status": "satisfied",
|
|
463
|
+
"evidence": (
|
|
464
|
+
f"Automated dependency update tooling found: "
|
|
465
|
+
f"{len(all_found)} artifact(s)."
|
|
466
|
+
),
|
|
467
|
+
"details": "; ".join(os.path.basename(f) for f in all_found[:5]),
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if lock_files:
|
|
471
|
+
return {
|
|
472
|
+
"status": "partially_satisfied",
|
|
473
|
+
"evidence": (
|
|
474
|
+
f"Lock files found ({len(lock_files)}) indicating dependency "
|
|
475
|
+
"management, but no automated update tooling (Dependabot, "
|
|
476
|
+
"Renovate) detected."
|
|
477
|
+
),
|
|
478
|
+
"details": "; ".join(os.path.basename(f) for f in lock_files[:5]),
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
"status": "not_satisfied",
|
|
483
|
+
"evidence": "No automated dependency update tooling detected.",
|
|
484
|
+
"details": (
|
|
485
|
+
"Expected: .github/dependabot.yml, renovate.json, .renovaterc, "
|
|
486
|
+
"or equivalent automated patch management configuration."
|
|
487
|
+
),
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _check_vuln_disclosure(project_dir):
|
|
492
|
+
"""SBD-06: Look for vulnerability disclosure policy files."""
|
|
493
|
+
found = _dir_or_file_exists(
|
|
494
|
+
project_dir,
|
|
495
|
+
glob_patterns=[
|
|
496
|
+
"SECURITY.md",
|
|
497
|
+
"SECURITY.txt",
|
|
498
|
+
"security.txt",
|
|
499
|
+
"security.md",
|
|
500
|
+
],
|
|
501
|
+
)
|
|
502
|
+
# Also check .well-known/security.txt
|
|
503
|
+
well_known = _dir_or_file_exists(
|
|
504
|
+
project_dir,
|
|
505
|
+
glob_patterns=[".well-known/security.txt"],
|
|
506
|
+
)
|
|
507
|
+
found.extend(well_known)
|
|
508
|
+
|
|
509
|
+
all_found = list(set(found))
|
|
510
|
+
if all_found:
|
|
511
|
+
return {
|
|
512
|
+
"status": "satisfied",
|
|
513
|
+
"evidence": (
|
|
514
|
+
f"Vulnerability disclosure policy found: "
|
|
515
|
+
f"{len(all_found)} file(s)."
|
|
516
|
+
),
|
|
517
|
+
"details": "; ".join(os.path.basename(f) for f in all_found[:5]),
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
"status": "not_satisfied",
|
|
522
|
+
"evidence": "No vulnerability disclosure policy detected.",
|
|
523
|
+
"details": (
|
|
524
|
+
"Expected: SECURITY.md, .well-known/security.txt, SECURITY.txt, "
|
|
525
|
+
"or equivalent disclosure policy file."
|
|
526
|
+
),
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def _check_audit_logging_complete(project_dir):
|
|
531
|
+
"""SBD-08: Scan for comprehensive audit logging patterns.
|
|
532
|
+
|
|
533
|
+
Checks for multiple distinct log event types.
|
|
534
|
+
"""
|
|
535
|
+
event_type_patterns = [
|
|
536
|
+
(r"login|auth.*log|authentication.*log", "authentication_logging"),
|
|
537
|
+
(r"access.*log|access_log|request.*log", "access_logging"),
|
|
538
|
+
(r"change.*log|change_log|modification.*log|update.*log", "change_logging"),
|
|
539
|
+
(r"error.*log|error_log|exception.*log", "error_logging"),
|
|
540
|
+
(r"security.*event|security.*log|security_event", "security_logging"),
|
|
541
|
+
]
|
|
542
|
+
extensions = (".py", ".js", ".ts", ".java", ".yaml", ".yml")
|
|
543
|
+
found_types = set()
|
|
544
|
+
evidence_files = []
|
|
545
|
+
|
|
546
|
+
for root, dirs, files in os.walk(project_dir):
|
|
547
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
548
|
+
for fname in files:
|
|
549
|
+
if not fname.endswith(extensions):
|
|
550
|
+
continue
|
|
551
|
+
fpath = os.path.join(root, fname)
|
|
552
|
+
try:
|
|
553
|
+
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
|
554
|
+
content = f.read()
|
|
555
|
+
for pattern, event_type in event_type_patterns:
|
|
556
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
557
|
+
found_types.add(event_type)
|
|
558
|
+
if fpath not in evidence_files:
|
|
559
|
+
evidence_files.append(fpath)
|
|
560
|
+
except Exception:
|
|
561
|
+
continue
|
|
562
|
+
|
|
563
|
+
# Also check for generic structured logging patterns
|
|
564
|
+
struct_patterns = [
|
|
565
|
+
r"audit_trail|AuditTrail",
|
|
566
|
+
r"logging\.getLogger|getLogger",
|
|
567
|
+
r"structlog|structured.log",
|
|
568
|
+
]
|
|
569
|
+
struct_matched, _ = _scan_files(project_dir, extensions, struct_patterns)
|
|
570
|
+
if struct_matched:
|
|
571
|
+
found_types.add("structured_logging")
|
|
572
|
+
for sf in struct_matched:
|
|
573
|
+
if sf not in evidence_files:
|
|
574
|
+
evidence_files.append(sf)
|
|
575
|
+
|
|
576
|
+
count = len(found_types)
|
|
577
|
+
if count >= 3:
|
|
578
|
+
return {
|
|
579
|
+
"status": "satisfied",
|
|
580
|
+
"evidence": (
|
|
581
|
+
f"Comprehensive audit logging detected: {count} distinct "
|
|
582
|
+
f"log event types across {len(evidence_files)} file(s)."
|
|
583
|
+
),
|
|
584
|
+
"details": (
|
|
585
|
+
f"Event types: {', '.join(sorted(found_types))}. "
|
|
586
|
+
f"Files: {'; '.join(os.path.basename(f) for f in evidence_files[:5])}"
|
|
587
|
+
),
|
|
588
|
+
}
|
|
589
|
+
elif count >= 1:
|
|
590
|
+
return {
|
|
591
|
+
"status": "partially_satisfied",
|
|
592
|
+
"evidence": (
|
|
593
|
+
f"Partial audit logging detected: {count} distinct log event "
|
|
594
|
+
f"type(s) found."
|
|
595
|
+
),
|
|
596
|
+
"details": (
|
|
597
|
+
f"Event types: {', '.join(sorted(found_types))}. "
|
|
598
|
+
"Need 3+ distinct log event types for full compliance."
|
|
599
|
+
),
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
"status": "not_satisfied",
|
|
604
|
+
"evidence": "No audit logging patterns detected in source files.",
|
|
605
|
+
"details": (
|
|
606
|
+
"Expected: authentication logging, access logging, change logging, "
|
|
607
|
+
"error logging, or security event logging patterns."
|
|
608
|
+
),
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def _check_tls_config(project_dir):
|
|
613
|
+
"""SBD-11: Scan for TLS 1.2+/1.3 and detect insecure protocols."""
|
|
614
|
+
secure_patterns = [
|
|
615
|
+
r"TLS\s*1\.[23]|TLSv1_[23]|tls_version.*1\.[23]",
|
|
616
|
+
r"TLS_1_2|TLS_1_3|PROTOCOL_TLS",
|
|
617
|
+
r"\bHTTPS\b|https://",
|
|
618
|
+
r"ssl_context|SSLContext",
|
|
619
|
+
r"mTLS|mutual.TLS",
|
|
620
|
+
r"strong.cipher|ECDHE|AES.GCM|CHACHA20",
|
|
621
|
+
]
|
|
622
|
+
insecure_patterns = [
|
|
623
|
+
r"SSLv2|SSLv3|PROTOCOL_SSLv",
|
|
624
|
+
r"TLSv1_0|TLS\s*1\.0|TLS_1_0",
|
|
625
|
+
r"TLSv1_1|TLS\s*1\.1|TLS_1_1",
|
|
626
|
+
r"verify\s*=\s*False|CERT_NONE|check_hostname\s*=\s*False",
|
|
627
|
+
r"ssl_verify.*false|tls_verify.*false",
|
|
628
|
+
]
|
|
629
|
+
extensions = (".py", ".yaml", ".yml", ".conf", ".tf", ".json")
|
|
630
|
+
|
|
631
|
+
secure_matched, total = _scan_files(project_dir, extensions, secure_patterns)
|
|
632
|
+
insecure_matched, _ = _scan_files(project_dir, extensions, insecure_patterns)
|
|
633
|
+
|
|
634
|
+
if total == 0:
|
|
635
|
+
return {
|
|
636
|
+
"status": "not_satisfied",
|
|
637
|
+
"evidence": "No configuration or source files found to assess.",
|
|
638
|
+
"details": "Project directory lacks files with expected extensions.",
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if secure_matched and not insecure_matched:
|
|
642
|
+
return {
|
|
643
|
+
"status": "satisfied",
|
|
644
|
+
"evidence": (
|
|
645
|
+
f"TLS configuration patterns found in {len(secure_matched)} "
|
|
646
|
+
f"file(s) with no insecure protocol patterns detected."
|
|
647
|
+
),
|
|
648
|
+
"details": "; ".join(
|
|
649
|
+
os.path.basename(f) for f in secure_matched[:5]
|
|
650
|
+
),
|
|
651
|
+
}
|
|
652
|
+
elif secure_matched and insecure_matched:
|
|
653
|
+
return {
|
|
654
|
+
"status": "partially_satisfied",
|
|
655
|
+
"evidence": (
|
|
656
|
+
f"TLS patterns found in {len(secure_matched)} file(s), but "
|
|
657
|
+
f"insecure patterns also detected in {len(insecure_matched)} "
|
|
658
|
+
f"file(s)."
|
|
659
|
+
),
|
|
660
|
+
"details": (
|
|
661
|
+
"Insecure files: "
|
|
662
|
+
+ "; ".join(
|
|
663
|
+
os.path.basename(f) for f in insecure_matched[:5]
|
|
664
|
+
)
|
|
665
|
+
+ ". Remove SSLv3, TLSv1.0, TLSv1.1, and verify=False usage."
|
|
666
|
+
),
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
"status": "not_satisfied",
|
|
671
|
+
"evidence": "No TLS configuration patterns detected.",
|
|
672
|
+
"details": (
|
|
673
|
+
"Expected: TLS 1.2+, strong ciphers, SSLContext configuration. "
|
|
674
|
+
"No HTTPS or TLS patterns found in project files."
|
|
675
|
+
),
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
def _check_encryption_at_rest(project_dir):
|
|
680
|
+
"""SBD-12: Scan for encryption-at-rest configuration patterns."""
|
|
681
|
+
patterns = [
|
|
682
|
+
r"\bFIPS\b|fips_mode|FIPS.140",
|
|
683
|
+
r"AES.256|AES_256|aes256",
|
|
684
|
+
r"encryption.at.rest|encrypt_at_rest|encrypted_at_rest",
|
|
685
|
+
r"storage_encrypted|StorageEncrypted|encrypted\s*=\s*true",
|
|
686
|
+
r"\bKMS\b|kms_key|aws_kms|key_management",
|
|
687
|
+
r"server.side.encryption|SSE.S3|SSE.KMS|SSEAlgorithm",
|
|
688
|
+
]
|
|
689
|
+
extensions = (".py", ".yaml", ".yml", ".tf", ".json", ".conf")
|
|
690
|
+
matched, total = _scan_files(project_dir, extensions, patterns)
|
|
691
|
+
|
|
692
|
+
if total == 0:
|
|
693
|
+
return {
|
|
694
|
+
"status": "not_satisfied",
|
|
695
|
+
"evidence": "No configuration files found to assess.",
|
|
696
|
+
"details": "Project directory lacks applicable config files.",
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if matched:
|
|
700
|
+
return {
|
|
701
|
+
"status": "satisfied",
|
|
702
|
+
"evidence": (
|
|
703
|
+
f"Encryption-at-rest patterns found in "
|
|
704
|
+
f"{len(matched)} file(s)."
|
|
705
|
+
),
|
|
706
|
+
"details": "; ".join(os.path.basename(f) for f in matched[:5]),
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
return {
|
|
710
|
+
"status": "not_satisfied",
|
|
711
|
+
"evidence": "No encryption-at-rest patterns detected.",
|
|
712
|
+
"details": (
|
|
713
|
+
"Expected: FIPS, AES-256, KMS, storage_encrypted, or "
|
|
714
|
+
"server-side encryption configuration patterns."
|
|
715
|
+
),
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
def _check_rbac_least_priv(project_dir):
|
|
720
|
+
"""SBD-14: Scan for RBAC / role / permission / least-privilege patterns."""
|
|
721
|
+
patterns = [
|
|
722
|
+
r"@login_required|@permission_required|@requires_auth",
|
|
723
|
+
r"@Secured|@PreAuthorize|@RolesAllowed",
|
|
724
|
+
r"role_required|check_permission|has_permission",
|
|
725
|
+
r"\bRBAC\b|role.based.access",
|
|
726
|
+
r"RoleBinding|ClusterRole|ClusterRoleBinding",
|
|
727
|
+
r"least.privilege|minimum.privilege|principle.of.least",
|
|
728
|
+
r"from\s+flask_login|from\s+django\.contrib\.auth",
|
|
729
|
+
]
|
|
730
|
+
extensions = (".py", ".yaml", ".yml", ".js", ".ts", ".java")
|
|
731
|
+
matched, total = _scan_files(project_dir, extensions, patterns)
|
|
732
|
+
|
|
733
|
+
if total == 0:
|
|
734
|
+
return {
|
|
735
|
+
"status": "not_satisfied",
|
|
736
|
+
"evidence": "No source files found to assess for RBAC patterns.",
|
|
737
|
+
"details": "Project directory lacks applicable source files.",
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if matched:
|
|
741
|
+
return {
|
|
742
|
+
"status": "satisfied",
|
|
743
|
+
"evidence": (
|
|
744
|
+
f"RBAC / least-privilege patterns found in "
|
|
745
|
+
f"{len(matched)} file(s)."
|
|
746
|
+
),
|
|
747
|
+
"details": "; ".join(os.path.basename(f) for f in matched[:5]),
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return {
|
|
751
|
+
"status": "not_satisfied",
|
|
752
|
+
"evidence": "No RBAC or least-privilege access control patterns detected.",
|
|
753
|
+
"details": (
|
|
754
|
+
"Expected: @login_required, role_required, RBAC, RoleBinding, "
|
|
755
|
+
"or least-privilege patterns."
|
|
756
|
+
),
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def _check_input_validation(project_dir):
|
|
761
|
+
"""SBD-16: Scan for input validation libraries and patterns."""
|
|
762
|
+
patterns = [
|
|
763
|
+
r"\bpydantic\b|from\s+pydantic|import\s+pydantic",
|
|
764
|
+
r"\bmarshmallow\b|from\s+marshmallow|import\s+marshmallow",
|
|
765
|
+
r"\bcerberus\b|from\s+cerberus|import\s+cerberus",
|
|
766
|
+
r"\bvoluptuous\b|from\s+voluptuous|import\s+voluptuous",
|
|
767
|
+
r"\bJoi\b|require\(['\"]joi|from\s+['\"]joi",
|
|
768
|
+
r"\bZod\b|from\s+['\"]zod|import.*\bzod\b",
|
|
769
|
+
r"@Valid|@NotNull|@NotBlank|@NotEmpty|@Size",
|
|
770
|
+
r"validate_input|sanitize_input|input_validation",
|
|
771
|
+
r"sanitize|validator\.validate|form\.validate",
|
|
772
|
+
]
|
|
773
|
+
extensions = (".py", ".js", ".ts", ".java")
|
|
774
|
+
matched, total = _scan_files(project_dir, extensions, patterns)
|
|
775
|
+
|
|
776
|
+
if total == 0:
|
|
777
|
+
return {
|
|
778
|
+
"status": "not_satisfied",
|
|
779
|
+
"evidence": "No source files found to assess for input validation.",
|
|
780
|
+
"details": "Project directory lacks applicable source files.",
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if len(matched) >= 2:
|
|
784
|
+
return {
|
|
785
|
+
"status": "satisfied",
|
|
786
|
+
"evidence": (
|
|
787
|
+
f"Input validation patterns found in "
|
|
788
|
+
f"{len(matched)} file(s)."
|
|
789
|
+
),
|
|
790
|
+
"details": "; ".join(os.path.basename(f) for f in matched[:5]),
|
|
791
|
+
}
|
|
792
|
+
elif len(matched) == 1:
|
|
793
|
+
return {
|
|
794
|
+
"status": "partially_satisfied",
|
|
795
|
+
"evidence": (
|
|
796
|
+
f"Input validation patterns found in only 1 file: "
|
|
797
|
+
f"{os.path.basename(matched[0])}."
|
|
798
|
+
),
|
|
799
|
+
"details": (
|
|
800
|
+
"Validation found in a single file. Should be applied "
|
|
801
|
+
"consistently across all input handling code."
|
|
802
|
+
),
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
return {
|
|
806
|
+
"status": "not_satisfied",
|
|
807
|
+
"evidence": "No input validation patterns detected.",
|
|
808
|
+
"details": (
|
|
809
|
+
"Expected: pydantic, marshmallow, cerberus, Joi, Zod, @Valid, "
|
|
810
|
+
"validate_input, or sanitize patterns."
|
|
811
|
+
),
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
|
|
815
|
+
def _check_output_encoding(project_dir):
|
|
816
|
+
"""SBD-17: Scan for XSS prevention / output encoding patterns."""
|
|
817
|
+
patterns = [
|
|
818
|
+
r"escape\(\)|html\.escape|cgi\.escape",
|
|
819
|
+
r"\bmarkupsafe\b|from\s+markupsafe|Markup\(",
|
|
820
|
+
r"\bbleach\b|bleach\.clean|bleach\.sanitize",
|
|
821
|
+
r"DOMPurify|dompurify|sanitizeHtml|sanitize_html",
|
|
822
|
+
r"htmlspecialchars|htmlentities",
|
|
823
|
+
r"Content.Security.Policy|CSP|content_security_policy",
|
|
824
|
+
r"auto_escape|autoescape|autoescaping",
|
|
825
|
+
]
|
|
826
|
+
extensions = (".py", ".js", ".ts", ".java", ".html", ".jinja", ".jinja2")
|
|
827
|
+
matched, total = _scan_files(project_dir, extensions, patterns)
|
|
828
|
+
|
|
829
|
+
if total == 0:
|
|
830
|
+
return {
|
|
831
|
+
"status": "not_satisfied",
|
|
832
|
+
"evidence": "No source or template files found to assess.",
|
|
833
|
+
"details": "Project directory lacks applicable files.",
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if matched:
|
|
837
|
+
return {
|
|
838
|
+
"status": "satisfied",
|
|
839
|
+
"evidence": (
|
|
840
|
+
f"Output encoding / XSS prevention patterns found in "
|
|
841
|
+
f"{len(matched)} file(s)."
|
|
842
|
+
),
|
|
843
|
+
"details": "; ".join(os.path.basename(f) for f in matched[:5]),
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
return {
|
|
847
|
+
"status": "not_satisfied",
|
|
848
|
+
"evidence": "No output encoding or XSS prevention patterns detected.",
|
|
849
|
+
"details": (
|
|
850
|
+
"Expected: escape(), markupsafe, bleach, DOMPurify, "
|
|
851
|
+
"sanitizeHtml, CSP headers, or auto-escaping configuration."
|
|
852
|
+
),
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def _check_security_headers(project_dir):
|
|
857
|
+
"""SBD-18: Scan for security response headers configuration."""
|
|
858
|
+
header_patterns = [
|
|
859
|
+
(r"Content.Security.Policy|content_security_policy|CSP", "CSP"),
|
|
860
|
+
(
|
|
861
|
+
r"Strict.Transport.Security|strict_transport_security|HSTS",
|
|
862
|
+
"HSTS",
|
|
863
|
+
),
|
|
864
|
+
(r"X.Frame.Options|x_frame_options|DENY|SAMEORIGIN", "X-Frame-Options"),
|
|
865
|
+
(
|
|
866
|
+
r"X.Content.Type.Options|x_content_type_options|nosniff",
|
|
867
|
+
"X-Content-Type-Options",
|
|
868
|
+
),
|
|
869
|
+
(
|
|
870
|
+
r"Access.Control.Allow.Origin|CORS|cors_allowed|cors_origins",
|
|
871
|
+
"CORS",
|
|
872
|
+
),
|
|
873
|
+
]
|
|
874
|
+
extensions = (".py", ".js", ".ts", ".yaml", ".yml", ".conf", ".json")
|
|
875
|
+
found_headers = set()
|
|
876
|
+
evidence_files = []
|
|
877
|
+
|
|
878
|
+
for root, dirs, files in os.walk(project_dir):
|
|
879
|
+
dirs[:] = [d for d in dirs if d not in _SKIP_DIRS]
|
|
880
|
+
for fname in files:
|
|
881
|
+
if not fname.endswith(extensions):
|
|
882
|
+
continue
|
|
883
|
+
fpath = os.path.join(root, fname)
|
|
884
|
+
try:
|
|
885
|
+
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
|
886
|
+
content = f.read()
|
|
887
|
+
for pattern, header_name in header_patterns:
|
|
888
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
889
|
+
found_headers.add(header_name)
|
|
890
|
+
if fpath not in evidence_files:
|
|
891
|
+
evidence_files.append(fpath)
|
|
892
|
+
except Exception:
|
|
893
|
+
continue
|
|
894
|
+
|
|
895
|
+
count = len(found_headers)
|
|
896
|
+
if count >= 3:
|
|
897
|
+
return {
|
|
898
|
+
"status": "satisfied",
|
|
899
|
+
"evidence": (
|
|
900
|
+
f"{count} distinct security headers configured: "
|
|
901
|
+
f"{', '.join(sorted(found_headers))}."
|
|
902
|
+
),
|
|
903
|
+
"details": (
|
|
904
|
+
"Files: "
|
|
905
|
+
+ "; ".join(os.path.basename(f) for f in evidence_files[:5])
|
|
906
|
+
),
|
|
907
|
+
}
|
|
908
|
+
elif count >= 1:
|
|
909
|
+
return {
|
|
910
|
+
"status": "partially_satisfied",
|
|
911
|
+
"evidence": (
|
|
912
|
+
f"Only {count} security header(s) detected: "
|
|
913
|
+
f"{', '.join(sorted(found_headers))}."
|
|
914
|
+
),
|
|
915
|
+
"details": (
|
|
916
|
+
"Need 3+ of: CSP, HSTS, X-Frame-Options, "
|
|
917
|
+
"X-Content-Type-Options, CORS for full compliance."
|
|
918
|
+
),
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
return {
|
|
922
|
+
"status": "not_satisfied",
|
|
923
|
+
"evidence": "No security response headers detected.",
|
|
924
|
+
"details": (
|
|
925
|
+
"Expected: Content-Security-Policy, Strict-Transport-Security, "
|
|
926
|
+
"X-Frame-Options, X-Content-Type-Options, or CORS configuration."
|
|
927
|
+
),
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def _check_secure_error_handling(project_dir):
|
|
932
|
+
"""SBD-19: Check for secure error handling and no debug info leakage."""
|
|
933
|
+
secure_patterns = [
|
|
934
|
+
r"DEBUG\s*=\s*False",
|
|
935
|
+
r"@app\.errorhandler|custom.error.handler|error_handler",
|
|
936
|
+
r"error_page|custom_error_response",
|
|
937
|
+
r"app\.config\[.DEBUG.\]\s*=\s*False",
|
|
938
|
+
]
|
|
939
|
+
insecure_patterns = [
|
|
940
|
+
r"DEBUG\s*=\s*True",
|
|
941
|
+
r"traceback\.print_exc|print_exc\(\)",
|
|
942
|
+
r"print\s*\(.*traceback|print\s*\(.*stack",
|
|
943
|
+
r"stack_trace.*response|stacktrace.*response",
|
|
944
|
+
r"app\.config\[.DEBUG.\]\s*=\s*True",
|
|
945
|
+
]
|
|
946
|
+
extensions = (".py", ".js", ".ts", ".yaml", ".yml", ".conf")
|
|
947
|
+
|
|
948
|
+
secure_matched, total = _scan_files(
|
|
949
|
+
project_dir, extensions, secure_patterns
|
|
950
|
+
)
|
|
951
|
+
insecure_matched, _ = _scan_files(
|
|
952
|
+
project_dir, extensions, insecure_patterns
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
if total == 0:
|
|
956
|
+
return {
|
|
957
|
+
"status": "not_satisfied",
|
|
958
|
+
"evidence": "No source or config files found to assess.",
|
|
959
|
+
"details": "Project directory lacks applicable files.",
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if secure_matched and not insecure_matched:
|
|
963
|
+
return {
|
|
964
|
+
"status": "satisfied",
|
|
965
|
+
"evidence": (
|
|
966
|
+
f"Secure error handling patterns found in "
|
|
967
|
+
f"{len(secure_matched)} file(s) with no insecure patterns."
|
|
968
|
+
),
|
|
969
|
+
"details": "; ".join(
|
|
970
|
+
os.path.basename(f) for f in secure_matched[:5]
|
|
971
|
+
),
|
|
972
|
+
}
|
|
973
|
+
elif secure_matched and insecure_matched:
|
|
974
|
+
return {
|
|
975
|
+
"status": "partially_satisfied",
|
|
976
|
+
"evidence": (
|
|
977
|
+
f"Secure error handling in {len(secure_matched)} file(s), "
|
|
978
|
+
f"but insecure patterns in {len(insecure_matched)} file(s)."
|
|
979
|
+
),
|
|
980
|
+
"details": (
|
|
981
|
+
"Insecure files: "
|
|
982
|
+
+ "; ".join(
|
|
983
|
+
os.path.basename(f) for f in insecure_matched[:5]
|
|
984
|
+
)
|
|
985
|
+
+ ". Remove DEBUG=True and stack trace exposure in responses."
|
|
986
|
+
),
|
|
987
|
+
}
|
|
988
|
+
elif insecure_matched and not secure_matched:
|
|
989
|
+
return {
|
|
990
|
+
"status": "not_satisfied",
|
|
991
|
+
"evidence": (
|
|
992
|
+
f"Insecure error handling patterns detected in "
|
|
993
|
+
f"{len(insecure_matched)} file(s) with no secure patterns."
|
|
994
|
+
),
|
|
995
|
+
"details": (
|
|
996
|
+
"Files: "
|
|
997
|
+
+ "; ".join(
|
|
998
|
+
os.path.basename(f) for f in insecure_matched[:5]
|
|
999
|
+
)
|
|
1000
|
+
+ ". DEBUG=True, traceback exposure, or stack traces "
|
|
1001
|
+
"in responses detected."
|
|
1002
|
+
),
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return {
|
|
1006
|
+
"status": "not_satisfied",
|
|
1007
|
+
"evidence": "No error handling patterns detected (secure or insecure).",
|
|
1008
|
+
"details": (
|
|
1009
|
+
"Expected: DEBUG=False, custom error handlers, and absence of "
|
|
1010
|
+
"DEBUG=True or traceback exposure in responses."
|
|
1011
|
+
),
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
def _check_sbom_freshness(project_dir):
|
|
1016
|
+
"""SBD-21: Check for SBOM files and their freshness (within 30 days)."""
|
|
1017
|
+
found = _dir_or_file_exists(
|
|
1018
|
+
project_dir,
|
|
1019
|
+
glob_patterns=[
|
|
1020
|
+
"*sbom*.json",
|
|
1021
|
+
"*bom*.xml",
|
|
1022
|
+
"*sbom*.xml",
|
|
1023
|
+
"*cyclonedx*",
|
|
1024
|
+
"*spdx*",
|
|
1025
|
+
],
|
|
1026
|
+
)
|
|
1027
|
+
|
|
1028
|
+
if not found:
|
|
1029
|
+
return {
|
|
1030
|
+
"status": "not_satisfied",
|
|
1031
|
+
"evidence": "No SBOM artifacts detected in the project.",
|
|
1032
|
+
"details": (
|
|
1033
|
+
"Expected: *sbom*.json, *bom*.xml, *cyclonedx*, or *spdx* "
|
|
1034
|
+
"files."
|
|
1035
|
+
),
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
now = datetime.now(timezone.utc)
|
|
1039
|
+
fresh_files = []
|
|
1040
|
+
stale_files = []
|
|
1041
|
+
for fpath in found:
|
|
1042
|
+
try:
|
|
1043
|
+
mtime = datetime.utcfromtimestamp(os.path.getmtime(fpath))
|
|
1044
|
+
age_days = (now - mtime).days
|
|
1045
|
+
if age_days <= 30:
|
|
1046
|
+
fresh_files.append((fpath, age_days))
|
|
1047
|
+
else:
|
|
1048
|
+
stale_files.append((fpath, age_days))
|
|
1049
|
+
except Exception:
|
|
1050
|
+
stale_files.append((fpath, -1))
|
|
1051
|
+
|
|
1052
|
+
if fresh_files and not stale_files:
|
|
1053
|
+
return {
|
|
1054
|
+
"status": "satisfied",
|
|
1055
|
+
"evidence": (
|
|
1056
|
+
f"SBOM artifact(s) found and fresh: {len(fresh_files)} "
|
|
1057
|
+
f"file(s) modified within 30 days."
|
|
1058
|
+
),
|
|
1059
|
+
"details": "; ".join(
|
|
1060
|
+
f"{os.path.basename(f)} ({d}d old)" for f, d in fresh_files[:5]
|
|
1061
|
+
),
|
|
1062
|
+
}
|
|
1063
|
+
elif fresh_files and stale_files:
|
|
1064
|
+
return {
|
|
1065
|
+
"status": "partially_satisfied",
|
|
1066
|
+
"evidence": (
|
|
1067
|
+
f"{len(fresh_files)} fresh SBOM(s) but "
|
|
1068
|
+
f"{len(stale_files)} stale SBOM(s) detected."
|
|
1069
|
+
),
|
|
1070
|
+
"details": (
|
|
1071
|
+
"Stale: "
|
|
1072
|
+
+ "; ".join(
|
|
1073
|
+
f"{os.path.basename(f)} ({d}d old)"
|
|
1074
|
+
for f, d in stale_files[:5]
|
|
1075
|
+
)
|
|
1076
|
+
),
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return {
|
|
1080
|
+
"status": "partially_satisfied",
|
|
1081
|
+
"evidence": (
|
|
1082
|
+
f"SBOM artifact(s) found but all are stale (>30 days old): "
|
|
1083
|
+
f"{len(stale_files)} file(s)."
|
|
1084
|
+
),
|
|
1085
|
+
"details": (
|
|
1086
|
+
"Stale: "
|
|
1087
|
+
+ "; ".join(
|
|
1088
|
+
f"{os.path.basename(f)} ({d}d old)" for f, d in stale_files[:5]
|
|
1089
|
+
)
|
|
1090
|
+
+ ". Regenerate SBOM to meet freshness requirement."
|
|
1091
|
+
),
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _check_dep_vuln_scanning(project_dir):
|
|
1096
|
+
"""SBD-22: Look for dependency vulnerability scanning tooling or results."""
|
|
1097
|
+
# Check for scanning tool configs
|
|
1098
|
+
found_configs = _dir_or_file_exists(
|
|
1099
|
+
project_dir,
|
|
1100
|
+
glob_patterns=[
|
|
1101
|
+
".snyk",
|
|
1102
|
+
".safety",
|
|
1103
|
+
".safety-policy.yml",
|
|
1104
|
+
"audit-report.json",
|
|
1105
|
+
"dependency-check-report*",
|
|
1106
|
+
"pip-audit-report*",
|
|
1107
|
+
"npm-audit-report*",
|
|
1108
|
+
],
|
|
1109
|
+
)
|
|
1110
|
+
|
|
1111
|
+
# Scan CI config files for audit commands
|
|
1112
|
+
ci_patterns = [
|
|
1113
|
+
r"pip.audit|pip_audit|pipaudit",
|
|
1114
|
+
r"npm\s+audit|yarn\s+audit",
|
|
1115
|
+
r"\bsafety\b.*check|safety\s+scan",
|
|
1116
|
+
r"\bsnyk\b.*test|snyk\s+monitor",
|
|
1117
|
+
r"dependency.check|DependencyCheck",
|
|
1118
|
+
r"trivy\s+fs|grype\s+dir",
|
|
1119
|
+
]
|
|
1120
|
+
ci_extensions = (
|
|
1121
|
+
".yaml", ".yml", ".json", ".toml", ".cfg", ".ini",
|
|
1122
|
+
)
|
|
1123
|
+
ci_matched, _ = _scan_files(project_dir, ci_extensions, ci_patterns)
|
|
1124
|
+
|
|
1125
|
+
all_found = list(set(found_configs + ci_matched))
|
|
1126
|
+
if all_found:
|
|
1127
|
+
return {
|
|
1128
|
+
"status": "satisfied",
|
|
1129
|
+
"evidence": (
|
|
1130
|
+
f"Dependency vulnerability scanning tooling/results found: "
|
|
1131
|
+
f"{len(all_found)} artifact(s)."
|
|
1132
|
+
),
|
|
1133
|
+
"details": "; ".join(
|
|
1134
|
+
os.path.basename(f) for f in all_found[:5]
|
|
1135
|
+
),
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return {
|
|
1139
|
+
"status": "not_satisfied",
|
|
1140
|
+
"evidence": "No dependency vulnerability scanning tooling or results detected.",
|
|
1141
|
+
"details": (
|
|
1142
|
+
"Expected: pip-audit, npm audit, safety, Snyk configs, "
|
|
1143
|
+
".snyk, audit-report.json, or dependency-check-report files."
|
|
1144
|
+
),
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def _check_threat_model(project_dir):
|
|
1149
|
+
"""SBD-24: Look for threat model artifacts."""
|
|
1150
|
+
found = _dir_or_file_exists(
|
|
1151
|
+
project_dir,
|
|
1152
|
+
glob_patterns=[
|
|
1153
|
+
"threat-model*",
|
|
1154
|
+
"threat_model*",
|
|
1155
|
+
"STRIDE*",
|
|
1156
|
+
"PASTA*",
|
|
1157
|
+
"threat-analysis*",
|
|
1158
|
+
"threat_analysis*",
|
|
1159
|
+
"attack-tree*",
|
|
1160
|
+
"attack_tree*",
|
|
1161
|
+
],
|
|
1162
|
+
)
|
|
1163
|
+
# Also check for threat model directories
|
|
1164
|
+
found_dirs = _dir_or_file_exists(
|
|
1165
|
+
project_dir,
|
|
1166
|
+
dir_names=[
|
|
1167
|
+
"threat-model",
|
|
1168
|
+
"threat_model",
|
|
1169
|
+
"threat-modeling",
|
|
1170
|
+
"threat_modeling",
|
|
1171
|
+
],
|
|
1172
|
+
)
|
|
1173
|
+
# Check within docs/ or security/ subdirectories
|
|
1174
|
+
nested_found = _dir_or_file_exists(
|
|
1175
|
+
project_dir,
|
|
1176
|
+
glob_patterns=[
|
|
1177
|
+
"docs/threat-model*",
|
|
1178
|
+
"docs/threat_model*",
|
|
1179
|
+
"security/threat-model*",
|
|
1180
|
+
"security/threat_model*",
|
|
1181
|
+
],
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
all_found = list(set(found + found_dirs + nested_found))
|
|
1185
|
+
if all_found:
|
|
1186
|
+
return {
|
|
1187
|
+
"status": "satisfied",
|
|
1188
|
+
"evidence": (
|
|
1189
|
+
f"Threat model artifact(s) found: {len(all_found)} item(s)."
|
|
1190
|
+
),
|
|
1191
|
+
"details": "; ".join(
|
|
1192
|
+
os.path.basename(f) for f in all_found[:5]
|
|
1193
|
+
),
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
return {
|
|
1197
|
+
"status": "not_satisfied",
|
|
1198
|
+
"evidence": "No threat model artifacts detected.",
|
|
1199
|
+
"details": (
|
|
1200
|
+
"Expected: threat-model.md, threat_model.*, STRIDE.*, PASTA.*, "
|
|
1201
|
+
"threat-analysis.*, attack-tree.*, or threat-model/ directory."
|
|
1202
|
+
),
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
|
|
1206
|
+
def _check_no_default_creds(project_dir):
|
|
1207
|
+
"""SBD-28: Scan config files for default credential patterns (inverse check).
|
|
1208
|
+
|
|
1209
|
+
Returns satisfied if NO default credentials are found.
|
|
1210
|
+
"""
|
|
1211
|
+
bad_patterns = [
|
|
1212
|
+
r"admin[:/]admin|admin/admin",
|
|
1213
|
+
r"root[:/]root|root/root",
|
|
1214
|
+
r"password123|passw0rd",
|
|
1215
|
+
r"changeme|change_me|CHANGEME",
|
|
1216
|
+
r"default[_\s]*password|default[_\s]*credential",
|
|
1217
|
+
r"test[:/]test|test/test",
|
|
1218
|
+
r"username.*=.*admin.*\n.*password.*=.*admin",
|
|
1219
|
+
]
|
|
1220
|
+
extensions = (
|
|
1221
|
+
".yaml", ".yml", ".json", ".conf", ".env",
|
|
1222
|
+
".ini", ".cfg", ".properties",
|
|
1223
|
+
)
|
|
1224
|
+
matched, total = _scan_files(project_dir, extensions, bad_patterns)
|
|
1225
|
+
|
|
1226
|
+
if total == 0:
|
|
1227
|
+
return {
|
|
1228
|
+
"status": "not_satisfied",
|
|
1229
|
+
"evidence": "No configuration files found to assess.",
|
|
1230
|
+
"details": "Project directory lacks applicable config files.",
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if matched:
|
|
1234
|
+
return {
|
|
1235
|
+
"status": "not_satisfied",
|
|
1236
|
+
"evidence": (
|
|
1237
|
+
f"Default credential patterns detected in "
|
|
1238
|
+
f"{len(matched)} config file(s)."
|
|
1239
|
+
),
|
|
1240
|
+
"details": (
|
|
1241
|
+
"Files with potential default credentials: "
|
|
1242
|
+
+ "; ".join(os.path.basename(f) for f in matched[:5])
|
|
1243
|
+
+ ". Remove admin/admin, root/root, changeme, test/test, "
|
|
1244
|
+
"and other default credential patterns."
|
|
1245
|
+
),
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
return {
|
|
1249
|
+
"status": "satisfied",
|
|
1250
|
+
"evidence": (
|
|
1251
|
+
f"Scanned {total} config file(s) -- no default credential "
|
|
1252
|
+
"patterns detected."
|
|
1253
|
+
),
|
|
1254
|
+
"details": (
|
|
1255
|
+
"No instances of admin/admin, root/root, changeme, test/test, "
|
|
1256
|
+
"or other default credential patterns."
|
|
1257
|
+
),
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
def _check_secure_config_baselines(project_dir):
|
|
1262
|
+
"""SBD-29: Check Dockerfiles for STIG hardening and secure config baselines."""
|
|
1263
|
+
# Check Dockerfiles for hardening
|
|
1264
|
+
dockerfiles = _dir_or_file_exists(
|
|
1265
|
+
project_dir,
|
|
1266
|
+
glob_patterns=["Dockerfile*", "*.dockerfile"],
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
hardened_docker = 0
|
|
1270
|
+
docker_evidence = []
|
|
1271
|
+
for df_path in dockerfiles:
|
|
1272
|
+
try:
|
|
1273
|
+
with open(df_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
1274
|
+
content = f.read()
|
|
1275
|
+
except Exception:
|
|
1276
|
+
continue
|
|
1277
|
+
|
|
1278
|
+
checks = {
|
|
1279
|
+
"non_root_user": bool(
|
|
1280
|
+
re.search(r"USER\s+(?!root)\S+", content)
|
|
1281
|
+
),
|
|
1282
|
+
"drop_capabilities": bool(
|
|
1283
|
+
re.search(
|
|
1284
|
+
r"drop.*ALL|securityContext.*drop|cap_drop",
|
|
1285
|
+
content,
|
|
1286
|
+
re.IGNORECASE | re.DOTALL,
|
|
1287
|
+
)
|
|
1288
|
+
),
|
|
1289
|
+
"read_only_rootfs": bool(
|
|
1290
|
+
re.search(
|
|
1291
|
+
r"readOnlyRootFilesystem|read.only",
|
|
1292
|
+
content,
|
|
1293
|
+
re.IGNORECASE,
|
|
1294
|
+
)
|
|
1295
|
+
),
|
|
1296
|
+
"minimal_base": bool(
|
|
1297
|
+
re.search(
|
|
1298
|
+
r"FROM.*(:slim|:alpine|-slim|-minimal|distroless|hardened)",
|
|
1299
|
+
content,
|
|
1300
|
+
re.IGNORECASE,
|
|
1301
|
+
)
|
|
1302
|
+
),
|
|
1303
|
+
}
|
|
1304
|
+
passed = sum(checks.values())
|
|
1305
|
+
if passed >= 2:
|
|
1306
|
+
hardened_docker += 1
|
|
1307
|
+
docker_evidence.append(
|
|
1308
|
+
f"{os.path.basename(df_path)}: {passed}/4 hardening checks"
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
# Check for insecure configs
|
|
1312
|
+
insecure_config_patterns = [
|
|
1313
|
+
r"DEBUG\s*=\s*True|DEBUG\s*:\s*true",
|
|
1314
|
+
r"Access.Control.Allow.Origin.*\*|CORS.*\*|allow_origins.*\*",
|
|
1315
|
+
r"AllowOverride\s+All|PermitRootLogin\s+yes",
|
|
1316
|
+
]
|
|
1317
|
+
config_extensions = (".py", ".yaml", ".yml", ".conf", ".json", ".ini")
|
|
1318
|
+
insecure_matched, _ = _scan_files(
|
|
1319
|
+
project_dir, config_extensions, insecure_config_patterns
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
has_hardened = hardened_docker > 0
|
|
1323
|
+
has_insecure = len(insecure_matched) > 0
|
|
1324
|
+
|
|
1325
|
+
if has_hardened and not has_insecure:
|
|
1326
|
+
return {
|
|
1327
|
+
"status": "satisfied",
|
|
1328
|
+
"evidence": (
|
|
1329
|
+
f"Secure config baselines detected: {hardened_docker} "
|
|
1330
|
+
f"hardened Dockerfile(s), no insecure configuration patterns."
|
|
1331
|
+
),
|
|
1332
|
+
"details": "; ".join(docker_evidence),
|
|
1333
|
+
}
|
|
1334
|
+
elif has_hardened and has_insecure:
|
|
1335
|
+
return {
|
|
1336
|
+
"status": "partially_satisfied",
|
|
1337
|
+
"evidence": (
|
|
1338
|
+
f"Hardened Dockerfile(s) found ({hardened_docker}), but "
|
|
1339
|
+
f"insecure config patterns in {len(insecure_matched)} file(s)."
|
|
1340
|
+
),
|
|
1341
|
+
"details": (
|
|
1342
|
+
"Hardened: " + "; ".join(docker_evidence)
|
|
1343
|
+
+ " | Insecure configs: "
|
|
1344
|
+
+ "; ".join(
|
|
1345
|
+
os.path.basename(f) for f in insecure_matched[:5]
|
|
1346
|
+
)
|
|
1347
|
+
),
|
|
1348
|
+
}
|
|
1349
|
+
elif not has_hardened and dockerfiles:
|
|
1350
|
+
return {
|
|
1351
|
+
"status": "not_satisfied",
|
|
1352
|
+
"evidence": (
|
|
1353
|
+
f"Dockerfiles found ({len(dockerfiles)}) but none pass "
|
|
1354
|
+
"STIG hardening checks."
|
|
1355
|
+
),
|
|
1356
|
+
"details": (
|
|
1357
|
+
"Expected: non-root USER, drop ALL capabilities, "
|
|
1358
|
+
"read-only rootfs, minimal base image."
|
|
1359
|
+
),
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
# No Dockerfiles -- check configs only
|
|
1363
|
+
secure_config_patterns = [
|
|
1364
|
+
r"DEBUG\s*=\s*False|DEBUG\s*:\s*false",
|
|
1365
|
+
r"security.*hardening|stig.*compliance|cis.*benchmark",
|
|
1366
|
+
]
|
|
1367
|
+
secure_matched, _ = _scan_files(
|
|
1368
|
+
project_dir, config_extensions, secure_config_patterns
|
|
1369
|
+
)
|
|
1370
|
+
|
|
1371
|
+
if secure_matched and not has_insecure:
|
|
1372
|
+
return {
|
|
1373
|
+
"status": "partially_satisfied",
|
|
1374
|
+
"evidence": (
|
|
1375
|
+
f"Secure configuration patterns found in "
|
|
1376
|
+
f"{len(secure_matched)} file(s) but no Dockerfiles to assess."
|
|
1377
|
+
),
|
|
1378
|
+
"details": "; ".join(
|
|
1379
|
+
os.path.basename(f) for f in secure_matched[:5]
|
|
1380
|
+
),
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
return {
|
|
1384
|
+
"status": "not_satisfied",
|
|
1385
|
+
"evidence": "No secure configuration baselines detected.",
|
|
1386
|
+
"details": (
|
|
1387
|
+
"Expected: STIG-hardened Dockerfiles (non-root USER, drop ALL), "
|
|
1388
|
+
"DEBUG=False, no wildcard CORS, no permissive security settings."
|
|
1389
|
+
),
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
def _check_cui_markings(project_dir):
|
|
1394
|
+
"""SBD-31: Scan Python and Markdown files for CUI marking strings.
|
|
1395
|
+
|
|
1396
|
+
Returns satisfied if >80% of files contain CUI markings.
|
|
1397
|
+
"""
|
|
1398
|
+
patterns = [
|
|
1399
|
+
r"CUI\s*//\s*SP-CTI",
|
|
1400
|
+
r"CONTROLLED UNCLASSIFIED INFORMATION",
|
|
1401
|
+
r"\(CUI\)",
|
|
1402
|
+
]
|
|
1403
|
+
matched, total = _scan_files(project_dir, (".py", ".md"), patterns)
|
|
1404
|
+
|
|
1405
|
+
if total == 0:
|
|
1406
|
+
return {
|
|
1407
|
+
"status": "not_satisfied",
|
|
1408
|
+
"evidence": "No Python or Markdown files found to assess.",
|
|
1409
|
+
"details": "Project directory contains no .py or .md files.",
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
ratio = len(matched) / total
|
|
1413
|
+
if ratio > 0.8:
|
|
1414
|
+
return {
|
|
1415
|
+
"status": "satisfied",
|
|
1416
|
+
"evidence": (
|
|
1417
|
+
f"CUI markings found in {len(matched)}/{total} files "
|
|
1418
|
+
f"({ratio:.0%})."
|
|
1419
|
+
),
|
|
1420
|
+
"details": f"Threshold: >80%. Files scanned: {total}.",
|
|
1421
|
+
}
|
|
1422
|
+
elif ratio > 0.4:
|
|
1423
|
+
return {
|
|
1424
|
+
"status": "partially_satisfied",
|
|
1425
|
+
"evidence": (
|
|
1426
|
+
f"CUI markings found in {len(matched)}/{total} files "
|
|
1427
|
+
f"({ratio:.0%})."
|
|
1428
|
+
),
|
|
1429
|
+
"details": "Some files lack CUI markings. Must exceed 80% coverage.",
|
|
1430
|
+
}
|
|
1431
|
+
else:
|
|
1432
|
+
return {
|
|
1433
|
+
"status": "not_satisfied",
|
|
1434
|
+
"evidence": (
|
|
1435
|
+
f"CUI markings found in only {len(matched)}/{total} files "
|
|
1436
|
+
f"({ratio:.0%})."
|
|
1437
|
+
),
|
|
1438
|
+
"details": (
|
|
1439
|
+
"Majority of files lack CUI markings. "
|
|
1440
|
+
"Requires >80% coverage."
|
|
1441
|
+
),
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
# -----------------------------------------------------------------
|
|
1446
|
+
# Requirement-to-check mapping
|
|
1447
|
+
# -----------------------------------------------------------------
|
|
1448
|
+
|
|
1449
|
+
AUTO_CHECKS = {
|
|
1450
|
+
"SBD-01": _check_mfa_patterns,
|
|
1451
|
+
"SBD-02": _check_default_passwords,
|
|
1452
|
+
"SBD-03": _check_memory_safe_language,
|
|
1453
|
+
"SBD-04": _check_memory_safety_tooling,
|
|
1454
|
+
"SBD-05": _check_patch_cadence,
|
|
1455
|
+
"SBD-06": _check_vuln_disclosure,
|
|
1456
|
+
"SBD-08": _check_audit_logging_complete,
|
|
1457
|
+
"SBD-11": _check_tls_config,
|
|
1458
|
+
"SBD-12": _check_encryption_at_rest,
|
|
1459
|
+
"SBD-14": _check_rbac_least_priv,
|
|
1460
|
+
"SBD-16": _check_input_validation,
|
|
1461
|
+
"SBD-17": _check_output_encoding,
|
|
1462
|
+
"SBD-18": _check_security_headers,
|
|
1463
|
+
"SBD-19": _check_secure_error_handling,
|
|
1464
|
+
"SBD-21": _check_sbom_freshness,
|
|
1465
|
+
"SBD-22": _check_dep_vuln_scanning,
|
|
1466
|
+
"SBD-24": _check_threat_model,
|
|
1467
|
+
"SBD-28": _check_no_default_creds,
|
|
1468
|
+
"SBD-29": _check_secure_config_baselines,
|
|
1469
|
+
"SBD-31": _check_cui_markings,
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
# -----------------------------------------------------------------
|
|
1474
|
+
# Core assessment function
|
|
1475
|
+
# -----------------------------------------------------------------
|
|
1476
|
+
|
|
1477
|
+
def run_sbd_assessment(
|
|
1478
|
+
project_id,
|
|
1479
|
+
domain="all",
|
|
1480
|
+
project_dir=None,
|
|
1481
|
+
gate=False,
|
|
1482
|
+
output_path=None,
|
|
1483
|
+
db_path=None,
|
|
1484
|
+
):
|
|
1485
|
+
"""Run SbD assessment per CISA Secure by Design and DoDI 5000.87.
|
|
1486
|
+
|
|
1487
|
+
Args:
|
|
1488
|
+
project_id: The project identifier.
|
|
1489
|
+
domain: Filter to a specific SbD domain or "all".
|
|
1490
|
+
project_dir: Project directory for automated file-based checks.
|
|
1491
|
+
gate: If True, evaluate the SbD gate (0 critical not_satisfied = pass).
|
|
1492
|
+
output_path: Override output directory for the assessment report.
|
|
1493
|
+
db_path: Override database path.
|
|
1494
|
+
|
|
1495
|
+
Returns:
|
|
1496
|
+
Dict with assessment results, summary, gate result, and output file path.
|
|
1497
|
+
"""
|
|
1498
|
+
conn = get_connection(db_path=db_path)
|
|
1499
|
+
try:
|
|
1500
|
+
project = _get_project(conn, project_id)
|
|
1501
|
+
|
|
1502
|
+
# Load SbD requirements catalog
|
|
1503
|
+
sbd_data = _load_sbd_requirements()
|
|
1504
|
+
metadata = sbd_data.get("metadata", {})
|
|
1505
|
+
requirements = sbd_data.get("requirements", [])
|
|
1506
|
+
|
|
1507
|
+
# Filter by domain if specified
|
|
1508
|
+
if domain != "all":
|
|
1509
|
+
requirements = [
|
|
1510
|
+
r for r in requirements
|
|
1511
|
+
if r.get("domain") == domain
|
|
1512
|
+
]
|
|
1513
|
+
if not requirements:
|
|
1514
|
+
raise ValueError(
|
|
1515
|
+
f"No requirements found for domain '{domain}'. "
|
|
1516
|
+
"Valid domains: Authentication, Memory Safety, "
|
|
1517
|
+
"Vulnerability Mgmt, Intrusion Evidence, Cryptography, "
|
|
1518
|
+
"Access Control, Input Handling, Error Handling, "
|
|
1519
|
+
"Supply Chain, Threat Modeling, Defense in Depth, "
|
|
1520
|
+
"Secure Defaults, CUI Compliance, DoD Software Assurance."
|
|
1521
|
+
)
|
|
1522
|
+
|
|
1523
|
+
# Resolve project directory for auto-checks
|
|
1524
|
+
if project_dir and Path(project_dir).is_dir():
|
|
1525
|
+
can_auto_check = True
|
|
1526
|
+
elif (
|
|
1527
|
+
project.get("directory_path")
|
|
1528
|
+
and Path(project["directory_path"]).is_dir()
|
|
1529
|
+
):
|
|
1530
|
+
project_dir = project["directory_path"]
|
|
1531
|
+
can_auto_check = True
|
|
1532
|
+
else:
|
|
1533
|
+
can_auto_check = False
|
|
1534
|
+
|
|
1535
|
+
now = datetime.now(timezone.utc)
|
|
1536
|
+
results = []
|
|
1537
|
+
|
|
1538
|
+
# -- Assess each requirement --
|
|
1539
|
+
for req in requirements:
|
|
1540
|
+
req_id = req["id"]
|
|
1541
|
+
automation_level = req.get("automation_level", "manual")
|
|
1542
|
+
status = "not_assessed"
|
|
1543
|
+
evidence = ""
|
|
1544
|
+
details = ""
|
|
1545
|
+
notes = ""
|
|
1546
|
+
|
|
1547
|
+
if automation_level == "auto" and can_auto_check:
|
|
1548
|
+
if req_id in AUTO_CHECKS:
|
|
1549
|
+
try:
|
|
1550
|
+
check_result = AUTO_CHECKS[req_id](project_dir)
|
|
1551
|
+
status = check_result["status"]
|
|
1552
|
+
evidence = check_result["evidence"]
|
|
1553
|
+
details = check_result.get("details", "")
|
|
1554
|
+
except Exception as e:
|
|
1555
|
+
status = "not_assessed"
|
|
1556
|
+
evidence = f"Auto-check error: {e}"
|
|
1557
|
+
notes = "Auto-check failed; manual review required."
|
|
1558
|
+
else:
|
|
1559
|
+
# Auto-level requirement without a mapped check function
|
|
1560
|
+
status = "not_assessed"
|
|
1561
|
+
evidence = (
|
|
1562
|
+
"No automated check implemented for this requirement."
|
|
1563
|
+
)
|
|
1564
|
+
notes = "Manual review required."
|
|
1565
|
+
|
|
1566
|
+
elif automation_level == "auto" and not can_auto_check:
|
|
1567
|
+
status = "not_assessed"
|
|
1568
|
+
evidence = (
|
|
1569
|
+
"No project directory available for automated scanning."
|
|
1570
|
+
)
|
|
1571
|
+
notes = "Provide --project-dir to enable auto-checks."
|
|
1572
|
+
|
|
1573
|
+
elif automation_level == "semi" and can_auto_check:
|
|
1574
|
+
# Run partial check if a mapped function exists
|
|
1575
|
+
if req_id in AUTO_CHECKS:
|
|
1576
|
+
try:
|
|
1577
|
+
check_result = AUTO_CHECKS[req_id](project_dir)
|
|
1578
|
+
status = check_result["status"]
|
|
1579
|
+
evidence = check_result["evidence"]
|
|
1580
|
+
details = check_result.get("details", "")
|
|
1581
|
+
notes = (
|
|
1582
|
+
"Semi-automated check completed. "
|
|
1583
|
+
"Manual review required to verify full compliance."
|
|
1584
|
+
)
|
|
1585
|
+
except Exception as e:
|
|
1586
|
+
status = "not_assessed"
|
|
1587
|
+
evidence = f"Partial auto-check error: {e}"
|
|
1588
|
+
notes = (
|
|
1589
|
+
"Semi-automated check failed; "
|
|
1590
|
+
"full manual review required."
|
|
1591
|
+
)
|
|
1592
|
+
else:
|
|
1593
|
+
status = "not_assessed"
|
|
1594
|
+
evidence = (
|
|
1595
|
+
"Semi-automated: no automated component implemented."
|
|
1596
|
+
)
|
|
1597
|
+
notes = (
|
|
1598
|
+
f"Manual review required. Evidence needed: "
|
|
1599
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1600
|
+
)
|
|
1601
|
+
|
|
1602
|
+
elif automation_level == "semi" and not can_auto_check:
|
|
1603
|
+
status = "not_assessed"
|
|
1604
|
+
evidence = (
|
|
1605
|
+
"Semi-automated check requires project directory."
|
|
1606
|
+
)
|
|
1607
|
+
notes = (
|
|
1608
|
+
f"Manual review required. Evidence needed: "
|
|
1609
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1610
|
+
)
|
|
1611
|
+
|
|
1612
|
+
else:
|
|
1613
|
+
# manual automation_level
|
|
1614
|
+
status = "not_assessed"
|
|
1615
|
+
evidence = "Manual assessment required."
|
|
1616
|
+
notes = (
|
|
1617
|
+
f"This requirement must be verified manually. "
|
|
1618
|
+
f"Evidence needed: "
|
|
1619
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1620
|
+
)
|
|
1621
|
+
|
|
1622
|
+
result_entry = {
|
|
1623
|
+
"requirement_id": req_id,
|
|
1624
|
+
"domain": req.get("domain", ""),
|
|
1625
|
+
"title": req.get("title", ""),
|
|
1626
|
+
"description": req.get("description", ""),
|
|
1627
|
+
"priority": req.get("priority", "medium"),
|
|
1628
|
+
"automation_level": automation_level,
|
|
1629
|
+
"nist_controls": req.get("nist_controls", []),
|
|
1630
|
+
"cisa_commitment": req.get("cisa_commitment", ""),
|
|
1631
|
+
"status": status,
|
|
1632
|
+
"evidence": evidence,
|
|
1633
|
+
"details": details,
|
|
1634
|
+
"notes": notes,
|
|
1635
|
+
}
|
|
1636
|
+
results.append(result_entry)
|
|
1637
|
+
|
|
1638
|
+
# -- Upsert into sbd_assessments table --
|
|
1639
|
+
# Uses INSERT OR REPLACE on UNIQUE(project_id, requirement_id)
|
|
1640
|
+
try:
|
|
1641
|
+
conn.execute(
|
|
1642
|
+
"""INSERT OR REPLACE INTO sbd_assessments
|
|
1643
|
+
(project_id, assessment_date, assessor, domain,
|
|
1644
|
+
requirement_id, status, evidence_description,
|
|
1645
|
+
evidence_path, automation_result, cisa_commitment,
|
|
1646
|
+
notes, updated_at)
|
|
1647
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1648
|
+
(
|
|
1649
|
+
project_id,
|
|
1650
|
+
now.isoformat(),
|
|
1651
|
+
"icdev-compliance-engine",
|
|
1652
|
+
req.get("domain", ""),
|
|
1653
|
+
req_id,
|
|
1654
|
+
status,
|
|
1655
|
+
evidence,
|
|
1656
|
+
details if details else None,
|
|
1657
|
+
json.dumps({
|
|
1658
|
+
"automation_level": automation_level,
|
|
1659
|
+
"check_function": (
|
|
1660
|
+
AUTO_CHECKS[req_id].__name__
|
|
1661
|
+
if req_id in AUTO_CHECKS
|
|
1662
|
+
else None
|
|
1663
|
+
),
|
|
1664
|
+
}),
|
|
1665
|
+
req.get("cisa_commitment", ""),
|
|
1666
|
+
notes if notes else None,
|
|
1667
|
+
now.isoformat(),
|
|
1668
|
+
),
|
|
1669
|
+
)
|
|
1670
|
+
except Exception as e:
|
|
1671
|
+
print(
|
|
1672
|
+
f"Warning: Could not upsert assessment for {req_id}: {e}",
|
|
1673
|
+
file=sys.stderr,
|
|
1674
|
+
)
|
|
1675
|
+
|
|
1676
|
+
conn.commit()
|
|
1677
|
+
|
|
1678
|
+
# -- Build summary by domain --
|
|
1679
|
+
domain_order = [
|
|
1680
|
+
"Authentication",
|
|
1681
|
+
"Memory Safety",
|
|
1682
|
+
"Vulnerability Mgmt",
|
|
1683
|
+
"Intrusion Evidence",
|
|
1684
|
+
"Cryptography",
|
|
1685
|
+
"Access Control",
|
|
1686
|
+
"Input Handling",
|
|
1687
|
+
"Error Handling",
|
|
1688
|
+
"Supply Chain",
|
|
1689
|
+
"Threat Modeling",
|
|
1690
|
+
"Defense in Depth",
|
|
1691
|
+
"Secure Defaults",
|
|
1692
|
+
"CUI Compliance",
|
|
1693
|
+
"DoD Software Assurance",
|
|
1694
|
+
]
|
|
1695
|
+
summary = {}
|
|
1696
|
+
for d in domain_order:
|
|
1697
|
+
summary[d] = {
|
|
1698
|
+
"total": 0,
|
|
1699
|
+
"satisfied": 0,
|
|
1700
|
+
"partially_satisfied": 0,
|
|
1701
|
+
"not_satisfied": 0,
|
|
1702
|
+
"not_assessed": 0,
|
|
1703
|
+
"not_applicable": 0,
|
|
1704
|
+
"risk_accepted": 0,
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
for r in results:
|
|
1708
|
+
d = r["domain"]
|
|
1709
|
+
if d not in summary:
|
|
1710
|
+
summary[d] = {
|
|
1711
|
+
"total": 0,
|
|
1712
|
+
"satisfied": 0,
|
|
1713
|
+
"partially_satisfied": 0,
|
|
1714
|
+
"not_satisfied": 0,
|
|
1715
|
+
"not_assessed": 0,
|
|
1716
|
+
"not_applicable": 0,
|
|
1717
|
+
"risk_accepted": 0,
|
|
1718
|
+
}
|
|
1719
|
+
summary[d]["total"] += 1
|
|
1720
|
+
st = r["status"]
|
|
1721
|
+
if st in summary[d]:
|
|
1722
|
+
summary[d][st] += 1
|
|
1723
|
+
|
|
1724
|
+
# -- Gate evaluation --
|
|
1725
|
+
critical_not_satisfied = 0
|
|
1726
|
+
critical_failures = []
|
|
1727
|
+
for r in results:
|
|
1728
|
+
if (
|
|
1729
|
+
r["priority"] == "critical"
|
|
1730
|
+
and r["status"] == "not_satisfied"
|
|
1731
|
+
):
|
|
1732
|
+
critical_not_satisfied += 1
|
|
1733
|
+
critical_failures.append(
|
|
1734
|
+
f"{r['requirement_id']}: {r['title']}"
|
|
1735
|
+
)
|
|
1736
|
+
|
|
1737
|
+
gate_passed = critical_not_satisfied == 0
|
|
1738
|
+
gate_result = {
|
|
1739
|
+
"evaluated": gate,
|
|
1740
|
+
"passed": gate_passed,
|
|
1741
|
+
"critical_not_satisfied": critical_not_satisfied,
|
|
1742
|
+
"critical_failures": critical_failures,
|
|
1743
|
+
"reason": (
|
|
1744
|
+
"PASS: 0 critical-priority requirements have status "
|
|
1745
|
+
"not_satisfied"
|
|
1746
|
+
if gate_passed
|
|
1747
|
+
else (
|
|
1748
|
+
f"FAIL: {critical_not_satisfied} critical-priority "
|
|
1749
|
+
f"requirement(s) not satisfied: "
|
|
1750
|
+
f"{', '.join(critical_failures)}"
|
|
1751
|
+
)
|
|
1752
|
+
),
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
# -- Generate Markdown assessment report --
|
|
1756
|
+
cui_config = _load_cui_config()
|
|
1757
|
+
doc_header = cui_config.get(
|
|
1758
|
+
"document_header", "CUI // SP-CTI"
|
|
1759
|
+
).strip()
|
|
1760
|
+
doc_footer = cui_config.get(
|
|
1761
|
+
"document_footer", "CUI // SP-CTI"
|
|
1762
|
+
).strip()
|
|
1763
|
+
|
|
1764
|
+
lines = [
|
|
1765
|
+
doc_header,
|
|
1766
|
+
"",
|
|
1767
|
+
"# SbD Assessment Report -- CISA Secure by Design / DoDI 5000.87",
|
|
1768
|
+
"",
|
|
1769
|
+
f"**Project:** {project.get('name', project_id)} ({project_id})",
|
|
1770
|
+
f"**Assessment Date:** {now.strftime('%Y-%m-%d %H:%M UTC')}",
|
|
1771
|
+
"**Assessor:** ICDEV Compliance Engine (automated)",
|
|
1772
|
+
f"**Domain Scope:** {domain}",
|
|
1773
|
+
(
|
|
1774
|
+
f"**CISA SbD Revision:** "
|
|
1775
|
+
f"{metadata.get('revision', 'N/A')}"
|
|
1776
|
+
),
|
|
1777
|
+
"**Classification:** CUI // SP-CTI",
|
|
1778
|
+
"",
|
|
1779
|
+
"---",
|
|
1780
|
+
"",
|
|
1781
|
+
"## Executive Summary",
|
|
1782
|
+
"",
|
|
1783
|
+
]
|
|
1784
|
+
|
|
1785
|
+
# Summary table
|
|
1786
|
+
lines.append(
|
|
1787
|
+
"| Domain | Total | Satisfied | Partial | Not Satisfied "
|
|
1788
|
+
"| Not Assessed | N/A | Risk Accepted |"
|
|
1789
|
+
)
|
|
1790
|
+
lines.append(
|
|
1791
|
+
"|--------|-------|-----------|---------|---------------"
|
|
1792
|
+
"|--------------|-----|---------------|"
|
|
1793
|
+
)
|
|
1794
|
+
|
|
1795
|
+
grand_total = {
|
|
1796
|
+
"total": 0,
|
|
1797
|
+
"satisfied": 0,
|
|
1798
|
+
"partially_satisfied": 0,
|
|
1799
|
+
"not_satisfied": 0,
|
|
1800
|
+
"not_assessed": 0,
|
|
1801
|
+
"not_applicable": 0,
|
|
1802
|
+
"risk_accepted": 0,
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
for d in domain_order:
|
|
1806
|
+
s = summary.get(d, {})
|
|
1807
|
+
if s.get("total", 0) == 0:
|
|
1808
|
+
continue
|
|
1809
|
+
lines.append(
|
|
1810
|
+
f"| {d} | {s['total']} | {s['satisfied']} | "
|
|
1811
|
+
f"{s['partially_satisfied']} | {s['not_satisfied']} | "
|
|
1812
|
+
f"{s['not_assessed']} | {s['not_applicable']} | "
|
|
1813
|
+
f"{s['risk_accepted']} |"
|
|
1814
|
+
)
|
|
1815
|
+
for key in grand_total:
|
|
1816
|
+
grand_total[key] += s.get(key, 0)
|
|
1817
|
+
|
|
1818
|
+
lines.append(
|
|
1819
|
+
f"| **Total** | **{grand_total['total']}** | "
|
|
1820
|
+
f"**{grand_total['satisfied']}** | "
|
|
1821
|
+
f"**{grand_total['partially_satisfied']}** | "
|
|
1822
|
+
f"**{grand_total['not_satisfied']}** | "
|
|
1823
|
+
f"**{grand_total['not_assessed']}** | "
|
|
1824
|
+
f"**{grand_total['not_applicable']}** | "
|
|
1825
|
+
f"**{grand_total['risk_accepted']}** |"
|
|
1826
|
+
)
|
|
1827
|
+
lines.append("")
|
|
1828
|
+
|
|
1829
|
+
# Gate evaluation section
|
|
1830
|
+
if gate:
|
|
1831
|
+
gate_label = (
|
|
1832
|
+
"PASS" if gate_result["passed"] else "**FAIL**"
|
|
1833
|
+
)
|
|
1834
|
+
lines.extend([
|
|
1835
|
+
"## SbD Gate Evaluation",
|
|
1836
|
+
"",
|
|
1837
|
+
f"**Gate Result:** {gate_label}",
|
|
1838
|
+
(
|
|
1839
|
+
"**Criteria:** 0 critical-priority requirements "
|
|
1840
|
+
"with status not_satisfied"
|
|
1841
|
+
),
|
|
1842
|
+
f"**Critical Failures:** {critical_not_satisfied}",
|
|
1843
|
+
"",
|
|
1844
|
+
])
|
|
1845
|
+
if critical_failures:
|
|
1846
|
+
lines.append("**Failed Requirements:**")
|
|
1847
|
+
for cf in critical_failures:
|
|
1848
|
+
lines.append(f"- {cf}")
|
|
1849
|
+
lines.append("")
|
|
1850
|
+
|
|
1851
|
+
lines.extend(["---", ""])
|
|
1852
|
+
|
|
1853
|
+
# -- Detailed findings per domain --
|
|
1854
|
+
lines.append("## Detailed Findings")
|
|
1855
|
+
lines.append("")
|
|
1856
|
+
|
|
1857
|
+
for d in domain_order:
|
|
1858
|
+
domain_results = [r for r in results if r["domain"] == d]
|
|
1859
|
+
if not domain_results:
|
|
1860
|
+
continue
|
|
1861
|
+
|
|
1862
|
+
lines.append(f"### {d}")
|
|
1863
|
+
lines.append("")
|
|
1864
|
+
|
|
1865
|
+
for r in domain_results:
|
|
1866
|
+
status_display = r["status"].replace("_", " ").title()
|
|
1867
|
+
priority_display = r["priority"].upper()
|
|
1868
|
+
nist_str = (
|
|
1869
|
+
", ".join(r["nist_controls"])
|
|
1870
|
+
if r["nist_controls"]
|
|
1871
|
+
else "N/A"
|
|
1872
|
+
)
|
|
1873
|
+
cisa_str = r.get("cisa_commitment", "N/A") or "N/A"
|
|
1874
|
+
|
|
1875
|
+
lines.extend([
|
|
1876
|
+
f"#### {r['requirement_id']}: {r['title']}",
|
|
1877
|
+
"",
|
|
1878
|
+
f"**Priority:** {priority_display} ",
|
|
1879
|
+
f"**Status:** {status_display} ",
|
|
1880
|
+
f"**Automation Level:** {r['automation_level']} ",
|
|
1881
|
+
f"**NIST Controls:** {nist_str} ",
|
|
1882
|
+
f"**CISA Commitment:** {cisa_str}",
|
|
1883
|
+
"",
|
|
1884
|
+
f"**Evidence:** {r['evidence']}",
|
|
1885
|
+
"",
|
|
1886
|
+
])
|
|
1887
|
+
if r["details"]:
|
|
1888
|
+
lines.append(f"**Details:** {r['details']}")
|
|
1889
|
+
lines.append("")
|
|
1890
|
+
if r["notes"]:
|
|
1891
|
+
lines.append(f"**Notes:** {r['notes']}")
|
|
1892
|
+
lines.append("")
|
|
1893
|
+
|
|
1894
|
+
lines.extend(["---", ""])
|
|
1895
|
+
|
|
1896
|
+
# Append CUI footer
|
|
1897
|
+
lines.extend([doc_footer, ""])
|
|
1898
|
+
content = "\n".join(lines)
|
|
1899
|
+
|
|
1900
|
+
# -- Write output file --
|
|
1901
|
+
if output_path:
|
|
1902
|
+
out_dir = Path(output_path)
|
|
1903
|
+
else:
|
|
1904
|
+
dir_path = project.get("directory_path", "")
|
|
1905
|
+
if dir_path:
|
|
1906
|
+
out_dir = Path(dir_path) / "compliance"
|
|
1907
|
+
else:
|
|
1908
|
+
out_dir = BASE_DIR / ".tmp" / "compliance" / project_id
|
|
1909
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
1910
|
+
|
|
1911
|
+
domain_suffix = (
|
|
1912
|
+
domain.lower().replace(" ", "_")
|
|
1913
|
+
if domain != "all"
|
|
1914
|
+
else "all"
|
|
1915
|
+
)
|
|
1916
|
+
out_file = (
|
|
1917
|
+
out_dir
|
|
1918
|
+
/ f"sbd_cisa_{project_id}_{domain_suffix}_"
|
|
1919
|
+
f"{now.strftime('%Y%m%d_%H%M%S')}.md"
|
|
1920
|
+
)
|
|
1921
|
+
|
|
1922
|
+
with open(out_file, "w", encoding="utf-8") as f:
|
|
1923
|
+
f.write(content)
|
|
1924
|
+
|
|
1925
|
+
# -- Log audit event --
|
|
1926
|
+
_log_audit_event(
|
|
1927
|
+
conn,
|
|
1928
|
+
project_id,
|
|
1929
|
+
f"SbD assessment completed ({domain})",
|
|
1930
|
+
{
|
|
1931
|
+
"domain": domain,
|
|
1932
|
+
"requirements_assessed": len(results),
|
|
1933
|
+
"summary": {k: v for k, v in grand_total.items()},
|
|
1934
|
+
"gate_result": gate_result,
|
|
1935
|
+
"output_file": str(out_file),
|
|
1936
|
+
},
|
|
1937
|
+
out_file,
|
|
1938
|
+
)
|
|
1939
|
+
|
|
1940
|
+
# -- Console output --
|
|
1941
|
+
print("SbD assessment completed:")
|
|
1942
|
+
print(f" File: {out_file}")
|
|
1943
|
+
print(f" Scope: {domain}")
|
|
1944
|
+
print(f" Requirements assessed: {len(results)}")
|
|
1945
|
+
for d in domain_order:
|
|
1946
|
+
s = summary.get(d, {})
|
|
1947
|
+
if s.get("total", 0) == 0:
|
|
1948
|
+
continue
|
|
1949
|
+
print(
|
|
1950
|
+
f" {d}: "
|
|
1951
|
+
f"SAT={s['satisfied']} "
|
|
1952
|
+
f"PARTIAL={s['partially_satisfied']} "
|
|
1953
|
+
f"NOT_SAT={s['not_satisfied']} "
|
|
1954
|
+
f"NOT_ASSESSED={s['not_assessed']}"
|
|
1955
|
+
)
|
|
1956
|
+
|
|
1957
|
+
if gate:
|
|
1958
|
+
print(f"\n Gate: {gate_result['reason']}")
|
|
1959
|
+
|
|
1960
|
+
return {
|
|
1961
|
+
"output_file": str(out_file),
|
|
1962
|
+
"results": results,
|
|
1963
|
+
"summary": summary,
|
|
1964
|
+
"gate_result": gate_result,
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
finally:
|
|
1968
|
+
conn.close()
|
|
1969
|
+
|
|
1970
|
+
|
|
1971
|
+
def assess_project(
|
|
1972
|
+
project_id,
|
|
1973
|
+
domain="all",
|
|
1974
|
+
project_dir=None,
|
|
1975
|
+
gate=False,
|
|
1976
|
+
output_path=None,
|
|
1977
|
+
db_path=None,
|
|
1978
|
+
):
|
|
1979
|
+
"""Alias for run_sbd_assessment (MCP compatibility)."""
|
|
1980
|
+
return run_sbd_assessment(
|
|
1981
|
+
project_id,
|
|
1982
|
+
domain=domain,
|
|
1983
|
+
project_dir=project_dir,
|
|
1984
|
+
gate=gate,
|
|
1985
|
+
output_path=output_path,
|
|
1986
|
+
db_path=db_path,
|
|
1987
|
+
)
|
|
1988
|
+
|
|
1989
|
+
|
|
1990
|
+
# -----------------------------------------------------------------
|
|
1991
|
+
# CLI entrypoint
|
|
1992
|
+
# -----------------------------------------------------------------
|
|
1993
|
+
|
|
1994
|
+
if __name__ == "__main__":
|
|
1995
|
+
parser = argparse.ArgumentParser(
|
|
1996
|
+
description="Run SbD assessment per CISA Secure by Design"
|
|
1997
|
+
)
|
|
1998
|
+
parser.add_argument(
|
|
1999
|
+
"--project-id", required=True, help="Project ID"
|
|
2000
|
+
)
|
|
2001
|
+
parser.add_argument(
|
|
2002
|
+
"--domain",
|
|
2003
|
+
default="all",
|
|
2004
|
+
choices=[
|
|
2005
|
+
"all",
|
|
2006
|
+
"Authentication",
|
|
2007
|
+
"Memory Safety",
|
|
2008
|
+
"Vulnerability Mgmt",
|
|
2009
|
+
"Intrusion Evidence",
|
|
2010
|
+
"Cryptography",
|
|
2011
|
+
"Access Control",
|
|
2012
|
+
"Input Handling",
|
|
2013
|
+
"Error Handling",
|
|
2014
|
+
"Supply Chain",
|
|
2015
|
+
"Threat Modeling",
|
|
2016
|
+
"Defense in Depth",
|
|
2017
|
+
"Secure Defaults",
|
|
2018
|
+
"CUI Compliance",
|
|
2019
|
+
"DoD Software Assurance",
|
|
2020
|
+
],
|
|
2021
|
+
help="SbD domain to assess (default: all)",
|
|
2022
|
+
)
|
|
2023
|
+
parser.add_argument(
|
|
2024
|
+
"--project-dir",
|
|
2025
|
+
help="Project directory for automated file-based checks",
|
|
2026
|
+
)
|
|
2027
|
+
parser.add_argument(
|
|
2028
|
+
"--gate",
|
|
2029
|
+
action="store_true",
|
|
2030
|
+
help="Evaluate SbD gate (0 critical not_satisfied = pass)",
|
|
2031
|
+
)
|
|
2032
|
+
parser.add_argument(
|
|
2033
|
+
"--output-dir",
|
|
2034
|
+
help="Output directory for the assessment report",
|
|
2035
|
+
)
|
|
2036
|
+
parser.add_argument(
|
|
2037
|
+
"--db-path",
|
|
2038
|
+
type=Path,
|
|
2039
|
+
default=DB_PATH,
|
|
2040
|
+
help="Override database path",
|
|
2041
|
+
)
|
|
2042
|
+
parser.add_argument("--json", action="store_true", dest="json_output", help="JSON output")
|
|
2043
|
+
args = parser.parse_args()
|
|
2044
|
+
|
|
2045
|
+
try:
|
|
2046
|
+
result = run_sbd_assessment(
|
|
2047
|
+
project_id=args.project_id,
|
|
2048
|
+
domain=args.domain,
|
|
2049
|
+
project_dir=args.project_dir,
|
|
2050
|
+
gate=args.gate,
|
|
2051
|
+
output_path=args.output_dir,
|
|
2052
|
+
db_path=args.db_path,
|
|
2053
|
+
)
|
|
2054
|
+
print(
|
|
2055
|
+
json.dumps(
|
|
2056
|
+
{
|
|
2057
|
+
"output_file": result.get("output_file"),
|
|
2058
|
+
"summary": result.get("summary"),
|
|
2059
|
+
"gate_result": result.get("gate_result"),
|
|
2060
|
+
},
|
|
2061
|
+
indent=2,
|
|
2062
|
+
)
|
|
2063
|
+
)
|
|
2064
|
+
|
|
2065
|
+
if args.gate and not result["gate_result"]["passed"]:
|
|
2066
|
+
sys.exit(1)
|
|
2067
|
+
|
|
2068
|
+
except (FileNotFoundError, ValueError) as e:
|
|
2069
|
+
print(f"ERROR: {e}", file=sys.stderr)
|
|
2070
|
+
sys.exit(1)
|