icdev 1.0.0__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.
- icdev/__init__.py +18 -0
- icdev/_paths.py +85 -0
- icdev/_version.py +3 -0
- icdev/data/__init__.py +1 -0
- icdev/data/args/__init__.py +1 -0
- icdev/data/args/agent_authority.yaml +61 -0
- icdev/data/args/agent_config.yaml +355 -0
- icdev/data/args/agentic_fitness.yaml +31 -0
- icdev/data/args/ai_governance_config.yaml +137 -0
- icdev/data/args/atlas_critique_config.yaml +66 -0
- icdev/data/args/bedrock_models.yaml +63 -0
- icdev/data/args/cicd_config.yaml +82 -0
- icdev/data/args/classification_config.yaml +232 -0
- icdev/data/args/cli_config.yaml +154 -0
- icdev/data/args/cloud_config.yaml +63 -0
- icdev/data/args/code_pattern_config.yaml +151 -0
- icdev/data/args/code_quality_config.yaml +47 -0
- icdev/data/args/companion_registry.yaml +202 -0
- icdev/data/args/context_config.yaml +82 -0
- icdev/data/args/csp_monitor_config.yaml +268 -0
- icdev/data/args/cui_markings.yaml +35 -0
- icdev/data/args/db_config.yaml +40 -0
- icdev/data/args/deployment_profiles.yaml +248 -0
- icdev/data/args/dev_profile_config.yaml +144 -0
- icdev/data/args/devsecops_config.yaml +286 -0
- icdev/data/args/endpoint_security_config.yaml +137 -0
- icdev/data/args/extension_config.yaml +79 -0
- icdev/data/args/file_access_tiers.yaml +88 -0
- icdev/data/args/framework_registry.yaml +415 -0
- icdev/data/args/innovation_config.yaml +431 -0
- icdev/data/args/installation_manifest.yaml +1087 -0
- icdev/data/args/llm_config.yaml +495 -0
- icdev/data/args/maintenance_config.yaml +55 -0
- icdev/data/args/memory_config.yaml +83 -0
- icdev/data/args/monitoring_config.yaml +127 -0
- icdev/data/args/mosa_config.yaml +190 -0
- icdev/data/args/nlq_config.yaml +35 -0
- icdev/data/args/observability_config.yaml +39 -0
- icdev/data/args/observability_tracing_config.yaml +170 -0
- icdev/data/args/oscal_tools_config.yaml +43 -0
- icdev/data/args/owasp_agentic_config.yaml +171 -0
- icdev/data/args/phase_registry.yaml +618 -0
- icdev/data/args/project_defaults.yaml +235 -0
- icdev/data/args/prompt_chains.yaml +163 -0
- icdev/data/args/resilience_config.yaml +50 -0
- icdev/data/args/ricoas_config.yaml +191 -0
- icdev/data/args/role_personas.yaml +362 -0
- icdev/data/args/scaling_config.yaml +176 -0
- icdev/data/args/security_gates.yaml +685 -0
- icdev/data/args/skill_injection_config.yaml +322 -0
- icdev/data/args/spec_config.yaml +53 -0
- icdev/data/args/supply_chain_config.yaml +76 -0
- icdev/data/args/translation_config.yaml +228 -0
- icdev/data/args/workflow_templates/ato_acceleration.yaml +54 -0
- icdev/data/args/workflow_templates/build_deploy.yaml +63 -0
- icdev/data/args/workflow_templates/full_compliance.yaml +43 -0
- icdev/data/args/workflow_templates/security_hardening.yaml +55 -0
- icdev/data/args/worktree_config.yaml +34 -0
- icdev/data/args/zta_config.yaml +247 -0
- icdev/data/context/__init__.py +1 -0
- icdev/data/context/agent/__init__.py +1 -0
- icdev/data/context/agent/response_schemas/__init__.py +1 -0
- icdev/data/context/agent/response_schemas/debate_position.json +46 -0
- icdev/data/context/agent/response_schemas/fitness_scorecard.json +74 -0
- icdev/data/context/agent/response_schemas/review_decision.json +39 -0
- icdev/data/context/agent/response_schemas/task_decomposition.json +82 -0
- icdev/data/context/agent/response_schemas/veto_decision.json +40 -0
- icdev/data/context/agentic/__init__.py +1 -0
- icdev/data/context/agentic/architecture_patterns.md +269 -0
- icdev/data/context/agentic/capability_registry.yaml +202 -0
- icdev/data/context/agentic/csp_mcp_registry.yaml +280 -0
- icdev/data/context/agentic/fitness_rubric.md +56 -0
- icdev/data/context/agentic/governance_baseline.md +205 -0
- icdev/data/context/ci/__init__.py +1 -0
- icdev/data/context/ci/worktree_templates.json +44 -0
- icdev/data/context/cloud/__init__.py +1 -0
- icdev/data/context/cloud/csp_service_registry.json +739 -0
- icdev/data/context/compliance/__init__.py +1 -0
- icdev/data/context/compliance/atlas_mitigations.json +293 -0
- icdev/data/context/compliance/atlas_techniques.json +833 -0
- icdev/data/context/compliance/cisa_sbd_requirements.json +432 -0
- icdev/data/context/compliance/cjis_security_policy.json +522 -0
- icdev/data/context/compliance/cmmc_practices.json +2494 -0
- icdev/data/context/compliance/cmmc_report_template.md +142 -0
- icdev/data/context/compliance/cnssi_1253_overlay.json +109 -0
- icdev/data/context/compliance/control_crosswalk.json +1914 -0
- icdev/data/context/compliance/control_families/__init__.py +1 -0
- icdev/data/context/compliance/csp_certifications.json +251 -0
- icdev/data/context/compliance/cssp_report_template.md +193 -0
- icdev/data/context/compliance/cui_templates/__init__.py +1 -0
- icdev/data/context/compliance/cui_templates/banner_block.txt +4 -0
- icdev/data/context/compliance/cui_templates/code_header.txt +8 -0
- icdev/data/context/compliance/cui_templates/document_template.md +35 -0
- icdev/data/context/compliance/data_type_framework_map.json +321 -0
- icdev/data/context/compliance/data_type_registry.json +147 -0
- icdev/data/context/compliance/dod_cssp_8530.json +463 -0
- icdev/data/context/compliance/eu_ai_act_annex_iii.json +108 -0
- icdev/data/context/compliance/export_templates/__init__.py +1 -0
- icdev/data/context/compliance/export_templates/emass_controls.csv.j2 +4 -0
- icdev/data/context/compliance/export_templates/evidence_package.md.j2 +39 -0
- icdev/data/context/compliance/export_templates/executive_summary.md.j2 +55 -0
- icdev/data/context/compliance/export_templates/poam_tracking.csv.j2 +4 -0
- icdev/data/context/compliance/fedramp_20x_ksi_schemas.json +133 -0
- icdev/data/context/compliance/fedramp_high_baseline.json +4370 -0
- icdev/data/context/compliance/fedramp_moderate_baseline.json +2183 -0
- icdev/data/context/compliance/fedramp_report_template.md +181 -0
- icdev/data/context/compliance/fips_200_areas.json +362 -0
- icdev/data/context/compliance/gao_ai_accountability.json +262 -0
- icdev/data/context/compliance/hipaa_security_rule.json +720 -0
- icdev/data/context/compliance/hitrust_csf_v11.json +930 -0
- icdev/data/context/compliance/impact_level_profiles.json +251 -0
- icdev/data/context/compliance/incident_response_template.md +1110 -0
- icdev/data/context/compliance/iso27001_2022_controls.json +750 -0
- icdev/data/context/compliance/iso27001_nist_bridge.json +382 -0
- icdev/data/context/compliance/iso42001_controls.json +254 -0
- icdev/data/context/compliance/ivv_checklist_template.md +80 -0
- icdev/data/context/compliance/ivv_report_template.md +116 -0
- icdev/data/context/compliance/ivv_requirements.json +372 -0
- icdev/data/context/compliance/mosa_crosswalk.json +327 -0
- icdev/data/context/compliance/mosa_framework.json +250 -0
- icdev/data/context/compliance/narrative_templates/AC.md.j2 +101 -0
- icdev/data/context/compliance/narrative_templates/AU.md.j2 +106 -0
- icdev/data/context/compliance/narrative_templates/IA.md.j2 +104 -0
- icdev/data/context/compliance/narrative_templates/SC.md.j2 +102 -0
- icdev/data/context/compliance/narrative_templates/SI.md.j2 +111 -0
- icdev/data/context/compliance/narrative_templates/__init__.py +1 -0
- icdev/data/context/compliance/narrative_templates/default.md.j2 +50 -0
- icdev/data/context/compliance/narrative_templates/executive_summary.j2 +27 -0
- icdev/data/context/compliance/narrative_templates/poam_milestone.j2 +19 -0
- icdev/data/context/compliance/narrative_templates/ssp_section.j2 +11 -0
- icdev/data/context/compliance/nist_800_171_controls.json +1552 -0
- icdev/data/context/compliance/nist_800_207_crosswalk.json +399 -0
- icdev/data/context/compliance/nist_800_207_zta.json +258 -0
- icdev/data/context/compliance/nist_800_53.json +324 -0
- icdev/data/context/compliance/nist_ai_600_1_genai.json +326 -0
- icdev/data/context/compliance/nist_ai_rmf.json +206 -0
- icdev/data/context/compliance/nist_sp_800_60_types.json +1667 -0
- icdev/data/context/compliance/omb_m25_21_high_impact_ai.json +248 -0
- icdev/data/context/compliance/omb_m26_04_unbiased_ai.json +262 -0
- icdev/data/context/compliance/owasp_agentic_asi.json +133 -0
- icdev/data/context/compliance/owasp_agentic_threats.json +285 -0
- icdev/data/context/compliance/owasp_llm_top10.json +274 -0
- icdev/data/context/compliance/pci_dss_v4.json +510 -0
- icdev/data/context/compliance/poam_template.md +117 -0
- icdev/data/context/compliance/safeai_controls.json +512 -0
- icdev/data/context/compliance/sbd_report_template.md +77 -0
- icdev/data/context/compliance/siem_config_templates/__init__.py +1 -0
- icdev/data/context/compliance/siem_config_templates/filebeat.yml +213 -0
- icdev/data/context/compliance/siem_config_templates/log_sources.json +208 -0
- icdev/data/context/compliance/soc2_trust_criteria.json +661 -0
- icdev/data/context/compliance/ssp_template.md +432 -0
- icdev/data/context/compliance/stig_templates/__init__.py +1 -0
- icdev/data/context/compliance/stig_templates/webapp_stig.json +139 -0
- icdev/data/context/compliance/xai_requirements.json +108 -0
- icdev/data/context/dashboard/__init__.py +1 -0
- icdev/data/context/dashboard/nlq_examples.json +50 -0
- icdev/data/context/dashboard/schema_descriptions.json +23 -0
- icdev/data/context/integration/__init__.py +1 -0
- icdev/data/context/integration/approval_workflows.json +32 -0
- icdev/data/context/integration/gitlab_field_mappings.json +33 -0
- icdev/data/context/integration/jira_field_mappings.json +32 -0
- icdev/data/context/integration/reqif_export_schema.json +23 -0
- icdev/data/context/integration/servicenow_field_mappings.json +22 -0
- icdev/data/context/languages/__init__.py +1 -0
- icdev/data/context/languages/framework_patterns.json +205 -0
- icdev/data/context/languages/language_registry.json +279 -0
- icdev/data/context/llm/__init__.py +1 -0
- icdev/data/context/llm/example_provider.py +86 -0
- icdev/data/context/mbse/__init__.py +1 -0
- icdev/data/context/mbse/des_report_template.md +162 -0
- icdev/data/context/mbse/des_requirements.json +411 -0
- icdev/data/context/mbse/digital_thread_patterns.json +403 -0
- icdev/data/context/mbse/reqif_schema.json +280 -0
- icdev/data/context/mbse/sysml_element_types.json +432 -0
- icdev/data/context/modernization/__init__.py +1 -0
- icdev/data/context/modernization/db_type_mappings.json +148 -0
- icdev/data/context/modernization/decomposition_patterns.json +284 -0
- icdev/data/context/modernization/framework_migration_patterns.json +359 -0
- icdev/data/context/modernization/migration_report_template.md +168 -0
- icdev/data/context/modernization/seven_rs_catalog.json +369 -0
- icdev/data/context/modernization/version_upgrade_rules.json +279 -0
- icdev/data/context/oscal/NIST_SP-800-53_rev5_catalog.json +254987 -0
- icdev/data/context/oscal/README.md +43 -0
- icdev/data/context/patterns/__init__.py +1 -0
- icdev/data/context/profiles/__init__.py +1 -0
- icdev/data/context/profiles/dod_baseline_v1.yaml +145 -0
- icdev/data/context/profiles/fedramp_baseline_v1.yaml +143 -0
- icdev/data/context/profiles/financial_baseline_v1.yaml +142 -0
- icdev/data/context/profiles/healthcare_baseline_v1.yaml +135 -0
- icdev/data/context/profiles/law_enforcement_v1.yaml +129 -0
- icdev/data/context/profiles/startup_v1.yaml +134 -0
- icdev/data/context/requirements/__init__.py +1 -0
- icdev/data/context/requirements/ambiguity_patterns.json +97 -0
- icdev/data/context/requirements/boundary_impact_rules.json +123 -0
- icdev/data/context/requirements/default_constitutions.json +67 -0
- icdev/data/context/requirements/document_extraction_rules.json +58 -0
- icdev/data/context/requirements/gap_patterns.json +108 -0
- icdev/data/context/requirements/readiness_rubric.json +78 -0
- icdev/data/context/requirements/red_alternative_patterns.json +210 -0
- icdev/data/context/requirements/safe_templates.json +72 -0
- icdev/data/context/requirements/spec_quality_checklist.json +122 -0
- icdev/data/context/simulation/__init__.py +1 -0
- icdev/data/context/simulation/architecture_patterns.json +36 -0
- icdev/data/context/simulation/coa_templates.json +38 -0
- icdev/data/context/simulation/cost_models.json +23 -0
- icdev/data/context/simulation/risk_categories.json +46 -0
- icdev/data/context/supply_chain/__init__.py +1 -0
- icdev/data/context/supply_chain/isa_templates.json +129 -0
- icdev/data/context/supply_chain/nist_800_161_controls.json +247 -0
- icdev/data/context/supply_chain/scrm_risk_matrix.json +147 -0
- icdev/data/context/templates/__init__.py +1 -0
- icdev/data/context/templates/ansible/__init__.py +1 -0
- icdev/data/context/templates/ansible/playbooks/__init__.py +1 -0
- icdev/data/context/templates/ansible/roles/__init__.py +1 -0
- icdev/data/context/templates/gitlab_ci/__init__.py +1 -0
- icdev/data/context/templates/grafana/__init__.py +1 -0
- icdev/data/context/templates/kubernetes/__init__.py +1 -0
- icdev/data/context/templates/project/__init__.py +1 -0
- icdev/data/context/templates/project/api/__init__.py +1 -0
- icdev/data/context/templates/project/cli/__init__.py +1 -0
- icdev/data/context/templates/project/data_pipeline/__init__.py +1 -0
- icdev/data/context/templates/project/iac/__init__.py +1 -0
- icdev/data/context/templates/project/javascript_frontend/__init__.py +1 -0
- icdev/data/context/templates/project/javascript_frontend/src/__init__.py +1 -0
- icdev/data/context/templates/project/javascript_frontend/tests/__init__.py +1 -0
- icdev/data/context/templates/project/microservice/__init__.py +1 -0
- icdev/data/context/templates/project/python_backend/__init__.py +1 -0
- icdev/data/context/templates/project/python_backend/src/__init__.py +1 -0
- icdev/data/context/templates/project/python_backend/tests/__init__.py +1 -0
- icdev/data/context/templates/project/python_backend/tests/features/__init__.py +1 -0
- icdev/data/context/templates/project/python_backend/tests/steps/__init__.py +1 -0
- icdev/data/context/templates/terraform/__init__.py +1 -0
- icdev/data/context/templates/terraform/govcloud_base/__init__.py +1 -0
- icdev/data/context/templates/terraform/modules/__init__.py +1 -0
- icdev/data/context/tone/__init__.py +1 -0
- icdev/data/context/translation/dependency_mappings.json +186 -0
- icdev/data/context/translation/type_mappings.json +149 -0
- icdev/data/docs/README.md +187 -0
- icdev/data/docs/__init__.py +1 -0
- icdev/data/docs/admin/gateway-guide.md +338 -0
- icdev/data/docs/admin/marketplace-guide.md +396 -0
- icdev/data/docs/admin/monitoring-guide.md +509 -0
- icdev/data/docs/architecture/compliance-framework.md +764 -0
- icdev/data/docs/architecture/database-schema.md +689 -0
- icdev/data/docs/architecture/gotcha-framework.md +518 -0
- icdev/data/docs/architecture/multi-agent-system.md +603 -0
- icdev/data/docs/dx/README.md +106 -0
- icdev/data/docs/dx/__init__.py +1 -0
- icdev/data/docs/dx/ci-cd-integration.md +378 -0
- icdev/data/docs/dx/claude-code-guide.md +213 -0
- icdev/data/docs/dx/companion-guide.md +232 -0
- icdev/data/docs/dx/dev-profiles.md +309 -0
- icdev/data/docs/dx/icdev-yaml-spec.md +219 -0
- icdev/data/docs/dx/integration-tiers.md +279 -0
- icdev/data/docs/dx/llm-routing-guide.md +456 -0
- icdev/data/docs/dx/quickstart.md +192 -0
- icdev/data/docs/dx/sdk-reference.md +356 -0
- icdev/data/docs/dx/unified-mcp-setup.md +525 -0
- icdev/data/docs/features/__init__.py +1 -0
- icdev/data/docs/features/phase-01-gotcha-framework.md +249 -0
- icdev/data/docs/features/phase-02-atlas-build-workflow.md +223 -0
- icdev/data/docs/features/phase-03-tdd-bdd-testing.md +261 -0
- icdev/data/docs/features/phase-04-nist-compliance.md +255 -0
- icdev/data/docs/features/phase-05-security-scanning.md +229 -0
- icdev/data/docs/features/phase-06-infrastructure-deployment.md +288 -0
- icdev/data/docs/features/phase-07-code-review-gates.md +276 -0
- icdev/data/docs/features/phase-08-self-healing.md +223 -0
- icdev/data/docs/features/phase-09-monitoring-observability.md +230 -0
- icdev/data/docs/features/phase-10-dashboard-web-ui.md +218 -0
- icdev/data/docs/features/phase-11-multi-agent-architecture.md +272 -0
- icdev/data/docs/features/phase-12-integration-testing.md +228 -0
- icdev/data/docs/features/phase-13-cicd-integration.md +257 -0
- icdev/data/docs/features/phase-14-secure-by-design-ivv.md +240 -0
- icdev/data/docs/features/phase-15-maintenance-audit.md +192 -0
- icdev/data/docs/features/phase-16-ato-acceleration.md +228 -0
- icdev/data/docs/features/phase-17-multi-framework-compliance.md +223 -0
- icdev/data/docs/features/phase-18-mbse-integration.md +242 -0
- icdev/data/docs/features/phase-19-agentic-generation.md +202 -0
- icdev/data/docs/features/phase-20-fips-security-categorization.md +198 -0
- icdev/data/docs/features/phase-21-saas-multi-tenancy.md +273 -0
- icdev/data/docs/features/phase-22-federated-gotcha-marketplace.md +242 -0
- icdev/data/docs/features/phase-23-universal-compliance-platform.md +238 -0
- icdev/data/docs/features/phase-24-devsecops-pipeline-security.md +198 -0
- icdev/data/docs/features/phase-25-zero-trust-architecture.md +220 -0
- icdev/data/docs/features/phase-26-dod-mosa.md +205 -0
- icdev/data/docs/features/phase-27-cli-capabilities.md +222 -0
- icdev/data/docs/features/phase-28-remote-command-gateway.md +235 -0
- icdev/data/docs/features/phase-29-proactive-monitoring.md +212 -0
- icdev/data/docs/features/phase-30-dashboard-auth.md +215 -0
- icdev/data/docs/features/phase-31-dashboard-ux-low-impact.md +188 -0
- icdev/data/docs/features/phase-32-dashboard-ux-medium-impact.md +223 -0
- icdev/data/docs/features/phase-33-modular-installation.md +218 -0
- icdev/data/docs/features/phase-34-dev-profiles.md +239 -0
- icdev/data/docs/features/phase-35-innovation-engine.md +257 -0
- icdev/data/docs/features/phase-36-evolutionary-intelligence.md +351 -0
- icdev/data/docs/features/phase-37-mitre-atlas-integration.md +485 -0
- icdev/data/docs/features/phase-38-cloud-agnostic-architecture.md +1033 -0
- icdev/data/docs/features/phase-39-observability-operations.md +178 -0
- icdev/data/docs/features/phase-40-nlq-compliance-queries.md +176 -0
- icdev/data/docs/features/phase-41-parallel-cicd.md +169 -0
- icdev/data/docs/features/phase-42-framework-planning.md +177 -0
- icdev/data/docs/features/phase-43-cross-language-translation.md +225 -0
- icdev/data/docs/features/phase-44-innovation-adaptation.md +227 -0
- icdev/data/docs/features/phase-45-owasp-agentic-security.md +239 -0
- icdev/data/docs/features/phase-46-observability-traceability-xai.md +240 -0
- icdev/data/docs/features/phase-47-unified-mcp-gateway.md +257 -0
- icdev/data/docs/features/phase-48-ai-transparency.md +203 -0
- icdev/data/docs/features/phase-49-ai-accountability.md +243 -0
- icdev/data/docs/features/phase-50-ai-governance-intake-chat.md +195 -0
- icdev/data/docs/features/phase-51-unified-chat-dashboard.md +240 -0
- icdev/data/docs/features/phase-52-code-intelligence.md +244 -0
- icdev/data/docs/features/phase-53-fedramp-20x-owasp-asi.md +359 -0
- icdev/data/docs/features/phase-54-slsa-swft-orchestration.md +379 -0
- icdev/data/docs/features/phase-55-a2a-v03-mcp-oauth.md +322 -0
- icdev/data/docs/features/phase-56-evidence-lineage.md +352 -0
- icdev/data/docs/features/phase-57-eu-ai-act-iron-bank.md +319 -0
- icdev/data/docs/features/phase-58-creative-engine.md +370 -0
- icdev/data/docs/features/phase-59-govcon-intelligence.md +535 -0
- icdev/data/docs/features/phase-60-cpmp.md +528 -0
- icdev/data/docs/features/phase-61-orchestration-improvements.md +534 -0
- icdev/data/docs/operations/dashboard-guide.md +354 -0
- icdev/data/docs/operations/deployment-guide.md +556 -0
- icdev/data/docs/operations/saas-admin-guide.md +439 -0
- icdev/data/docs/operations/security-operations-guide.md +733 -0
- icdev/data/docs/runbooks/backup-restore.md +412 -0
- icdev/data/docs/runbooks/troubleshooting.md +499 -0
- icdev/data/features/__init__.py +1 -0
- icdev/data/features/cicd_integration.feature +41 -0
- icdev/data/features/compliance_gates.feature +46 -0
- icdev/data/features/dashboard.feature +72 -0
- icdev/data/features/environment.py +25 -0
- icdev/data/features/project_management.feature +32 -0
- icdev/data/features/requirements_intake.feature +42 -0
- icdev/data/features/saas_platform.feature +53 -0
- icdev/data/features/security_scanning.feature +36 -0
- icdev/data/features/steps/__init__.py +1 -0
- icdev/data/features/steps/cicd_steps.py +465 -0
- icdev/data/features/steps/compliance_steps.py +308 -0
- icdev/data/features/steps/dashboard_steps.py +88 -0
- icdev/data/features/steps/project_steps.py +126 -0
- icdev/data/features/steps/requirements_intake_steps.py +689 -0
- icdev/data/features/steps/saas_platform_steps.py +572 -0
- icdev/data/features/steps/security_steps.py +236 -0
- icdev/data/features/steps/testing_steps.py +226 -0
- icdev/data/features/testing_pipeline.feature +42 -0
- icdev/data/goals/__init__.py +1 -0
- icdev/data/goals/agent_management.md +144 -0
- icdev/data/goals/agentic_generation.md +345 -0
- icdev/data/goals/agentic_threat_model.md +309 -0
- icdev/data/goals/ai_accountability.md +90 -0
- icdev/data/goals/ai_governance_intake.md +132 -0
- icdev/data/goals/ai_transparency.md +76 -0
- icdev/data/goals/atlas_integration.md +405 -0
- icdev/data/goals/ato_acceleration.md +139 -0
- icdev/data/goals/boundary_supply_chain.md +206 -0
- icdev/data/goals/build_app.md +544 -0
- icdev/data/goals/cicd_integration.md +86 -0
- icdev/data/goals/claude_dir_maintenance.md +77 -0
- icdev/data/goals/cli_capabilities.md +340 -0
- icdev/data/goals/cloud_agnostic.md +312 -0
- icdev/data/goals/code_intelligence.md +197 -0
- icdev/data/goals/code_review.md +94 -0
- icdev/data/goals/compliance_workflow.md +858 -0
- icdev/data/goals/continuous_harmonization.md +140 -0
- icdev/data/goals/cross_language_translation.md +171 -0
- icdev/data/goals/dashboard.md +142 -0
- icdev/data/goals/deploy_workflow.md +390 -0
- icdev/data/goals/devsecops_workflow.md +408 -0
- icdev/data/goals/evolutionary_intelligence.md +305 -0
- icdev/data/goals/external_integration.md +113 -0
- icdev/data/goals/framework_planning.md +63 -0
- icdev/data/goals/init_project.md +235 -0
- icdev/data/goals/innovation_engine.md +199 -0
- icdev/data/goals/integration_testing.md +189 -0
- icdev/data/goals/maintenance_audit.md +196 -0
- icdev/data/goals/manifest.md +56 -0
- icdev/data/goals/mbse_integration.md +504 -0
- icdev/data/goals/modernization_workflow.md +618 -0
- icdev/data/goals/monitoring.md +126 -0
- icdev/data/goals/mosa_workflow.md +463 -0
- icdev/data/goals/multi_agent_orchestration.md +68 -0
- icdev/data/goals/nlq_compliance.md +63 -0
- icdev/data/goals/observability.md +64 -0
- icdev/data/goals/observability_traceability_xai.md +154 -0
- icdev/data/goals/owasp_agentic_security.md +395 -0
- icdev/data/goals/parallel_cicd.md +61 -0
- icdev/data/goals/requirements_intake.md +213 -0
- icdev/data/goals/sbd_ivv_workflow.md +195 -0
- icdev/data/goals/security_categorization.md +133 -0
- icdev/data/goals/security_scan.md +381 -0
- icdev/data/goals/self_healing.md +120 -0
- icdev/data/goals/simulation_engine.md +111 -0
- icdev/data/goals/tdd_workflow.md +403 -0
- icdev/data/goals/zero_trust_architecture.md +403 -0
- icdev/data/hardprompts/__init__.py +1 -0
- icdev/data/hardprompts/agent/__init__.py +1 -0
- icdev/data/hardprompts/agent/agentic_architect.md +100 -0
- icdev/data/hardprompts/agent/debate_prompt.md +32 -0
- icdev/data/hardprompts/agent/fitness_evaluation.md +48 -0
- icdev/data/hardprompts/agent/governance_review.md +214 -0
- icdev/data/hardprompts/agent/reviewer_prompt.md +34 -0
- icdev/data/hardprompts/agent/skill_design.md +172 -0
- icdev/data/hardprompts/agent/task_decomposition.md +275 -0
- icdev/data/hardprompts/agent/veto_check_prompt.md +33 -0
- icdev/data/hardprompts/architect/__init__.py +1 -0
- icdev/data/hardprompts/architect/api_design.md +283 -0
- icdev/data/hardprompts/architect/data_model.md +277 -0
- icdev/data/hardprompts/architect/system_design.md +180 -0
- icdev/data/hardprompts/builder/__init__.py +1 -0
- icdev/data/hardprompts/builder/code_generation.md +59 -0
- icdev/data/hardprompts/builder/refactor.md +58 -0
- icdev/data/hardprompts/builder/scaffold_project.md +69 -0
- icdev/data/hardprompts/builder/test_generation.md +87 -0
- icdev/data/hardprompts/ci/__init__.py +1 -0
- icdev/data/hardprompts/ci/worktree_setup.md +35 -0
- icdev/data/hardprompts/compliance/__init__.py +1 -0
- icdev/data/hardprompts/compliance/cmmc_assessment.md +63 -0
- icdev/data/hardprompts/compliance/cssp_assessment.md +75 -0
- icdev/data/hardprompts/compliance/cui_marking.md +86 -0
- icdev/data/hardprompts/compliance/fedramp_assessment.md +55 -0
- icdev/data/hardprompts/compliance/ivv_assessment.md +96 -0
- icdev/data/hardprompts/compliance/poam_generation.md +57 -0
- icdev/data/hardprompts/compliance/sbd_assessment.md +101 -0
- icdev/data/hardprompts/compliance/security_categorization.md +74 -0
- icdev/data/hardprompts/compliance/ssp_generation.md +56 -0
- icdev/data/hardprompts/compliance/stig_evaluation.md +63 -0
- icdev/data/hardprompts/dashboard/__init__.py +1 -0
- icdev/data/hardprompts/dashboard/nlq_system_prompt.md +26 -0
- icdev/data/hardprompts/infra/__init__.py +1 -0
- icdev/data/hardprompts/infra/k8s_manifests.md +118 -0
- icdev/data/hardprompts/infra/pipeline_generation.md +160 -0
- icdev/data/hardprompts/infra/terraform_generation.md +92 -0
- icdev/data/hardprompts/integration/__init__.py +1 -0
- icdev/data/hardprompts/integration/approval_review.md +17 -0
- icdev/data/hardprompts/integration/jira_mapping.md +25 -0
- icdev/data/hardprompts/integration/servicenow_mapping.md +14 -0
- icdev/data/hardprompts/knowledge/__init__.py +1 -0
- icdev/data/hardprompts/knowledge/pattern_detection.md +73 -0
- icdev/data/hardprompts/knowledge/recommendation_engine.md +90 -0
- icdev/data/hardprompts/knowledge/root_cause_analysis.md +91 -0
- icdev/data/hardprompts/maintenance/__init__.py +1 -0
- icdev/data/hardprompts/maintenance/maintenance_assessment.md +82 -0
- icdev/data/hardprompts/mbse/__init__.py +1 -0
- icdev/data/hardprompts/mbse/digital_thread.md +67 -0
- icdev/data/hardprompts/mbse/model_import.md +62 -0
- icdev/data/hardprompts/mbse/model_to_code.md +65 -0
- icdev/data/hardprompts/modernization/__init__.py +1 -0
- icdev/data/hardprompts/modernization/legacy_analysis.md +93 -0
- icdev/data/hardprompts/modernization/migration_planning.md +150 -0
- icdev/data/hardprompts/modernization/seven_r_assessment.md +107 -0
- icdev/data/hardprompts/requirements/__init__.py +1 -0
- icdev/data/hardprompts/requirements/bdd_generation.md +35 -0
- icdev/data/hardprompts/requirements/clarification_prioritization.md +29 -0
- icdev/data/hardprompts/requirements/decomposition.md +60 -0
- icdev/data/hardprompts/requirements/document_extraction.md +45 -0
- icdev/data/hardprompts/requirements/gap_detection.md +70 -0
- icdev/data/hardprompts/requirements/intake_conversation.md +101 -0
- icdev/data/hardprompts/requirements/readiness_assessment.md +39 -0
- icdev/data/hardprompts/requirements/spec_quality.md +33 -0
- icdev/data/hardprompts/requirements/traceability_analysis.md +23 -0
- icdev/data/hardprompts/security/__init__.py +1 -0
- icdev/data/hardprompts/security/endpoint_security.md +78 -0
- icdev/data/hardprompts/security/threat_model.md +70 -0
- icdev/data/hardprompts/security/vulnerability_assessment.md +81 -0
- icdev/data/hardprompts/simulation/__init__.py +1 -0
- icdev/data/hardprompts/simulation/architecture_impact.md +27 -0
- icdev/data/hardprompts/simulation/coa_alternative.md +27 -0
- icdev/data/hardprompts/simulation/coa_generation.md +25 -0
- icdev/data/hardprompts/simulation/compliance_impact.md +28 -0
- icdev/data/hardprompts/simulation/cost_estimation.md +33 -0
- icdev/data/hardprompts/simulation/risk_assessment.md +28 -0
- icdev/data/hardprompts/translation/code_translation.md +68 -0
- icdev/data/hardprompts/translation/dependency_suggestion.md +44 -0
- icdev/data/hardprompts/translation/test_translation.md +64 -0
- icdev/data/hardprompts/translation/translation_repair.md +59 -0
- icdev/py.typed +0 -0
- icdev/tools/__init__.py +1 -0
- icdev/tools/_gen_formatter.py +12 -0
- icdev/tools/a2a/__init__.py +1 -0
- icdev/tools/a2a/agent_cards/architect.json +43 -0
- icdev/tools/a2a/agent_cards/builder.json +50 -0
- icdev/tools/a2a/agent_cards/compliance.json +57 -0
- icdev/tools/a2a/agent_cards/devsecops.json +71 -0
- icdev/tools/a2a/agent_cards/infra.json +57 -0
- icdev/tools/a2a/agent_cards/integration.json +57 -0
- icdev/tools/a2a/agent_cards/knowledge.json +43 -0
- icdev/tools/a2a/agent_cards/mbse.json +57 -0
- icdev/tools/a2a/agent_cards/modernization.json +50 -0
- icdev/tools/a2a/agent_cards/monitor.json +43 -0
- icdev/tools/a2a/agent_cards/orchestrator.json +36 -0
- icdev/tools/a2a/agent_cards/requirements_analyst.json +64 -0
- icdev/tools/a2a/agent_cards/security.json +50 -0
- icdev/tools/a2a/agent_cards/simulation.json +57 -0
- icdev/tools/a2a/agent_cards/supply_chain.json +50 -0
- icdev/tools/a2a/agent_client.py +349 -0
- icdev/tools/a2a/agent_registry.py +412 -0
- icdev/tools/a2a/agent_server.py +579 -0
- icdev/tools/a2a/task.py +200 -0
- icdev/tools/agent/__init__.py +2 -0
- icdev/tools/agent/a2a_agent_card_generator.py +285 -0
- icdev/tools/agent/a2a_discovery_server.py +250 -0
- icdev/tools/agent/agent_executor.py +529 -0
- icdev/tools/agent/agent_memory.py +557 -0
- icdev/tools/agent/agent_models.py +51 -0
- icdev/tools/agent/atlas_critique.py +908 -0
- icdev/tools/agent/authority.py +443 -0
- icdev/tools/agent/bedrock_client.py +1075 -0
- icdev/tools/agent/collaboration.py +871 -0
- icdev/tools/agent/dispatcher_mode.py +665 -0
- icdev/tools/agent/mailbox.py +575 -0
- icdev/tools/agent/prompt_chain_executor.py +1064 -0
- icdev/tools/agent/session_purpose.py +350 -0
- icdev/tools/agent/skill_router.py +638 -0
- icdev/tools/agent/skill_selector.py +486 -0
- icdev/tools/agent/team_orchestrator.py +1108 -0
- icdev/tools/agent/token_tracker.py +290 -0
- icdev/tools/analysis/__init__.py +1 -0
- icdev/tools/analysis/code_analyzer.py +780 -0
- icdev/tools/analysis/runtime_feedback.py +389 -0
- icdev/tools/audit/__init__.py +1 -0
- icdev/tools/audit/audit_logger.py +196 -0
- icdev/tools/audit/audit_query.py +157 -0
- icdev/tools/audit/decision_recorder.py +72 -0
- icdev/tools/builder/__init__.py +1 -0
- icdev/tools/builder/agentic_fitness.py +534 -0
- icdev/tools/builder/agentic_test_templates/test_a2a_callback.py +117 -0
- icdev/tools/builder/agentic_test_templates/test_a2a_lifecycle.feature +52 -0
- icdev/tools/builder/agentic_test_templates/test_agent_card.feature +37 -0
- icdev/tools/builder/agentic_test_templates/test_agent_health.py +128 -0
- icdev/tools/builder/agentic_test_templates/test_memory_system.feature +50 -0
- icdev/tools/builder/agentic_test_templates/test_skill_execution.feature +40 -0
- icdev/tools/builder/app_blueprint.py +1583 -0
- icdev/tools/builder/child_app_generator.py +2852 -0
- icdev/tools/builder/claude_md_generator.py +1734 -0
- icdev/tools/builder/code_generator.py +3703 -0
- icdev/tools/builder/db_init_generator.py +1709 -0
- icdev/tools/builder/dev_profile_manager.py +954 -0
- icdev/tools/builder/formatter.py +768 -0
- icdev/tools/builder/goal_adapter.py +592 -0
- icdev/tools/builder/gotcha_validator.py +812 -0
- icdev/tools/builder/language_support.py +441 -0
- icdev/tools/builder/linter.py +976 -0
- icdev/tools/builder/profile_detector.py +657 -0
- icdev/tools/builder/profile_md_generator.py +723 -0
- icdev/tools/builder/scaffolder.py +1590 -0
- icdev/tools/builder/scaffolder_extended.py +1771 -0
- icdev/tools/builder/test_writer.py +950 -0
- icdev/tools/ci/__init__.py +2 -0
- icdev/tools/ci/connectors/__init__.py +2 -0
- icdev/tools/ci/connectors/base_connector.py +80 -0
- icdev/tools/ci/connectors/connector_registry.py +188 -0
- icdev/tools/ci/connectors/mattermost_connector.py +159 -0
- icdev/tools/ci/connectors/slack_connector.py +197 -0
- icdev/tools/ci/core/__init__.py +2 -0
- icdev/tools/ci/core/air_gap_detector.py +115 -0
- icdev/tools/ci/core/comment_handler.py +192 -0
- icdev/tools/ci/core/conversation_manager.py +479 -0
- icdev/tools/ci/core/event_envelope.py +500 -0
- icdev/tools/ci/core/event_router.py +443 -0
- icdev/tools/ci/core/failure_parser.py +397 -0
- icdev/tools/ci/core/recovery_engine.py +527 -0
- icdev/tools/ci/modules/__init__.py +2 -0
- icdev/tools/ci/modules/agent.py +271 -0
- icdev/tools/ci/modules/git_ops.py +175 -0
- icdev/tools/ci/modules/state.py +117 -0
- icdev/tools/ci/modules/vcs.py +303 -0
- icdev/tools/ci/modules/workflow_ops.py +295 -0
- icdev/tools/ci/modules/worktree.py +340 -0
- icdev/tools/ci/pipeline_config_generator.py +558 -0
- icdev/tools/ci/triggers/__init__.py +2 -0
- icdev/tools/ci/triggers/gitlab_task_monitor.py +330 -0
- icdev/tools/ci/triggers/poll_trigger.py +237 -0
- icdev/tools/ci/triggers/webhook_server.py +356 -0
- icdev/tools/ci/workflows/__init__.py +2 -0
- icdev/tools/ci/workflows/icdev_build.py +140 -0
- icdev/tools/ci/workflows/icdev_comply.py +284 -0
- icdev/tools/ci/workflows/icdev_document.py +152 -0
- icdev/tools/ci/workflows/icdev_e2e.py +188 -0
- icdev/tools/ci/workflows/icdev_patch.py +186 -0
- icdev/tools/ci/workflows/icdev_plan.py +202 -0
- icdev/tools/ci/workflows/icdev_plan_build.py +41 -0
- icdev/tools/ci/workflows/icdev_plan_build_test.py +46 -0
- icdev/tools/ci/workflows/icdev_plan_build_test_review.py +47 -0
- icdev/tools/ci/workflows/icdev_review.py +126 -0
- icdev/tools/ci/workflows/icdev_sdlc.py +261 -0
- icdev/tools/ci/workflows/icdev_test.py +240 -0
- icdev/tools/cli/__init__.py +1 -0
- icdev/tools/cli/output_formatter.py +756 -0
- icdev/tools/cli_formatter.py +42 -0
- icdev/tools/cloud/__init__.py +11 -0
- icdev/tools/cloud/cloud_mode_manager.py +364 -0
- icdev/tools/cloud/csp_changelog.py +383 -0
- icdev/tools/cloud/csp_health_checker.py +268 -0
- icdev/tools/cloud/csp_monitor.py +951 -0
- icdev/tools/cloud/iam_provider.py +593 -0
- icdev/tools/cloud/kms_provider.py +346 -0
- icdev/tools/cloud/monitoring_provider.py +628 -0
- icdev/tools/cloud/provider_factory.py +376 -0
- icdev/tools/cloud/region_validator.py +345 -0
- icdev/tools/cloud/registry_provider.py +563 -0
- icdev/tools/cloud/secrets_provider.py +486 -0
- icdev/tools/cloud/storage_provider.py +446 -0
- icdev/tools/compat/__init__.py +21 -0
- icdev/tools/compat/cli_harmonizer.py +251 -0
- icdev/tools/compat/datetime_utils.py +18 -0
- icdev/tools/compat/db_utils.py +160 -0
- icdev/tools/compat/platform_utils.py +123 -0
- icdev/tools/compliance/__init__.py +1 -0
- icdev/tools/compliance/accountability_manager.py +397 -0
- icdev/tools/compliance/ai_accountability_audit.py +294 -0
- icdev/tools/compliance/ai_impact_assessor.py +273 -0
- icdev/tools/compliance/ai_incident_response.py +301 -0
- icdev/tools/compliance/ai_inventory_manager.py +239 -0
- icdev/tools/compliance/ai_reassessment_scheduler.py +256 -0
- icdev/tools/compliance/ai_transparency_audit.py +248 -0
- icdev/tools/compliance/atlas_assessor.py +278 -0
- icdev/tools/compliance/atlas_report_generator.py +1211 -0
- icdev/tools/compliance/base_assessor.py +597 -0
- icdev/tools/compliance/cato_monitor.py +1385 -0
- icdev/tools/compliance/cato_scheduler.py +699 -0
- icdev/tools/compliance/cjis_assessor.py +76 -0
- icdev/tools/compliance/classification_manager.py +1353 -0
- icdev/tools/compliance/cmmc_assessor.py +1491 -0
- icdev/tools/compliance/cmmc_report_generator.py +1100 -0
- icdev/tools/compliance/compliance_detector.py +463 -0
- icdev/tools/compliance/compliance_exporter.py +427 -0
- icdev/tools/compliance/compliance_status.py +825 -0
- icdev/tools/compliance/control_mapper.py +505 -0
- icdev/tools/compliance/crosswalk_engine.py +1203 -0
- icdev/tools/compliance/cssp_assessor.py +1045 -0
- icdev/tools/compliance/cssp_evidence_collector.py +729 -0
- icdev/tools/compliance/cssp_report_generator.py +1116 -0
- icdev/tools/compliance/cui_marker.py +388 -0
- icdev/tools/compliance/diagram_validator.py +600 -0
- icdev/tools/compliance/emass/__init__.py +2 -0
- icdev/tools/compliance/emass/emass_client.py +840 -0
- icdev/tools/compliance/emass/emass_export.py +777 -0
- icdev/tools/compliance/emass/emass_sync.py +826 -0
- icdev/tools/compliance/eu_ai_act_classifier.py +194 -0
- icdev/tools/compliance/evidence_collector.py +468 -0
- icdev/tools/compliance/fairness_assessor.py +316 -0
- icdev/tools/compliance/fedramp_assessor.py +1808 -0
- icdev/tools/compliance/fedramp_authorization_packager.py +137 -0
- icdev/tools/compliance/fedramp_ksi_generator.py +355 -0
- icdev/tools/compliance/fedramp_report_generator.py +1128 -0
- icdev/tools/compliance/fips199_categorizer.py +881 -0
- icdev/tools/compliance/fips200_validator.py +315 -0
- icdev/tools/compliance/gao_ai_assessor.py +231 -0
- icdev/tools/compliance/gao_evidence_builder.py +308 -0
- icdev/tools/compliance/hipaa_assessor.py +78 -0
- icdev/tools/compliance/hitrust_assessor.py +49 -0
- icdev/tools/compliance/incident_response_plan.py +718 -0
- icdev/tools/compliance/iso27001_assessor.py +92 -0
- icdev/tools/compliance/iso42001_assessor.py +114 -0
- icdev/tools/compliance/ivv_assessor.py +2327 -0
- icdev/tools/compliance/ivv_report_generator.py +1662 -0
- icdev/tools/compliance/model_card_generator.py +297 -0
- icdev/tools/compliance/mosa_assessor.py +117 -0
- icdev/tools/compliance/multi_regime_assessor.py +451 -0
- icdev/tools/compliance/narrative_generator.py +1013 -0
- icdev/tools/compliance/nist_800_207_assessor.py +191 -0
- icdev/tools/compliance/nist_ai_600_1_assessor.py +188 -0
- icdev/tools/compliance/nist_ai_rmf_assessor.py +110 -0
- icdev/tools/compliance/nist_lookup.py +245 -0
- icdev/tools/compliance/omb_m25_21_assessor.py +228 -0
- icdev/tools/compliance/omb_m26_04_assessor.py +188 -0
- icdev/tools/compliance/oscal_catalog_adapter.py +395 -0
- icdev/tools/compliance/oscal_generator.py +2170 -0
- icdev/tools/compliance/oscal_tools.py +1182 -0
- icdev/tools/compliance/owasp_agentic_assessor.py +226 -0
- icdev/tools/compliance/owasp_asi_assessor.py +200 -0
- icdev/tools/compliance/owasp_llm_assessor.py +244 -0
- icdev/tools/compliance/pci_dss_assessor.py +80 -0
- icdev/tools/compliance/pi_compliance_tracker.py +1461 -0
- icdev/tools/compliance/poam_generator.py +405 -0
- icdev/tools/compliance/resolve_marking.py +283 -0
- icdev/tools/compliance/sbd_assessor.py +2068 -0
- icdev/tools/compliance/sbd_report_generator.py +1236 -0
- icdev/tools/compliance/sbom_generator.py +1008 -0
- icdev/tools/compliance/siem_config_generator.py +674 -0
- icdev/tools/compliance/slsa_attestation_generator.py +490 -0
- icdev/tools/compliance/soc2_assessor.py +77 -0
- icdev/tools/compliance/ssp_generator.py +573 -0
- icdev/tools/compliance/stig_checker.py +727 -0
- icdev/tools/compliance/swft_evidence_bundler.py +337 -0
- icdev/tools/compliance/system_card_generator.py +309 -0
- icdev/tools/compliance/traceability_matrix.py +1281 -0
- icdev/tools/compliance/universal_classification_manager.py +1172 -0
- icdev/tools/compliance/xacta/__init__.py +2 -0
- icdev/tools/compliance/xacta/xacta_client.py +449 -0
- icdev/tools/compliance/xacta/xacta_export.py +557 -0
- icdev/tools/compliance/xacta/xacta_sync.py +333 -0
- icdev/tools/compliance/xai_assessor.py +231 -0
- icdev/tools/dashboard/__init__.py +1 -0
- icdev/tools/dashboard/api/__init__.py +1 -0
- icdev/tools/dashboard/api/_pipeline_state.py +17 -0
- icdev/tools/dashboard/api/activity.py +206 -0
- icdev/tools/dashboard/api/admin.py +176 -0
- icdev/tools/dashboard/api/agents.py +53 -0
- icdev/tools/dashboard/api/ai_accountability.py +163 -0
- icdev/tools/dashboard/api/ai_transparency.py +198 -0
- icdev/tools/dashboard/api/audit.py +58 -0
- icdev/tools/dashboard/api/batch.py +666 -0
- icdev/tools/dashboard/api/chat.py +241 -0
- icdev/tools/dashboard/api/cicd.py +219 -0
- icdev/tools/dashboard/api/code_quality.py +223 -0
- icdev/tools/dashboard/api/compliance.py +171 -0
- icdev/tools/dashboard/api/cpmp.py +915 -0
- icdev/tools/dashboard/api/diagrams.py +65 -0
- icdev/tools/dashboard/api/events.py +250 -0
- icdev/tools/dashboard/api/evidence.py +99 -0
- icdev/tools/dashboard/api/fedramp_20x.py +77 -0
- icdev/tools/dashboard/api/govcon.py +1095 -0
- icdev/tools/dashboard/api/intake.py +1171 -0
- icdev/tools/dashboard/api/lineage.py +163 -0
- icdev/tools/dashboard/api/metrics.py +155 -0
- icdev/tools/dashboard/api/nlq.py +72 -0
- icdev/tools/dashboard/api/orchestration.py +472 -0
- icdev/tools/dashboard/api/oscal.py +183 -0
- icdev/tools/dashboard/api/prod_audit.py +183 -0
- icdev/tools/dashboard/api/projects.py +191 -0
- icdev/tools/dashboard/api/proposals.py +1084 -0
- icdev/tools/dashboard/api/traces.py +363 -0
- icdev/tools/dashboard/api/usage.py +234 -0
- icdev/tools/dashboard/app.py +1986 -0
- icdev/tools/dashboard/auth.py +500 -0
- icdev/tools/dashboard/byok.py +245 -0
- icdev/tools/dashboard/chat_manager.py +675 -0
- icdev/tools/dashboard/config.py +116 -0
- icdev/tools/dashboard/diagram_definitions.py +642 -0
- icdev/tools/dashboard/nlq_processor.py +323 -0
- icdev/tools/dashboard/phase_loader.py +136 -0
- icdev/tools/dashboard/sse_manager.py +89 -0
- icdev/tools/dashboard/state_tracker.py +267 -0
- icdev/tools/dashboard/static/css/style.css +706 -0
- icdev/tools/dashboard/static/css/ux.css +2047 -0
- icdev/tools/dashboard/static/js/activity.js +322 -0
- icdev/tools/dashboard/static/js/api.js +161 -0
- icdev/tools/dashboard/static/js/batch.js +814 -0
- icdev/tools/dashboard/static/js/charts.js +618 -0
- icdev/tools/dashboard/static/js/chat.js +1514 -0
- icdev/tools/dashboard/static/js/kanban.js +113 -0
- icdev/tools/dashboard/static/js/live.js +569 -0
- icdev/tools/dashboard/static/js/mermaid-icdev.js +332 -0
- icdev/tools/dashboard/static/js/proposals.js +588 -0
- icdev/tools/dashboard/static/js/shortcuts.js +544 -0
- icdev/tools/dashboard/static/js/tables.js +652 -0
- icdev/tools/dashboard/static/js/tour.js +524 -0
- icdev/tools/dashboard/static/js/ux.js +942 -0
- icdev/tools/dashboard/templates/404.html +10 -0
- icdev/tools/dashboard/templates/activity.html +80 -0
- icdev/tools/dashboard/templates/admin/users.html +144 -0
- icdev/tools/dashboard/templates/ai_accountability.html +235 -0
- icdev/tools/dashboard/templates/ai_transparency.html +263 -0
- icdev/tools/dashboard/templates/base.html +104 -0
- icdev/tools/dashboard/templates/batch.html +23 -0
- icdev/tools/dashboard/templates/chat.html +332 -0
- icdev/tools/dashboard/templates/children.html +149 -0
- icdev/tools/dashboard/templates/cicd.html +253 -0
- icdev/tools/dashboard/templates/code_quality.html +214 -0
- icdev/tools/dashboard/templates/cpmp/cor_detail.html +220 -0
- icdev/tools/dashboard/templates/cpmp/cor_portal.html +91 -0
- icdev/tools/dashboard/templates/cpmp/deliverable_detail.html +197 -0
- icdev/tools/dashboard/templates/cpmp/detail.html +578 -0
- icdev/tools/dashboard/templates/cpmp/portfolio.html +202 -0
- icdev/tools/dashboard/templates/dev_profiles.html +304 -0
- icdev/tools/dashboard/templates/diagrams.html +224 -0
- icdev/tools/dashboard/templates/events/timeline.html +232 -0
- icdev/tools/dashboard/templates/evidence.html +134 -0
- icdev/tools/dashboard/templates/fedramp_20x.html +207 -0
- icdev/tools/dashboard/templates/gateway.html +244 -0
- icdev/tools/dashboard/templates/govcon/capabilities.html +135 -0
- icdev/tools/dashboard/templates/govcon/pipeline.html +214 -0
- icdev/tools/dashboard/templates/govcon/requirements.html +120 -0
- icdev/tools/dashboard/templates/index.html +254 -0
- icdev/tools/dashboard/templates/lineage.html +141 -0
- icdev/tools/dashboard/templates/login.html +51 -0
- icdev/tools/dashboard/templates/monitoring/overview.html +193 -0
- icdev/tools/dashboard/templates/orchestration/dashboard.html +545 -0
- icdev/tools/dashboard/templates/oscal.html +263 -0
- icdev/tools/dashboard/templates/phases.html +150 -0
- icdev/tools/dashboard/templates/prod_audit.html +280 -0
- icdev/tools/dashboard/templates/profile.html +183 -0
- icdev/tools/dashboard/templates/projects/detail.html +583 -0
- icdev/tools/dashboard/templates/projects/list.html +47 -0
- icdev/tools/dashboard/templates/proposals/detail.html +1253 -0
- icdev/tools/dashboard/templates/proposals/list.html +179 -0
- icdev/tools/dashboard/templates/proposals/section_detail.html +193 -0
- icdev/tools/dashboard/templates/provenance.html +181 -0
- icdev/tools/dashboard/templates/query/nlq.html +234 -0
- icdev/tools/dashboard/templates/quick_paths.html +69 -0
- icdev/tools/dashboard/templates/traces.html +155 -0
- icdev/tools/dashboard/templates/translation_detail.html +199 -0
- icdev/tools/dashboard/templates/translations.html +162 -0
- icdev/tools/dashboard/templates/usage.html +225 -0
- icdev/tools/dashboard/templates/wizard.html +539 -0
- icdev/tools/dashboard/templates/xai.html +208 -0
- icdev/tools/dashboard/ux_helpers.py +962 -0
- icdev/tools/dashboard/websocket.py +81 -0
- icdev/tools/db/__init__.py +1 -0
- icdev/tools/db/backup.py +312 -0
- icdev/tools/db/backup_manager.py +832 -0
- icdev/tools/db/init_icdev_db.py +5900 -0
- icdev/tools/db/migrate.py +178 -0
- icdev/tools/db/migration_runner.py +549 -0
- icdev/tools/db/migrations/001_baseline/meta.json +9 -0
- icdev/tools/db/migrations/001_baseline/up.py +68 -0
- icdev/tools/db/migrations/002_memory_enhancements/down.sql +8 -0
- icdev/tools/db/migrations/002_memory_enhancements/meta.json +9 -0
- icdev/tools/db/migrations/002_memory_enhancements/up.py +118 -0
- icdev/tools/db/migrations/003_dev_profiles/meta.json +8 -0
- icdev/tools/db/migrations/003_dev_profiles/up.py +93 -0
- icdev/tools/db/migrations/004_innovation_engine/down.py +19 -0
- icdev/tools/db/migrations/004_innovation_engine/up.py +227 -0
- icdev/tools/db/migrations/005_phase_37_ai_security/down.py +19 -0
- icdev/tools/db/migrations/005_phase_37_ai_security/up.py +258 -0
- icdev/tools/db/migrations/006_phase_36_evolution/down.py +21 -0
- icdev/tools/db/migrations/006_phase_36_evolution/up.py +323 -0
- icdev/tools/db/migrations/007_phase_38_cloud/down.py +14 -0
- icdev/tools/db/migrations/007_phase_38_cloud/up.py +110 -0
- icdev/tools/db/migrations/008_phase36_37_integration/up.py +55 -0
- icdev/tools/db/migrations/__init__.py +2 -0
- icdev/tools/devsecops/__init__.py +2 -0
- icdev/tools/devsecops/attestation_manager.py +458 -0
- icdev/tools/devsecops/network_segmentation_generator.py +614 -0
- icdev/tools/devsecops/pdp_config_generator.py +1256 -0
- icdev/tools/devsecops/pipeline_security_generator.py +484 -0
- icdev/tools/devsecops/policy_generator.py +653 -0
- icdev/tools/devsecops/profile_manager.py +388 -0
- icdev/tools/devsecops/service_mesh_generator.py +1073 -0
- icdev/tools/devsecops/zta_maturity_scorer.py +368 -0
- icdev/tools/devsecops/zta_terraform_generator.py +1303 -0
- icdev/tools/dx/__init__.py +3 -0
- icdev/tools/dx/companion.py +266 -0
- icdev/tools/dx/instruction_generator.py +753 -0
- icdev/tools/dx/mcp_config_generator.py +282 -0
- icdev/tools/dx/skill_translator.py +425 -0
- icdev/tools/dx/tool_detector.py +144 -0
- icdev/tools/extensions/__init__.py +21 -0
- icdev/tools/extensions/builtins/010_ai_governance_chat.py +277 -0
- icdev/tools/extensions/builtins/__init__.py +2 -0
- icdev/tools/extensions/extension_manager.py +455 -0
- icdev/tools/infra/__init__.py +1 -0
- icdev/tools/infra/ansible_generator.py +869 -0
- icdev/tools/infra/dockerfile_generator.py +361 -0
- icdev/tools/infra/infra_status.py +393 -0
- icdev/tools/infra/ironbank_metadata_generator.py +411 -0
- icdev/tools/infra/k8s_generator.py +1002 -0
- icdev/tools/infra/pipeline_generator.py +832 -0
- icdev/tools/infra/rollback.py +400 -0
- icdev/tools/infra/terraform_generator.py +1142 -0
- icdev/tools/infra/terraform_generator_azure.py +1254 -0
- icdev/tools/infra/terraform_generator_gcp.py +953 -0
- icdev/tools/infra/terraform_generator_ibm.py +360 -0
- icdev/tools/infra/terraform_generator_oci.py +919 -0
- icdev/tools/infra/terraform_generator_onprem.py +319 -0
- icdev/tools/innovation/__init__.py +8 -0
- icdev/tools/innovation/competitive_intel.py +492 -0
- icdev/tools/innovation/innovation_manager.py +681 -0
- icdev/tools/innovation/introspective_analyzer.py +774 -0
- icdev/tools/innovation/register_external_patterns.py +440 -0
- icdev/tools/innovation/signal_ranker.py +1038 -0
- icdev/tools/innovation/solution_generator.py +697 -0
- icdev/tools/innovation/standards_monitor.py +466 -0
- icdev/tools/innovation/trend_detector.py +1046 -0
- icdev/tools/innovation/triage_engine.py +1149 -0
- icdev/tools/innovation/web_scanner.py +894 -0
- icdev/tools/installer/__init__.py +1 -0
- icdev/tools/installer/compliance_configurator.py +637 -0
- icdev/tools/installer/installer.py +1711 -0
- icdev/tools/installer/module_registry.py +805 -0
- icdev/tools/installer/platform_setup.py +961 -0
- icdev/tools/integration/__init__.py +2 -0
- icdev/tools/integration/approval_manager.py +561 -0
- icdev/tools/integration/doors_exporter.py +627 -0
- icdev/tools/integration/gitlab_connector.py +784 -0
- icdev/tools/integration/jira_connector.py +774 -0
- icdev/tools/integration/servicenow_connector.py +693 -0
- icdev/tools/knowledge/__init__.py +1 -0
- icdev/tools/knowledge/knowledge_ingest.py +293 -0
- icdev/tools/knowledge/pattern_detector.py +693 -0
- icdev/tools/knowledge/recommendation_engine.py +461 -0
- icdev/tools/knowledge/self_heal_analyzer.py +504 -0
- icdev/tools/llm/__init__.py +72 -0
- icdev/tools/llm/anthropic_provider.py +170 -0
- icdev/tools/llm/azure_openai_provider.py +338 -0
- icdev/tools/llm/bedrock_provider.py +315 -0
- icdev/tools/llm/embedding_provider.py +438 -0
- icdev/tools/llm/gemini_provider.py +381 -0
- icdev/tools/llm/ibm_watsonx_provider.py +232 -0
- icdev/tools/llm/oci_genai_provider.py +462 -0
- icdev/tools/llm/ollama_provider.py +340 -0
- icdev/tools/llm/openai_provider.py +225 -0
- icdev/tools/llm/provider.py +355 -0
- icdev/tools/llm/provider_sdk.py +175 -0
- icdev/tools/llm/router.py +780 -0
- icdev/tools/llm/vertex_ai_provider.py +374 -0
- icdev/tools/maintenance/__init__.py +2 -0
- icdev/tools/maintenance/dependency_scanner.py +1030 -0
- icdev/tools/maintenance/maintenance_auditor.py +815 -0
- icdev/tools/maintenance/remediation_engine.py +966 -0
- icdev/tools/maintenance/vulnerability_checker.py +987 -0
- icdev/tools/mbse/__init__.py +3 -0
- icdev/tools/mbse/des_assessor.py +1186 -0
- icdev/tools/mbse/des_report_generator.py +800 -0
- icdev/tools/mbse/diagram_extractor.py +811 -0
- icdev/tools/mbse/digital_thread.py +1665 -0
- icdev/tools/mbse/model_code_generator.py +1122 -0
- icdev/tools/mbse/model_control_mapper.py +420 -0
- icdev/tools/mbse/pi_model_tracker.py +1093 -0
- icdev/tools/mbse/reqif_parser.py +1483 -0
- icdev/tools/mbse/sync_engine.py +1805 -0
- icdev/tools/mbse/xmi_parser.py +1573 -0
- icdev/tools/mcp/__init__.py +1 -0
- icdev/tools/mcp/base_server.py +535 -0
- icdev/tools/mcp/builder_server.py +725 -0
- icdev/tools/mcp/compliance_server.py +1407 -0
- icdev/tools/mcp/context_indexer.py +199 -0
- icdev/tools/mcp/context_server.py +305 -0
- icdev/tools/mcp/core_server.py +679 -0
- icdev/tools/mcp/devsecops_server.py +432 -0
- icdev/tools/mcp/gap_handlers.py +1079 -0
- icdev/tools/mcp/gateway_server.py +339 -0
- icdev/tools/mcp/generate_registry.py +623 -0
- icdev/tools/mcp/infra_server.py +264 -0
- icdev/tools/mcp/innovation_server.py +316 -0
- icdev/tools/mcp/integration_server.py +527 -0
- icdev/tools/mcp/knowledge_server.py +429 -0
- icdev/tools/mcp/maintenance_server.py +248 -0
- icdev/tools/mcp/marketplace_server.py +499 -0
- icdev/tools/mcp/mbse_server.py +398 -0
- icdev/tools/mcp/modernization_server.py +496 -0
- icdev/tools/mcp/observability_server.py +354 -0
- icdev/tools/mcp/requirements_server.py +415 -0
- icdev/tools/mcp/simulation_server.py +468 -0
- icdev/tools/mcp/standalone/__init__.py +2 -0
- icdev/tools/mcp/standalone/builder.py +59 -0
- icdev/tools/mcp/standalone/compliance.py +59 -0
- icdev/tools/mcp/standalone/core.py +59 -0
- icdev/tools/mcp/standalone/knowledge.py +59 -0
- icdev/tools/mcp/standalone/maintenance.py +59 -0
- icdev/tools/mcp/supply_chain_server.py +476 -0
- icdev/tools/mcp/tool_registry.py +2008 -0
- icdev/tools/mcp/unified_server.py +158 -0
- icdev/tools/memory/__init__.py +2 -0
- icdev/tools/memory/auto_capture.py +347 -0
- icdev/tools/memory/embed_memory.py +158 -0
- icdev/tools/memory/history_compressor.py +334 -0
- icdev/tools/memory/hybrid_search.py +236 -0
- icdev/tools/memory/maintenance_cron.py +289 -0
- icdev/tools/memory/memory_consolidation.py +444 -0
- icdev/tools/memory/memory_db.py +133 -0
- icdev/tools/memory/memory_read.py +102 -0
- icdev/tools/memory/memory_write.py +222 -0
- icdev/tools/memory/semantic_search.py +139 -0
- icdev/tools/memory/time_decay.py +435 -0
- icdev/tools/modernization/__init__.py +3 -0
- icdev/tools/modernization/architecture_extractor.py +734 -0
- icdev/tools/modernization/compliance_bridge.py +1499 -0
- icdev/tools/modernization/db_migration_planner.py +1385 -0
- icdev/tools/modernization/doc_generator.py +1428 -0
- icdev/tools/modernization/framework_migrator.py +1525 -0
- icdev/tools/modernization/legacy_analyzer.py +1948 -0
- icdev/tools/modernization/migration_code_generator.py +1639 -0
- icdev/tools/modernization/migration_report_generator.py +1653 -0
- icdev/tools/modernization/migration_tracker.py +1726 -0
- icdev/tools/modernization/monolith_decomposer.py +1508 -0
- icdev/tools/modernization/seven_r_assessor.py +1658 -0
- icdev/tools/modernization/strangler_fig_manager.py +1705 -0
- icdev/tools/modernization/ui_analyzer.py +771 -0
- icdev/tools/modernization/version_migrator.py +1392 -0
- icdev/tools/monitor/__init__.py +1 -0
- icdev/tools/monitor/alert_correlator.py +495 -0
- icdev/tools/monitor/auto_resolver.py +612 -0
- icdev/tools/monitor/health_checker.py +509 -0
- icdev/tools/monitor/heartbeat_daemon.py +792 -0
- icdev/tools/monitor/log_analyzer.py +516 -0
- icdev/tools/monitor/metric_collector.py +496 -0
- icdev/tools/mosa/__init__.py +10 -0
- icdev/tools/mosa/icd_generator.py +370 -0
- icdev/tools/mosa/modular_design_analyzer.py +683 -0
- icdev/tools/mosa/mosa_code_enforcer.py +349 -0
- icdev/tools/mosa/tsp_generator.py +265 -0
- icdev/tools/observability/__init__.py +100 -0
- icdev/tools/observability/genai_attributes.py +88 -0
- icdev/tools/observability/instrumentation.py +140 -0
- icdev/tools/observability/mlflow_exporter.py +194 -0
- icdev/tools/observability/otel_tracer.py +168 -0
- icdev/tools/observability/provenance/__init__.py +3 -0
- icdev/tools/observability/provenance/prov_recorder.py +324 -0
- icdev/tools/observability/shap/__init__.py +3 -0
- icdev/tools/observability/shap/agent_shap.py +275 -0
- icdev/tools/observability/sqlite_tracer.py +361 -0
- icdev/tools/observability/trace_context.py +205 -0
- icdev/tools/observability/tracer.py +230 -0
- icdev/tools/orchestration/__init__.py +2 -0
- icdev/tools/orchestration/workflow_composer.py +361 -0
- icdev/tools/project/__init__.py +1 -0
- icdev/tools/project/manifest_loader.py +418 -0
- icdev/tools/project/project_create.py +350 -0
- icdev/tools/project/project_list.py +174 -0
- icdev/tools/project/project_scaffold.py +1715 -0
- icdev/tools/project/project_status.py +479 -0
- icdev/tools/project/session_context_builder.py +757 -0
- icdev/tools/project/validate_manifest.py +55 -0
- icdev/tools/registry/__init__.py +10 -0
- icdev/tools/registry/absorption_engine.py +832 -0
- icdev/tools/registry/capability_evaluator.py +668 -0
- icdev/tools/registry/child_registry.py +617 -0
- icdev/tools/registry/cross_pollinator.py +1065 -0
- icdev/tools/registry/genome_manager.py +671 -0
- icdev/tools/registry/learning_collector.py +912 -0
- icdev/tools/registry/propagation_manager.py +942 -0
- icdev/tools/registry/staging_manager.py +742 -0
- icdev/tools/registry/telemetry_collector.py +423 -0
- icdev/tools/requirements/__init__.py +1 -0
- icdev/tools/requirements/ai_governance_scorer.py +208 -0
- icdev/tools/requirements/boundary_analyzer.py +1293 -0
- icdev/tools/requirements/clarification_engine.py +618 -0
- icdev/tools/requirements/complexity_scorer.py +387 -0
- icdev/tools/requirements/consistency_analyzer.py +803 -0
- icdev/tools/requirements/constitution_manager.py +605 -0
- icdev/tools/requirements/decomposition_engine.py +778 -0
- icdev/tools/requirements/document_extractor.py +1016 -0
- icdev/tools/requirements/elicitation_techniques.py +519 -0
- icdev/tools/requirements/gap_detector.py +271 -0
- icdev/tools/requirements/intake_engine.py +2188 -0
- icdev/tools/requirements/prd_generator.py +847 -0
- icdev/tools/requirements/prd_validator.py +595 -0
- icdev/tools/requirements/readiness_scorer.py +313 -0
- icdev/tools/requirements/spec_organizer.py +1029 -0
- icdev/tools/requirements/spec_quality_checker.py +1097 -0
- icdev/tools/requirements/traceability_builder.py +579 -0
- icdev/tools/resilience/__init__.py +34 -0
- icdev/tools/resilience/circuit_breaker.py +340 -0
- icdev/tools/resilience/correlation.py +150 -0
- icdev/tools/resilience/errors.py +81 -0
- icdev/tools/resilience/retry.py +95 -0
- icdev/tools/schemas/__init__.py +27 -0
- icdev/tools/schemas/chat.py +61 -0
- icdev/tools/schemas/compliance.py +56 -0
- icdev/tools/schemas/core.py +85 -0
- icdev/tools/schemas/innovation.py +37 -0
- icdev/tools/schemas/validation.py +109 -0
- icdev/tools/sdk/__init__.py +3 -0
- icdev/tools/sdk/icdev_client.py +218 -0
- icdev/tools/security/__init__.py +1 -0
- icdev/tools/security/agent_output_validator.py +330 -0
- icdev/tools/security/agent_trust_scorer.py +466 -0
- icdev/tools/security/ai_bom_generator.py +725 -0
- icdev/tools/security/ai_telemetry_logger.py +469 -0
- icdev/tools/security/atlas_red_team.py +543 -0
- icdev/tools/security/code_pattern_scanner.py +378 -0
- icdev/tools/security/confabulation_detector.py +271 -0
- icdev/tools/security/container_scanner.py +491 -0
- icdev/tools/security/dependency_auditor.py +944 -0
- icdev/tools/security/endpoint_security_scanner.py +579 -0
- icdev/tools/security/mcp_tool_authorizer.py +243 -0
- icdev/tools/security/prompt_injection_detector.py +737 -0
- icdev/tools/security/sast_runner.py +948 -0
- icdev/tools/security/secret_detector.py +378 -0
- icdev/tools/security/tool_chain_validator.py +357 -0
- icdev/tools/security/vuln_scanner.py +539 -0
- icdev/tools/simulation/__init__.py +2 -0
- icdev/tools/simulation/coa_generator.py +1552 -0
- icdev/tools/simulation/monte_carlo.py +758 -0
- icdev/tools/simulation/scenario_manager.py +1073 -0
- icdev/tools/simulation/simulation_engine.py +1104 -0
- icdev/tools/supply_chain/__init__.py +2 -0
- icdev/tools/supply_chain/cve_triager.py +705 -0
- icdev/tools/supply_chain/dependency_graph.py +645 -0
- icdev/tools/supply_chain/isa_manager.py +540 -0
- icdev/tools/supply_chain/scrm_assessor.py +546 -0
- icdev/tools/testing/__init__.py +2 -0
- icdev/tools/testing/acceptance_validator.py +411 -0
- icdev/tools/testing/claude_dir_validator.py +831 -0
- icdev/tools/testing/data_types.py +199 -0
- icdev/tools/testing/e2e_runner.py +715 -0
- icdev/tools/testing/fuzz_cli.py +306 -0
- icdev/tools/testing/health_check.py +483 -0
- icdev/tools/testing/platform_check.py +143 -0
- icdev/tools/testing/production_audit.py +1862 -0
- icdev/tools/testing/production_remediate.py +804 -0
- icdev/tools/testing/screenshot_validator.py +539 -0
- icdev/tools/testing/smoke_test.py +283 -0
- icdev/tools/testing/test_agent_models.py +117 -0
- icdev/tools/testing/test_orchestrator.py +957 -0
- icdev/tools/testing/utils.py +229 -0
- icdev/tools/translation/__init__.py +17 -0
- icdev/tools/translation/code_translator.py +550 -0
- icdev/tools/translation/dependency_mapper.py +277 -0
- icdev/tools/translation/feature_map.py +395 -0
- icdev/tools/translation/project_assembler.py +439 -0
- icdev/tools/translation/source_extractor.py +609 -0
- icdev/tools/translation/test_translator.py +333 -0
- icdev/tools/translation/translation_manager.py +582 -0
- icdev/tools/translation/translation_validator.py +662 -0
- icdev/tools/translation/type_checker.py +371 -0
- icdev-1.0.0.dist-info/METADATA +868 -0
- icdev-1.0.0.dist-info/RECORD +1105 -0
- icdev-1.0.0.dist-info/WHEEL +5 -0
- icdev-1.0.0.dist-info/entry_points.txt +9 -0
- icdev-1.0.0.dist-info/licenses/LICENSE +254 -0
- icdev-1.0.0.dist-info/licenses/NOTICE +268 -0
- icdev-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,2327 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# CUI // SP-CTI
|
|
3
|
+
"""IV&V assessment tool per IEEE 1012 and DoD standards.
|
|
4
|
+
|
|
5
|
+
Loads IV&V requirements from ivv_requirements.json, performs automated checks
|
|
6
|
+
where possible, stores results in ivv_assessments table, generates findings in
|
|
7
|
+
ivv_findings table, evaluates IV&V gates, applies CUI markings, and logs audit events."""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import re
|
|
13
|
+
import sqlite3
|
|
14
|
+
import sys
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from icdev._paths import get_project_root
|
|
18
|
+
|
|
19
|
+
BASE_DIR = get_project_root()
|
|
20
|
+
DB_PATH = BASE_DIR / "data" / "icdev.db"
|
|
21
|
+
IVV_REQUIREMENTS_PATH = BASE_DIR / "context" / "compliance" / "ivv_requirements.json"
|
|
22
|
+
|
|
23
|
+
PROCESS_AREAS = [
|
|
24
|
+
"Requirements Verification",
|
|
25
|
+
"Design Verification",
|
|
26
|
+
"Code Verification",
|
|
27
|
+
"Test Verification",
|
|
28
|
+
"Integration Verification",
|
|
29
|
+
"Traceability Analysis",
|
|
30
|
+
"Security Verification",
|
|
31
|
+
"Build/Deploy Verification",
|
|
32
|
+
"Process Compliance",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Process area codes used for scoring categories
|
|
36
|
+
PROCESS_AREA_CODES = {
|
|
37
|
+
"Requirements Verification": "REQ",
|
|
38
|
+
"Design Verification": "DES",
|
|
39
|
+
"Code Verification": "CODE",
|
|
40
|
+
"Test Verification": "TEST",
|
|
41
|
+
"Integration Verification": "INT",
|
|
42
|
+
"Traceability Analysis": "RTM",
|
|
43
|
+
"Security Verification": "SEC",
|
|
44
|
+
"Build/Deploy Verification": "BLD",
|
|
45
|
+
"Process Compliance": "PROC",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Verification areas contribute to verification_score
|
|
49
|
+
VERIFICATION_AREAS = ["REQ", "DES", "CODE", "RTM", "SEC", "BLD", "PROC"]
|
|
50
|
+
|
|
51
|
+
# Validation areas contribute to validation_score
|
|
52
|
+
VALIDATION_AREAS = ["TEST", "INT"]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# -----------------------------------------------------------------
|
|
56
|
+
# Database helpers
|
|
57
|
+
# -----------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
def _get_connection(db_path=None):
|
|
60
|
+
"""Get a database connection with Row factory."""
|
|
61
|
+
path = db_path or DB_PATH
|
|
62
|
+
if not path.exists():
|
|
63
|
+
raise FileNotFoundError(
|
|
64
|
+
f"Database not found: {path}\n"
|
|
65
|
+
"Run: python tools/db/init_icdev_db.py"
|
|
66
|
+
)
|
|
67
|
+
conn = sqlite3.connect(str(path))
|
|
68
|
+
conn.row_factory = sqlite3.Row
|
|
69
|
+
return conn
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_project(conn, project_id):
|
|
73
|
+
"""Load project data from the projects table."""
|
|
74
|
+
row = conn.execute(
|
|
75
|
+
"SELECT * FROM projects WHERE id = ?", (project_id,)
|
|
76
|
+
).fetchone()
|
|
77
|
+
if not row:
|
|
78
|
+
raise ValueError(f"Project '{project_id}' not found.")
|
|
79
|
+
return dict(row)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# -----------------------------------------------------------------
|
|
83
|
+
# Configuration helpers
|
|
84
|
+
# -----------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
def _load_cui_config():
|
|
87
|
+
"""Load CUI marking configuration."""
|
|
88
|
+
try:
|
|
89
|
+
sys.path.insert(0, str(BASE_DIR / "tools" / "compliance"))
|
|
90
|
+
from cui_marker import load_cui_config
|
|
91
|
+
return load_cui_config()
|
|
92
|
+
except ImportError:
|
|
93
|
+
return {
|
|
94
|
+
"document_header": (
|
|
95
|
+
"////////////////////////////////////////////////////////////////////\n"
|
|
96
|
+
"CONTROLLED UNCLASSIFIED INFORMATION (CUI) // SP-CTI\n"
|
|
97
|
+
"Distribution: Distribution D -- Authorized DoD Personnel Only\n"
|
|
98
|
+
"////////////////////////////////////////////////////////////////////"
|
|
99
|
+
),
|
|
100
|
+
"document_footer": (
|
|
101
|
+
"////////////////////////////////////////////////////////////////////\n"
|
|
102
|
+
"CUI // SP-CTI | Department of Defense\n"
|
|
103
|
+
"////////////////////////////////////////////////////////////////////"
|
|
104
|
+
),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _load_ivv_requirements():
|
|
109
|
+
"""Load IV&V requirements from the JSON catalog."""
|
|
110
|
+
if not IVV_REQUIREMENTS_PATH.exists():
|
|
111
|
+
raise FileNotFoundError(
|
|
112
|
+
f"IV&V requirements file not found: {IVV_REQUIREMENTS_PATH}\n"
|
|
113
|
+
"Expected: context/compliance/ivv_requirements.json"
|
|
114
|
+
)
|
|
115
|
+
with open(IVV_REQUIREMENTS_PATH, "r", encoding="utf-8") as f:
|
|
116
|
+
return json.load(f)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _log_audit_event(conn, project_id, action, details, file_path=None):
|
|
120
|
+
"""Log an audit trail event (append-only, NIST AU compliant)."""
|
|
121
|
+
try:
|
|
122
|
+
conn.execute(
|
|
123
|
+
"""INSERT INTO audit_trail
|
|
124
|
+
(project_id, event_type, actor, action, details,
|
|
125
|
+
affected_files, classification)
|
|
126
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
|
127
|
+
(
|
|
128
|
+
project_id,
|
|
129
|
+
"ivv_assessed",
|
|
130
|
+
"icdev-ivv-engine",
|
|
131
|
+
action,
|
|
132
|
+
json.dumps(details),
|
|
133
|
+
json.dumps([str(file_path)] if file_path else []),
|
|
134
|
+
"CUI",
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
conn.commit()
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"Warning: Could not log audit event: {e}", file=sys.stderr)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# -----------------------------------------------------------------
|
|
143
|
+
# Auto-check helper: walk project files matching extensions
|
|
144
|
+
# -----------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
def _scan_files(project_dir, extensions, patterns, threshold=1):
|
|
147
|
+
"""Scan project files for regex patterns.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
project_dir: Root directory to walk.
|
|
151
|
+
extensions: Tuple of file extensions to include (e.g. ('.py', '.md')).
|
|
152
|
+
patterns: List of regex patterns to search for.
|
|
153
|
+
threshold: Minimum number of files with matches to consider satisfied.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Tuple of (matched_files, total_scanned).
|
|
157
|
+
"""
|
|
158
|
+
matched_files = []
|
|
159
|
+
total_scanned = 0
|
|
160
|
+
for root, _, files in os.walk(project_dir):
|
|
161
|
+
for fname in files:
|
|
162
|
+
if not fname.endswith(extensions):
|
|
163
|
+
continue
|
|
164
|
+
fpath = os.path.join(root, fname)
|
|
165
|
+
total_scanned += 1
|
|
166
|
+
try:
|
|
167
|
+
with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
|
|
168
|
+
content = f.read()
|
|
169
|
+
for pattern in patterns:
|
|
170
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
171
|
+
matched_files.append(fpath)
|
|
172
|
+
break
|
|
173
|
+
except Exception:
|
|
174
|
+
continue
|
|
175
|
+
return matched_files, total_scanned
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _dir_or_file_exists(project_dir, dir_names=None, glob_patterns=None):
|
|
179
|
+
"""Check if specific directories or file globs exist under project_dir.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
project_dir: Root directory to search.
|
|
183
|
+
dir_names: List of directory names to look for.
|
|
184
|
+
glob_patterns: List of glob patterns to match files.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
List of found paths.
|
|
188
|
+
"""
|
|
189
|
+
found = []
|
|
190
|
+
project_path = Path(project_dir)
|
|
191
|
+
|
|
192
|
+
if dir_names:
|
|
193
|
+
for dname in dir_names:
|
|
194
|
+
candidate = project_path / dname
|
|
195
|
+
if candidate.is_dir():
|
|
196
|
+
found.append(str(candidate))
|
|
197
|
+
# Also check one level deeper (e.g. infra/terraform/)
|
|
198
|
+
for child in project_path.rglob(dname):
|
|
199
|
+
if child.is_dir() and str(child) not in found:
|
|
200
|
+
found.append(str(child))
|
|
201
|
+
|
|
202
|
+
if glob_patterns:
|
|
203
|
+
for gp in glob_patterns:
|
|
204
|
+
for match in project_path.rglob(gp):
|
|
205
|
+
if str(match) not in found:
|
|
206
|
+
found.append(str(match))
|
|
207
|
+
|
|
208
|
+
return found
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# -----------------------------------------------------------------
|
|
212
|
+
# Auto-check functions
|
|
213
|
+
# Each returns a dict:
|
|
214
|
+
# {"status": "satisfied"|"not_satisfied"|"partially_satisfied"|"not_applicable",
|
|
215
|
+
# "evidence": "description of what was found",
|
|
216
|
+
# "details": "specifics"}
|
|
217
|
+
# -----------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
def _check_req_completeness(project_dir):
|
|
220
|
+
"""IVV-01: Requirements Completeness.
|
|
221
|
+
|
|
222
|
+
Look for requirements.md, user-stories.md, *.feature files, docs/requirements/.
|
|
223
|
+
Satisfied if at least 2 types of requirements docs found; partially if 1;
|
|
224
|
+
not_satisfied if 0.
|
|
225
|
+
"""
|
|
226
|
+
types_found = []
|
|
227
|
+
project_path = Path(project_dir)
|
|
228
|
+
|
|
229
|
+
# Type 1: requirements documents
|
|
230
|
+
req_docs = _dir_or_file_exists(
|
|
231
|
+
project_dir,
|
|
232
|
+
glob_patterns=[
|
|
233
|
+
"requirements.md", "REQUIREMENTS.md", "requirements.txt",
|
|
234
|
+
"requirements.rst", "functional-requirements*", "non-functional-requirements*",
|
|
235
|
+
],
|
|
236
|
+
)
|
|
237
|
+
if req_docs:
|
|
238
|
+
types_found.append(("requirements_docs", req_docs))
|
|
239
|
+
|
|
240
|
+
# Type 2: user stories
|
|
241
|
+
user_stories = _dir_or_file_exists(
|
|
242
|
+
project_dir,
|
|
243
|
+
glob_patterns=[
|
|
244
|
+
"user-stories.md", "user_stories.md", "USER_STORIES.md",
|
|
245
|
+
"user-stories*.md", "stories*.md",
|
|
246
|
+
],
|
|
247
|
+
)
|
|
248
|
+
if user_stories:
|
|
249
|
+
types_found.append(("user_stories", user_stories))
|
|
250
|
+
|
|
251
|
+
# Type 3: .feature files (BDD/Gherkin)
|
|
252
|
+
feature_files = list(project_path.rglob("*.feature"))
|
|
253
|
+
if feature_files:
|
|
254
|
+
types_found.append(("feature_files", [str(f) for f in feature_files]))
|
|
255
|
+
|
|
256
|
+
# Type 4: requirements directory
|
|
257
|
+
req_dirs = _dir_or_file_exists(
|
|
258
|
+
project_dir,
|
|
259
|
+
dir_names=["requirements", "docs/requirements", "specs"],
|
|
260
|
+
)
|
|
261
|
+
if req_dirs:
|
|
262
|
+
types_found.append(("requirements_directory", req_dirs))
|
|
263
|
+
|
|
264
|
+
count = len(types_found)
|
|
265
|
+
all_evidence = []
|
|
266
|
+
for tname, tfiles in types_found:
|
|
267
|
+
all_evidence.append(f"{tname}: {len(tfiles)} item(s)")
|
|
268
|
+
|
|
269
|
+
if count >= 2:
|
|
270
|
+
return {
|
|
271
|
+
"status": "satisfied",
|
|
272
|
+
"evidence": (
|
|
273
|
+
f"Requirements completeness: {count} types of requirements "
|
|
274
|
+
f"documentation found."
|
|
275
|
+
),
|
|
276
|
+
"details": "; ".join(all_evidence),
|
|
277
|
+
}
|
|
278
|
+
elif count == 1:
|
|
279
|
+
return {
|
|
280
|
+
"status": "partially_satisfied",
|
|
281
|
+
"evidence": (
|
|
282
|
+
"Only 1 type of requirements documentation found. "
|
|
283
|
+
"At least 2 types required for full completeness."
|
|
284
|
+
),
|
|
285
|
+
"details": "; ".join(all_evidence),
|
|
286
|
+
}
|
|
287
|
+
else:
|
|
288
|
+
return {
|
|
289
|
+
"status": "not_satisfied",
|
|
290
|
+
"evidence": "No requirements documentation found.",
|
|
291
|
+
"details": (
|
|
292
|
+
"Expected: requirements.md, user-stories.md, *.feature files, "
|
|
293
|
+
"or docs/requirements/ directory."
|
|
294
|
+
),
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _check_req_consistency(project_dir):
|
|
299
|
+
"""IVV-02: Requirements Consistency.
|
|
300
|
+
|
|
301
|
+
Find .feature files, check if test file names match feature names
|
|
302
|
+
(e.g., auth.feature -> test_auth.py or auth_steps.py).
|
|
303
|
+
Satisfied if >80% features have matching tests; partially if >50%.
|
|
304
|
+
"""
|
|
305
|
+
project_path = Path(project_dir)
|
|
306
|
+
feature_files = list(project_path.rglob("*.feature"))
|
|
307
|
+
|
|
308
|
+
if not feature_files:
|
|
309
|
+
return {
|
|
310
|
+
"status": "not_satisfied",
|
|
311
|
+
"evidence": "No .feature files found for consistency analysis.",
|
|
312
|
+
"details": "Cannot assess requirements consistency without feature files.",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# Collect all test file stems for matching
|
|
316
|
+
test_files = set()
|
|
317
|
+
for ext in ("*.py", "*.js", "*.ts"):
|
|
318
|
+
for tf in project_path.rglob(ext):
|
|
319
|
+
test_files.add(tf.stem.lower())
|
|
320
|
+
|
|
321
|
+
matched = 0
|
|
322
|
+
unmatched_features = []
|
|
323
|
+
|
|
324
|
+
for feat in feature_files:
|
|
325
|
+
feat_stem = feat.stem.lower()
|
|
326
|
+
# Check for test_<feature>, <feature>_test, <feature>_steps, test<feature>
|
|
327
|
+
candidates = [
|
|
328
|
+
f"test_{feat_stem}",
|
|
329
|
+
f"{feat_stem}_test",
|
|
330
|
+
f"{feat_stem}_steps",
|
|
331
|
+
f"test{feat_stem}",
|
|
332
|
+
f"{feat_stem}_spec",
|
|
333
|
+
f"steps_{feat_stem}",
|
|
334
|
+
]
|
|
335
|
+
if any(c in test_files for c in candidates):
|
|
336
|
+
matched += 1
|
|
337
|
+
else:
|
|
338
|
+
unmatched_features.append(feat.name)
|
|
339
|
+
|
|
340
|
+
total = len(feature_files)
|
|
341
|
+
ratio = matched / total if total > 0 else 0
|
|
342
|
+
|
|
343
|
+
if ratio > 0.8:
|
|
344
|
+
return {
|
|
345
|
+
"status": "satisfied",
|
|
346
|
+
"evidence": (
|
|
347
|
+
f"Requirements consistency: {matched}/{total} features "
|
|
348
|
+
f"({ratio:.0%}) have matching test files."
|
|
349
|
+
),
|
|
350
|
+
"details": "Threshold: >80%. All features are consistent with test naming.",
|
|
351
|
+
}
|
|
352
|
+
elif ratio > 0.5:
|
|
353
|
+
return {
|
|
354
|
+
"status": "partially_satisfied",
|
|
355
|
+
"evidence": (
|
|
356
|
+
f"Requirements consistency: {matched}/{total} features "
|
|
357
|
+
f"({ratio:.0%}) have matching test files."
|
|
358
|
+
),
|
|
359
|
+
"details": (
|
|
360
|
+
f"Threshold: >80% for full compliance. "
|
|
361
|
+
f"Unmatched: {', '.join(unmatched_features[:5])}"
|
|
362
|
+
),
|
|
363
|
+
}
|
|
364
|
+
else:
|
|
365
|
+
return {
|
|
366
|
+
"status": "not_satisfied",
|
|
367
|
+
"evidence": (
|
|
368
|
+
f"Requirements consistency: only {matched}/{total} features "
|
|
369
|
+
f"({ratio:.0%}) have matching test files."
|
|
370
|
+
),
|
|
371
|
+
"details": (
|
|
372
|
+
f"Most feature files lack corresponding test files. "
|
|
373
|
+
f"Unmatched: {', '.join(unmatched_features[:10])}"
|
|
374
|
+
),
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _check_req_testability(project_dir):
|
|
379
|
+
"""IVV-03: Requirements Testability.
|
|
380
|
+
|
|
381
|
+
Each .feature file should have at least one corresponding step file
|
|
382
|
+
in a steps/ directory.
|
|
383
|
+
Satisfied if all features have steps; partially if >70%.
|
|
384
|
+
"""
|
|
385
|
+
project_path = Path(project_dir)
|
|
386
|
+
feature_files = list(project_path.rglob("*.feature"))
|
|
387
|
+
|
|
388
|
+
if not feature_files:
|
|
389
|
+
return {
|
|
390
|
+
"status": "not_satisfied",
|
|
391
|
+
"evidence": "No .feature files found to assess testability.",
|
|
392
|
+
"details": "Cannot verify requirements testability without BDD feature files.",
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# Find all step definition files
|
|
396
|
+
step_files = set()
|
|
397
|
+
for steps_dir in project_path.rglob("steps"):
|
|
398
|
+
if steps_dir.is_dir():
|
|
399
|
+
for sf in steps_dir.iterdir():
|
|
400
|
+
if sf.suffix in (".py", ".js", ".ts", ".rb"):
|
|
401
|
+
step_files.add(sf.stem.lower())
|
|
402
|
+
|
|
403
|
+
# Also look for *_steps.py pattern outside steps/ directories
|
|
404
|
+
for sf in project_path.rglob("*_steps.py"):
|
|
405
|
+
step_files.add(sf.stem.lower())
|
|
406
|
+
for sf in project_path.rglob("*_steps.js"):
|
|
407
|
+
step_files.add(sf.stem.lower())
|
|
408
|
+
|
|
409
|
+
matched = 0
|
|
410
|
+
unmatched = []
|
|
411
|
+
|
|
412
|
+
for feat in feature_files:
|
|
413
|
+
feat_stem = feat.stem.lower()
|
|
414
|
+
# Match: steps/<feature>.py, <feature>_steps.py, steps_<feature>.py
|
|
415
|
+
candidates = [
|
|
416
|
+
feat_stem,
|
|
417
|
+
f"{feat_stem}_steps",
|
|
418
|
+
f"steps_{feat_stem}",
|
|
419
|
+
f"test_{feat_stem}",
|
|
420
|
+
f"{feat_stem}_step_defs",
|
|
421
|
+
]
|
|
422
|
+
if any(c in step_files for c in candidates):
|
|
423
|
+
matched += 1
|
|
424
|
+
else:
|
|
425
|
+
unmatched.append(feat.name)
|
|
426
|
+
|
|
427
|
+
total = len(feature_files)
|
|
428
|
+
ratio = matched / total if total > 0 else 0
|
|
429
|
+
|
|
430
|
+
if ratio >= 1.0:
|
|
431
|
+
return {
|
|
432
|
+
"status": "satisfied",
|
|
433
|
+
"evidence": (
|
|
434
|
+
f"All {total} feature file(s) have corresponding step "
|
|
435
|
+
f"implementation files."
|
|
436
|
+
),
|
|
437
|
+
"details": "Step files found for every .feature file.",
|
|
438
|
+
}
|
|
439
|
+
elif ratio > 0.7:
|
|
440
|
+
return {
|
|
441
|
+
"status": "partially_satisfied",
|
|
442
|
+
"evidence": (
|
|
443
|
+
f"Testability: {matched}/{total} features ({ratio:.0%}) "
|
|
444
|
+
f"have step implementations."
|
|
445
|
+
),
|
|
446
|
+
"details": (
|
|
447
|
+
f"Threshold: 100% for full compliance, >70% for partial. "
|
|
448
|
+
f"Missing steps for: {', '.join(unmatched[:5])}"
|
|
449
|
+
),
|
|
450
|
+
}
|
|
451
|
+
else:
|
|
452
|
+
return {
|
|
453
|
+
"status": "not_satisfied",
|
|
454
|
+
"evidence": (
|
|
455
|
+
f"Testability: only {matched}/{total} features ({ratio:.0%}) "
|
|
456
|
+
f"have step implementations."
|
|
457
|
+
),
|
|
458
|
+
"details": (
|
|
459
|
+
f"Most feature files lack corresponding step definitions. "
|
|
460
|
+
f"Missing: {', '.join(unmatched[:10])}"
|
|
461
|
+
),
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _check_design_architecture(project_dir):
|
|
466
|
+
"""IVV-05: Architecture Documentation.
|
|
467
|
+
|
|
468
|
+
Look for architecture.md, system_design.md, ARCHITECTURE.md,
|
|
469
|
+
docs/architecture/, docs/design/, adr/ (Architecture Decision Records).
|
|
470
|
+
Satisfied if architecture docs exist.
|
|
471
|
+
"""
|
|
472
|
+
found_docs = _dir_or_file_exists(
|
|
473
|
+
project_dir,
|
|
474
|
+
glob_patterns=[
|
|
475
|
+
"architecture.md", "ARCHITECTURE.md", "system_design.md",
|
|
476
|
+
"SYSTEM_DESIGN.md", "system-design.md", "design.md",
|
|
477
|
+
"DESIGN.md", "architecture.rst", "system_architecture*",
|
|
478
|
+
],
|
|
479
|
+
)
|
|
480
|
+
found_dirs = _dir_or_file_exists(
|
|
481
|
+
project_dir,
|
|
482
|
+
dir_names=[
|
|
483
|
+
"architecture", "docs/architecture", "docs/design",
|
|
484
|
+
"design", "adr", "docs/adr", "doc/architecture",
|
|
485
|
+
],
|
|
486
|
+
)
|
|
487
|
+
all_found = list(set(found_docs + found_dirs))
|
|
488
|
+
|
|
489
|
+
if all_found:
|
|
490
|
+
return {
|
|
491
|
+
"status": "satisfied",
|
|
492
|
+
"evidence": (
|
|
493
|
+
f"Architecture documentation found: {len(all_found)} artifact(s)."
|
|
494
|
+
),
|
|
495
|
+
"details": "; ".join(
|
|
496
|
+
os.path.basename(f) for f in all_found[:5]
|
|
497
|
+
),
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
"status": "not_satisfied",
|
|
501
|
+
"evidence": "No architecture documentation found.",
|
|
502
|
+
"details": (
|
|
503
|
+
"Expected: architecture.md, system_design.md, ARCHITECTURE.md, "
|
|
504
|
+
"docs/architecture/, docs/design/, or adr/ directory."
|
|
505
|
+
),
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _check_independent_sast(project_dir):
|
|
510
|
+
"""IVV-08: Independent SAST.
|
|
511
|
+
|
|
512
|
+
Check for SAST result files: *sast*report*, *bandit*report*, *.sarif.
|
|
513
|
+
Also scan for known SAST tool output in JSON/XML files.
|
|
514
|
+
Satisfied if SAST results found.
|
|
515
|
+
"""
|
|
516
|
+
found_files = _dir_or_file_exists(
|
|
517
|
+
project_dir,
|
|
518
|
+
glob_patterns=[
|
|
519
|
+
"*sast*report*", "*bandit*report*", "*.sarif",
|
|
520
|
+
"*sast*result*", "*sonarqube*report*", "*semgrep*",
|
|
521
|
+
"*codeql*", "*sast*.json", "*sast*.xml",
|
|
522
|
+
],
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
# Also scan JSON/XML files for SAST tool output signatures
|
|
526
|
+
sast_patterns = [
|
|
527
|
+
r'"tool".*"bandit"|"tool".*"semgrep"|"tool".*"codeql"',
|
|
528
|
+
r"bandit.*results|severity.*confidence.*location",
|
|
529
|
+
r"sonarqube|sonarlint|checkmarx|fortify|coverity",
|
|
530
|
+
]
|
|
531
|
+
matched, _ = _scan_files(
|
|
532
|
+
project_dir, (".json", ".xml", ".sarif"), sast_patterns
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
all_found = list(set(found_files + matched))
|
|
536
|
+
if all_found:
|
|
537
|
+
return {
|
|
538
|
+
"status": "satisfied",
|
|
539
|
+
"evidence": (
|
|
540
|
+
f"SAST results found: {len(all_found)} artifact(s)."
|
|
541
|
+
),
|
|
542
|
+
"details": "; ".join(
|
|
543
|
+
os.path.basename(f) for f in all_found[:5]
|
|
544
|
+
),
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
"status": "not_satisfied",
|
|
548
|
+
"evidence": "No SAST scan results detected in the project.",
|
|
549
|
+
"details": (
|
|
550
|
+
"Expected: *sast*report*, *bandit*report*, *.sarif files, "
|
|
551
|
+
"or SAST tool output in JSON/XML."
|
|
552
|
+
),
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _check_coding_standards(project_dir):
|
|
557
|
+
"""IVV-09: Coding Standards Compliance.
|
|
558
|
+
|
|
559
|
+
Look for linter configs: .flake8, .pylintrc, .eslintrc*, tslint.json,
|
|
560
|
+
pyproject.toml (with [tool.ruff] or [tool.black]), setup.cfg (with [flake8]).
|
|
561
|
+
Satisfied if linter config found.
|
|
562
|
+
"""
|
|
563
|
+
# Direct config file checks
|
|
564
|
+
found = _dir_or_file_exists(
|
|
565
|
+
project_dir,
|
|
566
|
+
glob_patterns=[
|
|
567
|
+
".flake8", ".pylintrc", ".eslintrc*", "tslint.json",
|
|
568
|
+
".prettierrc*", ".stylelintrc*", ".editorconfig",
|
|
569
|
+
"ruff.toml", ".ruff.toml", ".bandit",
|
|
570
|
+
],
|
|
571
|
+
)
|
|
572
|
+
if found:
|
|
573
|
+
return {
|
|
574
|
+
"status": "satisfied",
|
|
575
|
+
"evidence": (
|
|
576
|
+
f"Coding standards configuration found: {len(found)} config file(s)."
|
|
577
|
+
),
|
|
578
|
+
"details": "; ".join(
|
|
579
|
+
os.path.basename(f) for f in found[:5]
|
|
580
|
+
),
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
# Check pyproject.toml for linter sections
|
|
584
|
+
pyproject_files = _dir_or_file_exists(
|
|
585
|
+
project_dir, glob_patterns=["pyproject.toml"]
|
|
586
|
+
)
|
|
587
|
+
for pp_path in pyproject_files:
|
|
588
|
+
try:
|
|
589
|
+
with open(pp_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
590
|
+
content = f.read()
|
|
591
|
+
if re.search(
|
|
592
|
+
r"\[tool\.(ruff|black|flake8|pylint|isort|mypy)\]",
|
|
593
|
+
content, re.IGNORECASE,
|
|
594
|
+
):
|
|
595
|
+
return {
|
|
596
|
+
"status": "satisfied",
|
|
597
|
+
"evidence": "Linter configuration found in pyproject.toml.",
|
|
598
|
+
"details": f"File: {os.path.basename(pp_path)}",
|
|
599
|
+
}
|
|
600
|
+
except Exception:
|
|
601
|
+
continue
|
|
602
|
+
|
|
603
|
+
# Check setup.cfg for [flake8] section
|
|
604
|
+
setup_cfg_files = _dir_or_file_exists(
|
|
605
|
+
project_dir, glob_patterns=["setup.cfg"]
|
|
606
|
+
)
|
|
607
|
+
for sc_path in setup_cfg_files:
|
|
608
|
+
try:
|
|
609
|
+
with open(sc_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
610
|
+
content = f.read()
|
|
611
|
+
if re.search(r"\[flake8\]|\[pylint\]|\[mypy\]", content):
|
|
612
|
+
return {
|
|
613
|
+
"status": "satisfied",
|
|
614
|
+
"evidence": "Linter configuration found in setup.cfg.",
|
|
615
|
+
"details": f"File: {os.path.basename(sc_path)}",
|
|
616
|
+
}
|
|
617
|
+
except Exception:
|
|
618
|
+
continue
|
|
619
|
+
|
|
620
|
+
return {
|
|
621
|
+
"status": "not_satisfied",
|
|
622
|
+
"evidence": "No coding standards or linter configuration found.",
|
|
623
|
+
"details": (
|
|
624
|
+
"Expected: .flake8, .pylintrc, .eslintrc*, tslint.json, "
|
|
625
|
+
"or [tool.ruff]/[tool.black] in pyproject.toml."
|
|
626
|
+
),
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def _check_code_review_completion(project_dir):
|
|
631
|
+
"""IVV-10: Code Review Completion.
|
|
632
|
+
|
|
633
|
+
Look for code review evidence: CODEOWNERS, .github/CODEOWNERS,
|
|
634
|
+
merge request templates, pull_request_template.md.
|
|
635
|
+
Also scan for @approval_required, review_required patterns.
|
|
636
|
+
Satisfied if code review infrastructure found.
|
|
637
|
+
"""
|
|
638
|
+
found_files = _dir_or_file_exists(
|
|
639
|
+
project_dir,
|
|
640
|
+
glob_patterns=[
|
|
641
|
+
"CODEOWNERS", ".github/CODEOWNERS", ".gitlab/CODEOWNERS",
|
|
642
|
+
"pull_request_template.md", "PULL_REQUEST_TEMPLATE.md",
|
|
643
|
+
"merge_request_template*", ".github/pull_request_template*",
|
|
644
|
+
".gitlab/merge_request_templates/*",
|
|
645
|
+
],
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
if found_files:
|
|
649
|
+
return {
|
|
650
|
+
"status": "satisfied",
|
|
651
|
+
"evidence": (
|
|
652
|
+
f"Code review infrastructure found: {len(found_files)} artifact(s)."
|
|
653
|
+
),
|
|
654
|
+
"details": "; ".join(
|
|
655
|
+
os.path.basename(f) for f in found_files[:5]
|
|
656
|
+
),
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
# Scan CI/pipeline files for review requirements
|
|
660
|
+
review_patterns = [
|
|
661
|
+
r"approval_required|required_approvals|approvals_required",
|
|
662
|
+
r"review_required|code.review|peer.review",
|
|
663
|
+
r"merge.*approval|approve.*merge|required_reviewers",
|
|
664
|
+
]
|
|
665
|
+
extensions = (".yml", ".yaml", ".json", ".toml", ".py")
|
|
666
|
+
matched, total = _scan_files(project_dir, extensions, review_patterns)
|
|
667
|
+
|
|
668
|
+
if matched:
|
|
669
|
+
return {
|
|
670
|
+
"status": "satisfied",
|
|
671
|
+
"evidence": (
|
|
672
|
+
f"Code review enforcement patterns found in {len(matched)} file(s)."
|
|
673
|
+
),
|
|
674
|
+
"details": "; ".join(
|
|
675
|
+
os.path.basename(f) for f in matched[:5]
|
|
676
|
+
),
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if total == 0:
|
|
680
|
+
return {
|
|
681
|
+
"status": "not_satisfied",
|
|
682
|
+
"evidence": "No applicable files found to assess code review configuration.",
|
|
683
|
+
"details": "Project directory lacks CI/CD or configuration files.",
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return {
|
|
687
|
+
"status": "not_satisfied",
|
|
688
|
+
"evidence": "No code review infrastructure or enforcement detected.",
|
|
689
|
+
"details": (
|
|
690
|
+
"Expected: CODEOWNERS, pull_request_template.md, or "
|
|
691
|
+
"approval_required patterns in CI configuration."
|
|
692
|
+
),
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def _check_complexity_metrics(project_dir):
|
|
697
|
+
"""IVV-11: Complexity Metrics.
|
|
698
|
+
|
|
699
|
+
Look for radon config in pyproject.toml/setup.cfg, .complexity,
|
|
700
|
+
complexity-report.*, McCabe config.
|
|
701
|
+
Also check for max-complexity in linter config.
|
|
702
|
+
Satisfied if complexity tooling configured.
|
|
703
|
+
"""
|
|
704
|
+
# Direct complexity tool artifacts
|
|
705
|
+
found = _dir_or_file_exists(
|
|
706
|
+
project_dir,
|
|
707
|
+
glob_patterns=[
|
|
708
|
+
".complexity", "complexity-report*", "*complexity*report*",
|
|
709
|
+
"radon-report*", "*radon*",
|
|
710
|
+
],
|
|
711
|
+
)
|
|
712
|
+
if found:
|
|
713
|
+
return {
|
|
714
|
+
"status": "satisfied",
|
|
715
|
+
"evidence": (
|
|
716
|
+
f"Complexity measurement artifacts found: {len(found)} item(s)."
|
|
717
|
+
),
|
|
718
|
+
"details": "; ".join(
|
|
719
|
+
os.path.basename(f) for f in found[:5]
|
|
720
|
+
),
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
# Check pyproject.toml for radon/complexity config
|
|
724
|
+
pyproject_files = _dir_or_file_exists(
|
|
725
|
+
project_dir, glob_patterns=["pyproject.toml"]
|
|
726
|
+
)
|
|
727
|
+
for pp_path in pyproject_files:
|
|
728
|
+
try:
|
|
729
|
+
with open(pp_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
730
|
+
content = f.read()
|
|
731
|
+
if re.search(
|
|
732
|
+
r"\[tool\.radon\]|max.complexity|mccabe|cyclomatic",
|
|
733
|
+
content, re.IGNORECASE,
|
|
734
|
+
):
|
|
735
|
+
return {
|
|
736
|
+
"status": "satisfied",
|
|
737
|
+
"evidence": "Complexity tooling configured in pyproject.toml.",
|
|
738
|
+
"details": f"File: {os.path.basename(pp_path)}",
|
|
739
|
+
}
|
|
740
|
+
except Exception:
|
|
741
|
+
continue
|
|
742
|
+
|
|
743
|
+
# Check setup.cfg for complexity settings
|
|
744
|
+
setup_cfg_files = _dir_or_file_exists(
|
|
745
|
+
project_dir, glob_patterns=["setup.cfg"]
|
|
746
|
+
)
|
|
747
|
+
for sc_path in setup_cfg_files:
|
|
748
|
+
try:
|
|
749
|
+
with open(sc_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
750
|
+
content = f.read()
|
|
751
|
+
if re.search(r"max.complexity|mccabe|radon", content, re.IGNORECASE):
|
|
752
|
+
return {
|
|
753
|
+
"status": "satisfied",
|
|
754
|
+
"evidence": "Complexity metrics configured in setup.cfg.",
|
|
755
|
+
"details": f"File: {os.path.basename(sc_path)}",
|
|
756
|
+
}
|
|
757
|
+
except Exception:
|
|
758
|
+
continue
|
|
759
|
+
|
|
760
|
+
# Check linter config files for max-complexity
|
|
761
|
+
linter_configs = _dir_or_file_exists(
|
|
762
|
+
project_dir,
|
|
763
|
+
glob_patterns=[".flake8", ".pylintrc", ".eslintrc*", "ruff.toml"],
|
|
764
|
+
)
|
|
765
|
+
for lc_path in linter_configs:
|
|
766
|
+
try:
|
|
767
|
+
with open(lc_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
768
|
+
content = f.read()
|
|
769
|
+
if re.search(r"max.complexity|mccabe|cyclomatic", content, re.IGNORECASE):
|
|
770
|
+
return {
|
|
771
|
+
"status": "satisfied",
|
|
772
|
+
"evidence": "Complexity threshold found in linter configuration.",
|
|
773
|
+
"details": f"File: {os.path.basename(lc_path)}",
|
|
774
|
+
}
|
|
775
|
+
except Exception:
|
|
776
|
+
continue
|
|
777
|
+
|
|
778
|
+
return {
|
|
779
|
+
"status": "not_satisfied",
|
|
780
|
+
"evidence": "No complexity measurement tooling or configuration detected.",
|
|
781
|
+
"details": (
|
|
782
|
+
"Expected: radon configuration, max-complexity in linter config, "
|
|
783
|
+
"or complexity-report artifacts."
|
|
784
|
+
),
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def _check_test_coverage(project_dir):
|
|
789
|
+
"""IVV-12: Test Coverage Adequacy.
|
|
790
|
+
|
|
791
|
+
Look for coverage artifacts: htmlcov/, coverage.xml, .coverage,
|
|
792
|
+
coverage-report.*, lcov.info, cobertura.xml.
|
|
793
|
+
Also check for pytest-cov in requirements, coverage config in pyproject.toml.
|
|
794
|
+
Satisfied if coverage reports/config found.
|
|
795
|
+
"""
|
|
796
|
+
# Coverage report artifacts
|
|
797
|
+
found_dirs = _dir_or_file_exists(
|
|
798
|
+
project_dir,
|
|
799
|
+
dir_names=["htmlcov", "coverage", "coverage-report"],
|
|
800
|
+
)
|
|
801
|
+
found_files = _dir_or_file_exists(
|
|
802
|
+
project_dir,
|
|
803
|
+
glob_patterns=[
|
|
804
|
+
"coverage.xml", ".coverage", "coverage-report*",
|
|
805
|
+
"lcov.info", "cobertura.xml", "coverage.json",
|
|
806
|
+
"*coverage*report*", ".coveragerc",
|
|
807
|
+
],
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
all_found = list(set(found_dirs + found_files))
|
|
811
|
+
if all_found:
|
|
812
|
+
return {
|
|
813
|
+
"status": "satisfied",
|
|
814
|
+
"evidence": (
|
|
815
|
+
f"Test coverage artifacts found: {len(all_found)} item(s)."
|
|
816
|
+
),
|
|
817
|
+
"details": "; ".join(
|
|
818
|
+
os.path.basename(f) for f in all_found[:5]
|
|
819
|
+
),
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
# Check requirements for coverage tools
|
|
823
|
+
req_files = _dir_or_file_exists(
|
|
824
|
+
project_dir,
|
|
825
|
+
glob_patterns=[
|
|
826
|
+
"requirements*.txt", "requirements/*.txt",
|
|
827
|
+
"Pipfile", "Pipfile.lock",
|
|
828
|
+
],
|
|
829
|
+
)
|
|
830
|
+
for rf_path in req_files:
|
|
831
|
+
try:
|
|
832
|
+
with open(rf_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
833
|
+
content = f.read()
|
|
834
|
+
if re.search(r"pytest.cov|coverage|istanbul|nyc", content, re.IGNORECASE):
|
|
835
|
+
return {
|
|
836
|
+
"status": "satisfied",
|
|
837
|
+
"evidence": "Coverage tooling found in project dependencies.",
|
|
838
|
+
"details": f"File: {os.path.basename(rf_path)}",
|
|
839
|
+
}
|
|
840
|
+
except Exception:
|
|
841
|
+
continue
|
|
842
|
+
|
|
843
|
+
# Check pyproject.toml for coverage config
|
|
844
|
+
pyproject_files = _dir_or_file_exists(
|
|
845
|
+
project_dir, glob_patterns=["pyproject.toml"]
|
|
846
|
+
)
|
|
847
|
+
for pp_path in pyproject_files:
|
|
848
|
+
try:
|
|
849
|
+
with open(pp_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
850
|
+
content = f.read()
|
|
851
|
+
if re.search(
|
|
852
|
+
r"\[tool\.coverage\]|\[tool\.pytest.*cov|--cov|coverage",
|
|
853
|
+
content, re.IGNORECASE,
|
|
854
|
+
):
|
|
855
|
+
return {
|
|
856
|
+
"status": "satisfied",
|
|
857
|
+
"evidence": "Coverage configuration found in pyproject.toml.",
|
|
858
|
+
"details": f"File: {os.path.basename(pp_path)}",
|
|
859
|
+
}
|
|
860
|
+
except Exception:
|
|
861
|
+
continue
|
|
862
|
+
|
|
863
|
+
return {
|
|
864
|
+
"status": "not_satisfied",
|
|
865
|
+
"evidence": "No test coverage reports or configuration detected.",
|
|
866
|
+
"details": (
|
|
867
|
+
"Expected: htmlcov/, coverage.xml, .coverage, lcov.info, "
|
|
868
|
+
"or pytest-cov in requirements."
|
|
869
|
+
),
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def _check_test_plan(project_dir):
|
|
874
|
+
"""IVV-13: Test Plan Completeness.
|
|
875
|
+
|
|
876
|
+
Look for test-plan.md, TEST_PLAN.md, docs/testing/,
|
|
877
|
+
tests/ directory with organized structure.
|
|
878
|
+
Check tests/ has subdirectories (unit/, integration/, e2e/) or at least
|
|
879
|
+
3+ test files. Satisfied if test plan or structured tests/ directory exists.
|
|
880
|
+
"""
|
|
881
|
+
# Check for explicit test plan docs
|
|
882
|
+
plan_docs = _dir_or_file_exists(
|
|
883
|
+
project_dir,
|
|
884
|
+
glob_patterns=[
|
|
885
|
+
"test-plan.md", "TEST_PLAN.md", "test_plan.md",
|
|
886
|
+
"test-plan.rst", "testing-plan*", "test-strategy*",
|
|
887
|
+
"TEST_STRATEGY*",
|
|
888
|
+
],
|
|
889
|
+
)
|
|
890
|
+
plan_dirs = _dir_or_file_exists(
|
|
891
|
+
project_dir,
|
|
892
|
+
dir_names=["docs/testing", "docs/test", "test-documentation"],
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
if plan_docs or plan_dirs:
|
|
896
|
+
all_found = list(set(plan_docs + plan_dirs))
|
|
897
|
+
return {
|
|
898
|
+
"status": "satisfied",
|
|
899
|
+
"evidence": (
|
|
900
|
+
f"Test plan documentation found: {len(all_found)} artifact(s)."
|
|
901
|
+
),
|
|
902
|
+
"details": "; ".join(
|
|
903
|
+
os.path.basename(f) for f in all_found[:5]
|
|
904
|
+
),
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
# Check for structured tests/ directory
|
|
908
|
+
project_path = Path(project_dir)
|
|
909
|
+
tests_dirs = list(project_path.rglob("tests"))
|
|
910
|
+
test_dirs = list(project_path.rglob("test"))
|
|
911
|
+
all_test_dirs = [d for d in (tests_dirs + test_dirs) if d.is_dir()]
|
|
912
|
+
|
|
913
|
+
for td in all_test_dirs:
|
|
914
|
+
# Check for organized subdirectories
|
|
915
|
+
subdirs = [
|
|
916
|
+
child.name for child in td.iterdir()
|
|
917
|
+
if child.is_dir() and child.name in (
|
|
918
|
+
"unit", "integration", "e2e", "functional",
|
|
919
|
+
"acceptance", "smoke", "performance", "security",
|
|
920
|
+
)
|
|
921
|
+
]
|
|
922
|
+
if subdirs:
|
|
923
|
+
return {
|
|
924
|
+
"status": "satisfied",
|
|
925
|
+
"evidence": (
|
|
926
|
+
f"Structured test directory found at {td.name}/ with "
|
|
927
|
+
f"subdirectories: {', '.join(subdirs)}."
|
|
928
|
+
),
|
|
929
|
+
"details": f"Path: {td}",
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
# Check for at least 3 test files
|
|
933
|
+
test_file_count = sum(
|
|
934
|
+
1 for f in td.iterdir()
|
|
935
|
+
if f.is_file() and f.name.startswith("test_")
|
|
936
|
+
)
|
|
937
|
+
if test_file_count >= 3:
|
|
938
|
+
return {
|
|
939
|
+
"status": "satisfied",
|
|
940
|
+
"evidence": (
|
|
941
|
+
f"Test directory found with {test_file_count} test file(s)."
|
|
942
|
+
),
|
|
943
|
+
"details": f"Path: {td}",
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
if all_test_dirs:
|
|
947
|
+
return {
|
|
948
|
+
"status": "partially_satisfied",
|
|
949
|
+
"evidence": (
|
|
950
|
+
"Test directory exists but lacks structured organization."
|
|
951
|
+
),
|
|
952
|
+
"details": (
|
|
953
|
+
"Expected: tests/ with unit/, integration/, e2e/ subdirectories "
|
|
954
|
+
"or at least 3 test files."
|
|
955
|
+
),
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
return {
|
|
959
|
+
"status": "not_satisfied",
|
|
960
|
+
"evidence": "No test plan documentation or structured tests/ directory found.",
|
|
961
|
+
"details": (
|
|
962
|
+
"Expected: test-plan.md, docs/testing/, or tests/ directory "
|
|
963
|
+
"with organized subdirectories."
|
|
964
|
+
),
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def _check_security_tests(project_dir):
|
|
969
|
+
"""IVV-14: Security Test Cases.
|
|
970
|
+
|
|
971
|
+
Scan test files for security patterns: test.*security, test.*auth,
|
|
972
|
+
test.*injection, test.*xss, test.*csrf, test.*permission, test.*access.
|
|
973
|
+
Satisfied if 2+ security test files found; partially if 1.
|
|
974
|
+
"""
|
|
975
|
+
project_path = Path(project_dir)
|
|
976
|
+
security_test_files = []
|
|
977
|
+
|
|
978
|
+
security_name_patterns = re.compile(
|
|
979
|
+
r"test.*(security|auth|injection|xss|csrf|permission|access|"
|
|
980
|
+
r"sanitiz|encrypt|token|jwt|session|privilege|exploit|vuln)",
|
|
981
|
+
re.IGNORECASE,
|
|
982
|
+
)
|
|
983
|
+
|
|
984
|
+
for ext in ("*.py", "*.js", "*.ts"):
|
|
985
|
+
for tf in project_path.rglob(ext):
|
|
986
|
+
if security_name_patterns.search(tf.stem):
|
|
987
|
+
security_test_files.append(str(tf))
|
|
988
|
+
|
|
989
|
+
# Also scan test file content for security test patterns
|
|
990
|
+
if not security_test_files:
|
|
991
|
+
content_patterns = [
|
|
992
|
+
r"def\s+test_.*(?:security|auth|inject|xss|csrf|permission|access)",
|
|
993
|
+
r"it\s*\(\s*['\"].*(?:security|auth|inject|xss|csrf|permission|access)",
|
|
994
|
+
r"describe\s*\(\s*['\"].*(?:security|auth|inject|xss|csrf|permission|access)",
|
|
995
|
+
]
|
|
996
|
+
matched, _ = _scan_files(
|
|
997
|
+
project_dir, (".py", ".js", ".ts"), content_patterns
|
|
998
|
+
)
|
|
999
|
+
security_test_files = matched
|
|
1000
|
+
|
|
1001
|
+
count = len(security_test_files)
|
|
1002
|
+
if count >= 2:
|
|
1003
|
+
return {
|
|
1004
|
+
"status": "satisfied",
|
|
1005
|
+
"evidence": (
|
|
1006
|
+
f"Security test coverage: {count} security-specific test file(s) found."
|
|
1007
|
+
),
|
|
1008
|
+
"details": "; ".join(
|
|
1009
|
+
os.path.basename(f) for f in security_test_files[:5]
|
|
1010
|
+
),
|
|
1011
|
+
}
|
|
1012
|
+
elif count == 1:
|
|
1013
|
+
return {
|
|
1014
|
+
"status": "partially_satisfied",
|
|
1015
|
+
"evidence": (
|
|
1016
|
+
"Security test coverage: only 1 security-specific test file found. "
|
|
1017
|
+
"At least 2 required for full compliance."
|
|
1018
|
+
),
|
|
1019
|
+
"details": os.path.basename(security_test_files[0]),
|
|
1020
|
+
}
|
|
1021
|
+
else:
|
|
1022
|
+
return {
|
|
1023
|
+
"status": "not_satisfied",
|
|
1024
|
+
"evidence": "No security-specific test files detected.",
|
|
1025
|
+
"details": (
|
|
1026
|
+
"Expected: test files containing security, auth, injection, "
|
|
1027
|
+
"xss, csrf, permission, or access patterns."
|
|
1028
|
+
),
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
def _check_bdd_coverage(project_dir):
|
|
1033
|
+
"""IVV-15: BDD Feature Coverage.
|
|
1034
|
+
|
|
1035
|
+
Count .feature files and compare to step implementation files.
|
|
1036
|
+
Satisfied if all features have steps; partially if >70%.
|
|
1037
|
+
"""
|
|
1038
|
+
project_path = Path(project_dir)
|
|
1039
|
+
feature_files = list(project_path.rglob("*.feature"))
|
|
1040
|
+
|
|
1041
|
+
if not feature_files:
|
|
1042
|
+
return {
|
|
1043
|
+
"status": "not_satisfied",
|
|
1044
|
+
"evidence": "No .feature files found for BDD coverage assessment.",
|
|
1045
|
+
"details": "Cannot verify BDD coverage without Gherkin feature files.",
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
# Find all step definition files
|
|
1049
|
+
step_file_stems = set()
|
|
1050
|
+
for steps_dir in project_path.rglob("steps"):
|
|
1051
|
+
if steps_dir.is_dir():
|
|
1052
|
+
for sf in steps_dir.iterdir():
|
|
1053
|
+
if sf.suffix in (".py", ".js", ".ts", ".rb"):
|
|
1054
|
+
step_file_stems.add(sf.stem.lower())
|
|
1055
|
+
|
|
1056
|
+
for sf in project_path.rglob("*_steps.py"):
|
|
1057
|
+
step_file_stems.add(sf.stem.lower())
|
|
1058
|
+
for sf in project_path.rglob("*_steps.js"):
|
|
1059
|
+
step_file_stems.add(sf.stem.lower())
|
|
1060
|
+
for sf in project_path.rglob("*_steps.rb"):
|
|
1061
|
+
step_file_stems.add(sf.stem.lower())
|
|
1062
|
+
|
|
1063
|
+
matched = 0
|
|
1064
|
+
unmatched = []
|
|
1065
|
+
|
|
1066
|
+
for feat in feature_files:
|
|
1067
|
+
feat_stem = feat.stem.lower()
|
|
1068
|
+
candidates = [
|
|
1069
|
+
feat_stem,
|
|
1070
|
+
f"{feat_stem}_steps",
|
|
1071
|
+
f"steps_{feat_stem}",
|
|
1072
|
+
f"test_{feat_stem}",
|
|
1073
|
+
f"{feat_stem}_step_defs",
|
|
1074
|
+
]
|
|
1075
|
+
if any(c in step_file_stems for c in candidates):
|
|
1076
|
+
matched += 1
|
|
1077
|
+
else:
|
|
1078
|
+
unmatched.append(feat.name)
|
|
1079
|
+
|
|
1080
|
+
total = len(feature_files)
|
|
1081
|
+
ratio = matched / total if total > 0 else 0
|
|
1082
|
+
|
|
1083
|
+
if ratio >= 1.0:
|
|
1084
|
+
return {
|
|
1085
|
+
"status": "satisfied",
|
|
1086
|
+
"evidence": (
|
|
1087
|
+
f"BDD coverage: all {total} feature file(s) have "
|
|
1088
|
+
f"corresponding step implementations."
|
|
1089
|
+
),
|
|
1090
|
+
"details": "Full BDD coverage achieved.",
|
|
1091
|
+
}
|
|
1092
|
+
elif ratio > 0.7:
|
|
1093
|
+
return {
|
|
1094
|
+
"status": "partially_satisfied",
|
|
1095
|
+
"evidence": (
|
|
1096
|
+
f"BDD coverage: {matched}/{total} features ({ratio:.0%}) "
|
|
1097
|
+
f"have step implementations."
|
|
1098
|
+
),
|
|
1099
|
+
"details": (
|
|
1100
|
+
f"Threshold: 100% for full compliance. "
|
|
1101
|
+
f"Missing steps for: {', '.join(unmatched[:5])}"
|
|
1102
|
+
),
|
|
1103
|
+
}
|
|
1104
|
+
else:
|
|
1105
|
+
return {
|
|
1106
|
+
"status": "not_satisfied",
|
|
1107
|
+
"evidence": (
|
|
1108
|
+
f"BDD coverage: only {matched}/{total} features ({ratio:.0%}) "
|
|
1109
|
+
f"have step implementations."
|
|
1110
|
+
),
|
|
1111
|
+
"details": (
|
|
1112
|
+
f"Majority of features lack step definitions. "
|
|
1113
|
+
f"Missing: {', '.join(unmatched[:10])}"
|
|
1114
|
+
),
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
def _check_e2e_tests(project_dir):
|
|
1119
|
+
"""IVV-17: End-to-End Verification.
|
|
1120
|
+
|
|
1121
|
+
Look for e2e/ directory, integration/ directory, playwright.config.*,
|
|
1122
|
+
cypress.config.*, selenium config.
|
|
1123
|
+
Satisfied if E2E test infrastructure found.
|
|
1124
|
+
"""
|
|
1125
|
+
found_dirs = _dir_or_file_exists(
|
|
1126
|
+
project_dir,
|
|
1127
|
+
dir_names=[
|
|
1128
|
+
"e2e", "integration", "end-to-end", "end_to_end",
|
|
1129
|
+
"tests/e2e", "tests/integration", "test/e2e", "test/integration",
|
|
1130
|
+
],
|
|
1131
|
+
)
|
|
1132
|
+
found_files = _dir_or_file_exists(
|
|
1133
|
+
project_dir,
|
|
1134
|
+
glob_patterns=[
|
|
1135
|
+
"playwright.config.*", "cypress.config.*", "cypress.json",
|
|
1136
|
+
"wdio.conf.*", "protractor.conf.*", "selenium.conf*",
|
|
1137
|
+
"nightwatch.conf.*", "testcafe*",
|
|
1138
|
+
],
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
all_found = list(set(found_dirs + found_files))
|
|
1142
|
+
if all_found:
|
|
1143
|
+
return {
|
|
1144
|
+
"status": "satisfied",
|
|
1145
|
+
"evidence": (
|
|
1146
|
+
f"E2E test infrastructure found: {len(all_found)} artifact(s)."
|
|
1147
|
+
),
|
|
1148
|
+
"details": "; ".join(
|
|
1149
|
+
os.path.basename(f) for f in all_found[:5]
|
|
1150
|
+
),
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
# Check for E2E-flavored test files
|
|
1154
|
+
e2e_patterns = [
|
|
1155
|
+
r"class\s+\w*E2E\w*Test|class\s+\w*Integration\w*Test",
|
|
1156
|
+
r"describe.*['\"]e2e|describe.*['\"]integration|describe.*['\"]end.to.end",
|
|
1157
|
+
r"def\s+test_.*(?:e2e|integration|end_to_end|workflow|journey)",
|
|
1158
|
+
]
|
|
1159
|
+
matched, _ = _scan_files(
|
|
1160
|
+
project_dir, (".py", ".js", ".ts"), e2e_patterns
|
|
1161
|
+
)
|
|
1162
|
+
if matched:
|
|
1163
|
+
return {
|
|
1164
|
+
"status": "satisfied",
|
|
1165
|
+
"evidence": (
|
|
1166
|
+
f"E2E test patterns found in {len(matched)} file(s)."
|
|
1167
|
+
),
|
|
1168
|
+
"details": "; ".join(
|
|
1169
|
+
os.path.basename(f) for f in matched[:5]
|
|
1170
|
+
),
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
return {
|
|
1174
|
+
"status": "not_satisfied",
|
|
1175
|
+
"evidence": "No E2E test infrastructure or integration test directories detected.",
|
|
1176
|
+
"details": (
|
|
1177
|
+
"Expected: e2e/ directory, integration/ directory, or "
|
|
1178
|
+
"E2E test framework configuration (Playwright, Cypress, Selenium)."
|
|
1179
|
+
),
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
def _check_rtm_exists(project_dir):
|
|
1184
|
+
"""IVV-19: RTM Completeness.
|
|
1185
|
+
|
|
1186
|
+
Look for traceability_matrix.md, rtm.md, RTM.*, requirements-traceability.*,
|
|
1187
|
+
trace-matrix.*. Also check compliance/rtm/ directory.
|
|
1188
|
+
Satisfied if RTM artifact exists.
|
|
1189
|
+
"""
|
|
1190
|
+
found_files = _dir_or_file_exists(
|
|
1191
|
+
project_dir,
|
|
1192
|
+
glob_patterns=[
|
|
1193
|
+
"traceability_matrix*", "traceability-matrix*",
|
|
1194
|
+
"rtm.md", "RTM.md", "RTM.*", "rtm.*",
|
|
1195
|
+
"requirements-traceability*", "requirements_traceability*",
|
|
1196
|
+
"trace-matrix*", "trace_matrix*",
|
|
1197
|
+
"traceability*",
|
|
1198
|
+
],
|
|
1199
|
+
)
|
|
1200
|
+
found_dirs = _dir_or_file_exists(
|
|
1201
|
+
project_dir,
|
|
1202
|
+
dir_names=[
|
|
1203
|
+
"rtm", "compliance/rtm", "docs/rtm",
|
|
1204
|
+
"traceability", "docs/traceability",
|
|
1205
|
+
],
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
all_found = list(set(found_files + found_dirs))
|
|
1209
|
+
if all_found:
|
|
1210
|
+
return {
|
|
1211
|
+
"status": "satisfied",
|
|
1212
|
+
"evidence": (
|
|
1213
|
+
f"Requirements Traceability Matrix found: {len(all_found)} artifact(s)."
|
|
1214
|
+
),
|
|
1215
|
+
"details": "; ".join(
|
|
1216
|
+
os.path.basename(f) for f in all_found[:5]
|
|
1217
|
+
),
|
|
1218
|
+
}
|
|
1219
|
+
return {
|
|
1220
|
+
"status": "not_satisfied",
|
|
1221
|
+
"evidence": "No Requirements Traceability Matrix (RTM) detected.",
|
|
1222
|
+
"details": (
|
|
1223
|
+
"Expected: traceability_matrix.md, rtm.md, RTM.*, "
|
|
1224
|
+
"requirements-traceability.*, or compliance/rtm/ directory."
|
|
1225
|
+
),
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
def _check_pipeline_security(project_dir):
|
|
1230
|
+
"""IVV-25: Pipeline Security.
|
|
1231
|
+
|
|
1232
|
+
Look for CI/CD config files and scan for security stages.
|
|
1233
|
+
Satisfied if CI pipeline has security stages; partially if CI exists
|
|
1234
|
+
but no security stages.
|
|
1235
|
+
"""
|
|
1236
|
+
ci_files = _dir_or_file_exists(
|
|
1237
|
+
project_dir,
|
|
1238
|
+
glob_patterns=[
|
|
1239
|
+
".gitlab-ci.yml", ".github/workflows/*.yml",
|
|
1240
|
+
".github/workflows/*.yaml", "Jenkinsfile",
|
|
1241
|
+
"azure-pipelines.yml", ".circleci/config.yml",
|
|
1242
|
+
"bitbucket-pipelines.yml", ".buildkite/pipeline.yml",
|
|
1243
|
+
],
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
if not ci_files:
|
|
1247
|
+
return {
|
|
1248
|
+
"status": "not_satisfied",
|
|
1249
|
+
"evidence": "No CI/CD pipeline configuration files detected.",
|
|
1250
|
+
"details": (
|
|
1251
|
+
"Expected: .gitlab-ci.yml, .github/workflows/*.yml, "
|
|
1252
|
+
"Jenkinsfile, or azure-pipelines.yml."
|
|
1253
|
+
),
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
# Scan CI files for security-related stages/jobs
|
|
1257
|
+
security_patterns = [
|
|
1258
|
+
r"\bsast\b|static.analysis|security.scan",
|
|
1259
|
+
r"\bbandit\b|\btrivy\b|\bsnyk\b|\bgrype\b|\bsemgrep\b",
|
|
1260
|
+
r"security|vulnerability|audit|secret.detect",
|
|
1261
|
+
r"container.scan|image.scan|dependency.check",
|
|
1262
|
+
]
|
|
1263
|
+
security_found = []
|
|
1264
|
+
for ci_path in ci_files:
|
|
1265
|
+
try:
|
|
1266
|
+
with open(ci_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
1267
|
+
content = f.read()
|
|
1268
|
+
for pattern in security_patterns:
|
|
1269
|
+
if re.search(pattern, content, re.IGNORECASE):
|
|
1270
|
+
security_found.append(ci_path)
|
|
1271
|
+
break
|
|
1272
|
+
except Exception:
|
|
1273
|
+
continue
|
|
1274
|
+
|
|
1275
|
+
if security_found:
|
|
1276
|
+
return {
|
|
1277
|
+
"status": "satisfied",
|
|
1278
|
+
"evidence": (
|
|
1279
|
+
f"Pipeline security stages found in {len(security_found)} "
|
|
1280
|
+
f"CI/CD configuration file(s)."
|
|
1281
|
+
),
|
|
1282
|
+
"details": "; ".join(
|
|
1283
|
+
os.path.basename(f) for f in security_found[:5]
|
|
1284
|
+
),
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
return {
|
|
1288
|
+
"status": "partially_satisfied",
|
|
1289
|
+
"evidence": (
|
|
1290
|
+
f"CI/CD configuration found ({len(ci_files)} file(s)) but "
|
|
1291
|
+
f"no security stages detected."
|
|
1292
|
+
),
|
|
1293
|
+
"details": (
|
|
1294
|
+
"Pipeline exists but lacks security stages. Expected: SAST, "
|
|
1295
|
+
"dependency audit, secret detection, or container scanning stages."
|
|
1296
|
+
),
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
def _check_artifact_integrity(project_dir):
|
|
1301
|
+
"""IVV-26: Artifact Integrity.
|
|
1302
|
+
|
|
1303
|
+
Look for SBOM files, checksums (SHA256SUMS, *.sha256, *.sig),
|
|
1304
|
+
signed artifacts. Also check for cosign, sigstore, GPG signature files.
|
|
1305
|
+
Satisfied if integrity artifacts found.
|
|
1306
|
+
"""
|
|
1307
|
+
# SBOM files
|
|
1308
|
+
sbom_files = _dir_or_file_exists(
|
|
1309
|
+
project_dir,
|
|
1310
|
+
glob_patterns=[
|
|
1311
|
+
"*sbom*.json", "*bom*.xml", "*sbom*.xml",
|
|
1312
|
+
"*cyclonedx*", "*spdx*",
|
|
1313
|
+
],
|
|
1314
|
+
)
|
|
1315
|
+
|
|
1316
|
+
# Checksum and signature files
|
|
1317
|
+
integrity_files = _dir_or_file_exists(
|
|
1318
|
+
project_dir,
|
|
1319
|
+
glob_patterns=[
|
|
1320
|
+
"SHA256SUMS", "*.sha256", "*.sha512", "*.sig",
|
|
1321
|
+
"*.asc", "*.gpg", "checksums*", "CHECKSUMS*",
|
|
1322
|
+
"*cosign*", "*sigstore*", "*.intoto.jsonl",
|
|
1323
|
+
"*.provenance", "*provenance*.json",
|
|
1324
|
+
],
|
|
1325
|
+
)
|
|
1326
|
+
|
|
1327
|
+
all_found = list(set(sbom_files + integrity_files))
|
|
1328
|
+
if all_found:
|
|
1329
|
+
return {
|
|
1330
|
+
"status": "satisfied",
|
|
1331
|
+
"evidence": (
|
|
1332
|
+
f"Artifact integrity mechanisms found: {len(all_found)} artifact(s)."
|
|
1333
|
+
),
|
|
1334
|
+
"details": "; ".join(
|
|
1335
|
+
os.path.basename(f) for f in all_found[:5]
|
|
1336
|
+
),
|
|
1337
|
+
}
|
|
1338
|
+
return {
|
|
1339
|
+
"status": "not_satisfied",
|
|
1340
|
+
"evidence": "No artifact integrity mechanisms detected (SBOM, checksums, signatures).",
|
|
1341
|
+
"details": (
|
|
1342
|
+
"Expected: SBOM files, SHA256SUMS, *.sha256, *.sig, "
|
|
1343
|
+
"cosign signatures, or provenance attestations."
|
|
1344
|
+
),
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
def _check_config_hardening(project_dir):
|
|
1349
|
+
"""IVV-27: Configuration Hardening.
|
|
1350
|
+
|
|
1351
|
+
Check Dockerfiles for STIG hardening: non-root USER, drop ALL capabilities,
|
|
1352
|
+
read-only rootfs, minimal base image.
|
|
1353
|
+
Satisfied if all Dockerfiles hardened; partially if some; not_satisfied if none.
|
|
1354
|
+
"""
|
|
1355
|
+
dockerfiles = _dir_or_file_exists(
|
|
1356
|
+
project_dir,
|
|
1357
|
+
glob_patterns=["Dockerfile*", "*.dockerfile"],
|
|
1358
|
+
)
|
|
1359
|
+
if not dockerfiles:
|
|
1360
|
+
return {
|
|
1361
|
+
"status": "not_satisfied",
|
|
1362
|
+
"evidence": "No Dockerfiles found in the project.",
|
|
1363
|
+
"details": "Cannot verify configuration hardening without container definitions.",
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
hardened_count = 0
|
|
1367
|
+
hardening_evidence = []
|
|
1368
|
+
|
|
1369
|
+
for df_path in dockerfiles:
|
|
1370
|
+
try:
|
|
1371
|
+
with open(df_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
1372
|
+
content = f.read()
|
|
1373
|
+
except Exception:
|
|
1374
|
+
continue
|
|
1375
|
+
|
|
1376
|
+
checks = {
|
|
1377
|
+
"non_root_user": bool(
|
|
1378
|
+
re.search(r"USER\s+(?!root)\S+", content)
|
|
1379
|
+
),
|
|
1380
|
+
"drop_capabilities": bool(
|
|
1381
|
+
re.search(
|
|
1382
|
+
r"drop.*ALL|securityContext.*drop|cap_drop",
|
|
1383
|
+
content, re.IGNORECASE | re.DOTALL,
|
|
1384
|
+
)
|
|
1385
|
+
),
|
|
1386
|
+
"read_only_rootfs": bool(
|
|
1387
|
+
re.search(
|
|
1388
|
+
r"readOnlyRootFilesystem|read.only",
|
|
1389
|
+
content, re.IGNORECASE,
|
|
1390
|
+
)
|
|
1391
|
+
),
|
|
1392
|
+
"minimal_base": bool(
|
|
1393
|
+
re.search(
|
|
1394
|
+
r"FROM.*(:slim|:alpine|-slim|-minimal|distroless|hardened)",
|
|
1395
|
+
content, re.IGNORECASE,
|
|
1396
|
+
)
|
|
1397
|
+
),
|
|
1398
|
+
}
|
|
1399
|
+
passed = sum(checks.values())
|
|
1400
|
+
if passed >= 2:
|
|
1401
|
+
hardened_count += 1
|
|
1402
|
+
hardening_evidence.append(
|
|
1403
|
+
f"{os.path.basename(df_path)}: {passed}/4 hardening checks passed"
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
if hardened_count == len(dockerfiles):
|
|
1407
|
+
return {
|
|
1408
|
+
"status": "satisfied",
|
|
1409
|
+
"evidence": (
|
|
1410
|
+
f"All {hardened_count} Dockerfile(s) show STIG hardening patterns."
|
|
1411
|
+
),
|
|
1412
|
+
"details": "; ".join(hardening_evidence),
|
|
1413
|
+
}
|
|
1414
|
+
elif hardened_count > 0:
|
|
1415
|
+
return {
|
|
1416
|
+
"status": "partially_satisfied",
|
|
1417
|
+
"evidence": (
|
|
1418
|
+
f"{hardened_count}/{len(dockerfiles)} Dockerfile(s) show hardening."
|
|
1419
|
+
),
|
|
1420
|
+
"details": "; ".join(hardening_evidence),
|
|
1421
|
+
}
|
|
1422
|
+
return {
|
|
1423
|
+
"status": "not_satisfied",
|
|
1424
|
+
"evidence": "Dockerfiles found but lack STIG hardening patterns.",
|
|
1425
|
+
"details": (
|
|
1426
|
+
"Expected: non-root USER, drop ALL capabilities, "
|
|
1427
|
+
"read-only rootfs, minimal base image."
|
|
1428
|
+
),
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
|
|
1432
|
+
def _check_rollback_capability(project_dir):
|
|
1433
|
+
"""IVV-28: Rollback Capability.
|
|
1434
|
+
|
|
1435
|
+
Look for rollback scripts, K8s deployment with rollout strategy,
|
|
1436
|
+
blue-green/canary patterns. Scan for rollback, kubectl rollout undo,
|
|
1437
|
+
deployment strategy, revisionHistoryLimit.
|
|
1438
|
+
Satisfied if rollback mechanism found.
|
|
1439
|
+
"""
|
|
1440
|
+
# Look for explicit rollback scripts/docs
|
|
1441
|
+
found_files = _dir_or_file_exists(
|
|
1442
|
+
project_dir,
|
|
1443
|
+
glob_patterns=[
|
|
1444
|
+
"*rollback*", "rollback.*", "rollback_*.py",
|
|
1445
|
+
"rollback_*.sh", "*rollback*.yml", "*rollback*.yaml",
|
|
1446
|
+
],
|
|
1447
|
+
)
|
|
1448
|
+
if found_files:
|
|
1449
|
+
return {
|
|
1450
|
+
"status": "satisfied",
|
|
1451
|
+
"evidence": (
|
|
1452
|
+
f"Rollback artifacts found: {len(found_files)} item(s)."
|
|
1453
|
+
),
|
|
1454
|
+
"details": "; ".join(
|
|
1455
|
+
os.path.basename(f) for f in found_files[:5]
|
|
1456
|
+
),
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
# Scan K8s manifests and IaC for rollback patterns
|
|
1460
|
+
rollback_patterns = [
|
|
1461
|
+
r"rollback|roll.back",
|
|
1462
|
+
r"kubectl.*rollout.*undo|rollout.*restart",
|
|
1463
|
+
r"strategy:\s*(RollingUpdate|Recreate|BlueGreen|Canary)",
|
|
1464
|
+
r"revisionHistoryLimit",
|
|
1465
|
+
r"blue.green|canary|rolling.update",
|
|
1466
|
+
r"deployment.*strategy|maxUnavailable|maxSurge",
|
|
1467
|
+
]
|
|
1468
|
+
extensions = (".yaml", ".yml", ".tf", ".py", ".sh", ".json")
|
|
1469
|
+
matched, total = _scan_files(project_dir, extensions, rollback_patterns)
|
|
1470
|
+
|
|
1471
|
+
if matched:
|
|
1472
|
+
return {
|
|
1473
|
+
"status": "satisfied",
|
|
1474
|
+
"evidence": (
|
|
1475
|
+
f"Rollback patterns found in {len(matched)} file(s)."
|
|
1476
|
+
),
|
|
1477
|
+
"details": "; ".join(
|
|
1478
|
+
os.path.basename(f) for f in matched[:5]
|
|
1479
|
+
),
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
if total == 0:
|
|
1483
|
+
return {
|
|
1484
|
+
"status": "not_satisfied",
|
|
1485
|
+
"evidence": "No deployment or infrastructure files found to assess.",
|
|
1486
|
+
"details": "Project directory lacks YAML, Terraform, or deployment scripts.",
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
return {
|
|
1490
|
+
"status": "not_satisfied",
|
|
1491
|
+
"evidence": "No rollback mechanism detected in deployment configuration.",
|
|
1492
|
+
"details": (
|
|
1493
|
+
"Expected: rollback scripts, kubectl rollout undo, "
|
|
1494
|
+
"deployment strategy, or revisionHistoryLimit in K8s manifests."
|
|
1495
|
+
),
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
# -----------------------------------------------------------------
|
|
1500
|
+
# Requirement-to-check mapping
|
|
1501
|
+
# -----------------------------------------------------------------
|
|
1502
|
+
|
|
1503
|
+
AUTO_CHECKS = {
|
|
1504
|
+
"IVV-01": _check_req_completeness,
|
|
1505
|
+
"IVV-02": _check_req_consistency,
|
|
1506
|
+
"IVV-03": _check_req_testability,
|
|
1507
|
+
"IVV-05": _check_design_architecture,
|
|
1508
|
+
"IVV-08": _check_independent_sast,
|
|
1509
|
+
"IVV-09": _check_coding_standards,
|
|
1510
|
+
"IVV-10": _check_code_review_completion,
|
|
1511
|
+
"IVV-11": _check_complexity_metrics,
|
|
1512
|
+
"IVV-12": _check_test_coverage,
|
|
1513
|
+
"IVV-13": _check_test_plan,
|
|
1514
|
+
"IVV-14": _check_security_tests,
|
|
1515
|
+
"IVV-15": _check_bdd_coverage,
|
|
1516
|
+
"IVV-17": _check_e2e_tests,
|
|
1517
|
+
"IVV-19": _check_rtm_exists,
|
|
1518
|
+
"IVV-25": _check_pipeline_security,
|
|
1519
|
+
"IVV-26": _check_artifact_integrity,
|
|
1520
|
+
"IVV-27": _check_config_hardening,
|
|
1521
|
+
"IVV-28": _check_rollback_capability,
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
# -----------------------------------------------------------------
|
|
1526
|
+
# Status mapping helpers
|
|
1527
|
+
# -----------------------------------------------------------------
|
|
1528
|
+
|
|
1529
|
+
def _map_check_status(check_status):
|
|
1530
|
+
"""Map auto-check result status to IV&V assessment status.
|
|
1531
|
+
|
|
1532
|
+
IV&V assessment status values: pass, fail, partial, not_applicable,
|
|
1533
|
+
deferred, not_assessed.
|
|
1534
|
+
"""
|
|
1535
|
+
mapping = {
|
|
1536
|
+
"satisfied": "pass",
|
|
1537
|
+
"partially_satisfied": "partial",
|
|
1538
|
+
"not_satisfied": "fail",
|
|
1539
|
+
"not_applicable": "not_applicable",
|
|
1540
|
+
}
|
|
1541
|
+
return mapping.get(check_status, "not_assessed")
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
def _map_priority_to_severity(priority):
|
|
1545
|
+
"""Map requirement priority to finding severity.
|
|
1546
|
+
|
|
1547
|
+
Priority: critical, high, medium, low
|
|
1548
|
+
Severity: critical, high, moderate, low
|
|
1549
|
+
"""
|
|
1550
|
+
mapping = {
|
|
1551
|
+
"critical": "critical",
|
|
1552
|
+
"high": "high",
|
|
1553
|
+
"medium": "moderate",
|
|
1554
|
+
"low": "low",
|
|
1555
|
+
}
|
|
1556
|
+
return mapping.get(priority, "moderate")
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
# -----------------------------------------------------------------
|
|
1560
|
+
# Core assessment function
|
|
1561
|
+
# -----------------------------------------------------------------
|
|
1562
|
+
|
|
1563
|
+
def run_ivv_assessment(
|
|
1564
|
+
project_id,
|
|
1565
|
+
process_area="all",
|
|
1566
|
+
project_dir=None,
|
|
1567
|
+
gate=False,
|
|
1568
|
+
output_path=None,
|
|
1569
|
+
db_path=None,
|
|
1570
|
+
):
|
|
1571
|
+
"""Run IV&V assessment per IEEE 1012 and DoD standards.
|
|
1572
|
+
|
|
1573
|
+
Args:
|
|
1574
|
+
project_id: The project identifier.
|
|
1575
|
+
process_area: Filter to a specific process area or "all".
|
|
1576
|
+
project_dir: Project directory for automated file-based checks.
|
|
1577
|
+
gate: If True, evaluate the IV&V gate (0 critical findings = pass).
|
|
1578
|
+
output_path: Override output directory for the assessment report.
|
|
1579
|
+
db_path: Override database path.
|
|
1580
|
+
|
|
1581
|
+
Returns:
|
|
1582
|
+
Dict with assessment results, summary, scores, gate result,
|
|
1583
|
+
and output file path.
|
|
1584
|
+
"""
|
|
1585
|
+
conn = _get_connection(db_path)
|
|
1586
|
+
try:
|
|
1587
|
+
project = _get_project(conn, project_id)
|
|
1588
|
+
|
|
1589
|
+
# Load IV&V requirements catalog
|
|
1590
|
+
ivv_data = _load_ivv_requirements()
|
|
1591
|
+
metadata = ivv_data.get("metadata", {})
|
|
1592
|
+
requirements = ivv_data.get("requirements", [])
|
|
1593
|
+
|
|
1594
|
+
# Filter by process area if specified
|
|
1595
|
+
if process_area != "all":
|
|
1596
|
+
requirements = [
|
|
1597
|
+
r for r in requirements
|
|
1598
|
+
if r["process_area"] == process_area
|
|
1599
|
+
]
|
|
1600
|
+
if not requirements:
|
|
1601
|
+
raise ValueError(
|
|
1602
|
+
f"No requirements found for process area '{process_area}'. "
|
|
1603
|
+
f"Valid areas: {', '.join(PROCESS_AREAS)}."
|
|
1604
|
+
)
|
|
1605
|
+
|
|
1606
|
+
# Resolve project directory for auto-checks
|
|
1607
|
+
if project_dir and Path(project_dir).is_dir():
|
|
1608
|
+
can_auto_check = True
|
|
1609
|
+
elif (
|
|
1610
|
+
project.get("directory_path")
|
|
1611
|
+
and Path(project["directory_path"]).is_dir()
|
|
1612
|
+
):
|
|
1613
|
+
project_dir = project["directory_path"]
|
|
1614
|
+
can_auto_check = True
|
|
1615
|
+
else:
|
|
1616
|
+
can_auto_check = False
|
|
1617
|
+
|
|
1618
|
+
now = datetime.now(timezone.utc)
|
|
1619
|
+
results = []
|
|
1620
|
+
findings = []
|
|
1621
|
+
|
|
1622
|
+
# -- Assess each requirement --
|
|
1623
|
+
for req in requirements:
|
|
1624
|
+
req_id = req["id"]
|
|
1625
|
+
automation_level = req.get("automation_level", "manual")
|
|
1626
|
+
check_status = "not_assessed"
|
|
1627
|
+
ivv_status = "not_assessed"
|
|
1628
|
+
evidence = ""
|
|
1629
|
+
details = ""
|
|
1630
|
+
notes = ""
|
|
1631
|
+
|
|
1632
|
+
if automation_level == "auto" and can_auto_check:
|
|
1633
|
+
if req_id in AUTO_CHECKS:
|
|
1634
|
+
try:
|
|
1635
|
+
check_result = AUTO_CHECKS[req_id](project_dir)
|
|
1636
|
+
check_status = check_result["status"]
|
|
1637
|
+
ivv_status = _map_check_status(check_status)
|
|
1638
|
+
evidence = check_result["evidence"]
|
|
1639
|
+
details = check_result.get("details", "")
|
|
1640
|
+
except Exception as e:
|
|
1641
|
+
ivv_status = "not_assessed"
|
|
1642
|
+
evidence = f"Auto-check error: {e}"
|
|
1643
|
+
notes = "Auto-check failed; manual review required."
|
|
1644
|
+
else:
|
|
1645
|
+
ivv_status = "not_assessed"
|
|
1646
|
+
evidence = (
|
|
1647
|
+
"No automated check implemented for this requirement."
|
|
1648
|
+
)
|
|
1649
|
+
notes = "Manual review required."
|
|
1650
|
+
|
|
1651
|
+
elif automation_level == "auto" and not can_auto_check:
|
|
1652
|
+
ivv_status = "not_assessed"
|
|
1653
|
+
evidence = (
|
|
1654
|
+
"No project directory available for automated scanning."
|
|
1655
|
+
)
|
|
1656
|
+
notes = "Provide --project-dir to enable auto-checks."
|
|
1657
|
+
|
|
1658
|
+
elif automation_level == "semi" and can_auto_check:
|
|
1659
|
+
if req_id in AUTO_CHECKS:
|
|
1660
|
+
try:
|
|
1661
|
+
check_result = AUTO_CHECKS[req_id](project_dir)
|
|
1662
|
+
check_status = check_result["status"]
|
|
1663
|
+
ivv_status = _map_check_status(check_status)
|
|
1664
|
+
evidence = check_result["evidence"]
|
|
1665
|
+
details = check_result.get("details", "")
|
|
1666
|
+
notes = (
|
|
1667
|
+
"Semi-automated check completed. "
|
|
1668
|
+
"Manual review required to verify full compliance."
|
|
1669
|
+
)
|
|
1670
|
+
except Exception as e:
|
|
1671
|
+
ivv_status = "not_assessed"
|
|
1672
|
+
evidence = f"Partial auto-check error: {e}"
|
|
1673
|
+
notes = (
|
|
1674
|
+
"Semi-automated check failed; "
|
|
1675
|
+
"full manual review required."
|
|
1676
|
+
)
|
|
1677
|
+
else:
|
|
1678
|
+
ivv_status = "not_assessed"
|
|
1679
|
+
evidence = (
|
|
1680
|
+
"Semi-automated: no automated component implemented."
|
|
1681
|
+
)
|
|
1682
|
+
notes = (
|
|
1683
|
+
f"Manual review required. Evidence needed: "
|
|
1684
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1685
|
+
)
|
|
1686
|
+
|
|
1687
|
+
elif automation_level == "semi" and not can_auto_check:
|
|
1688
|
+
ivv_status = "not_assessed"
|
|
1689
|
+
evidence = (
|
|
1690
|
+
"Semi-automated check requires project directory."
|
|
1691
|
+
)
|
|
1692
|
+
notes = (
|
|
1693
|
+
f"Manual review required. Evidence needed: "
|
|
1694
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1695
|
+
)
|
|
1696
|
+
|
|
1697
|
+
else:
|
|
1698
|
+
# manual automation_level
|
|
1699
|
+
ivv_status = "not_assessed"
|
|
1700
|
+
evidence = "Manual assessment required."
|
|
1701
|
+
notes = (
|
|
1702
|
+
f"This requirement must be verified manually. "
|
|
1703
|
+
f"Evidence needed: "
|
|
1704
|
+
f"{req.get('evidence_required', 'See requirement description.')}"
|
|
1705
|
+
)
|
|
1706
|
+
|
|
1707
|
+
result_entry = {
|
|
1708
|
+
"requirement_id": req_id,
|
|
1709
|
+
"process_area": req["process_area"],
|
|
1710
|
+
"process_area_code": req.get("process_area_code", ""),
|
|
1711
|
+
"title": req["title"],
|
|
1712
|
+
"description": req["description"],
|
|
1713
|
+
"verification_type": req.get("verification_type", "verification"),
|
|
1714
|
+
"priority": req.get("priority", "medium"),
|
|
1715
|
+
"automation_level": automation_level,
|
|
1716
|
+
"nist_controls": req.get("nist_controls", []),
|
|
1717
|
+
"status": ivv_status,
|
|
1718
|
+
"evidence": evidence,
|
|
1719
|
+
"details": details,
|
|
1720
|
+
"notes": notes,
|
|
1721
|
+
}
|
|
1722
|
+
results.append(result_entry)
|
|
1723
|
+
|
|
1724
|
+
# -- Upsert into ivv_assessments table --
|
|
1725
|
+
try:
|
|
1726
|
+
conn.execute(
|
|
1727
|
+
"""INSERT OR REPLACE INTO ivv_assessments
|
|
1728
|
+
(project_id, assessment_date, assessor, process_area,
|
|
1729
|
+
verification_type, requirement_id, status,
|
|
1730
|
+
evidence_description, evidence_path,
|
|
1731
|
+
automation_result, notes, updated_at)
|
|
1732
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1733
|
+
(
|
|
1734
|
+
project_id,
|
|
1735
|
+
now.isoformat(),
|
|
1736
|
+
"icdev-ivv-engine",
|
|
1737
|
+
req["process_area"],
|
|
1738
|
+
req.get("verification_type", "verification"),
|
|
1739
|
+
req_id,
|
|
1740
|
+
ivv_status,
|
|
1741
|
+
evidence,
|
|
1742
|
+
details if details else None,
|
|
1743
|
+
json.dumps({
|
|
1744
|
+
"automation_level": automation_level,
|
|
1745
|
+
"check_function": (
|
|
1746
|
+
AUTO_CHECKS[req_id].__name__
|
|
1747
|
+
if req_id in AUTO_CHECKS
|
|
1748
|
+
else None
|
|
1749
|
+
),
|
|
1750
|
+
}),
|
|
1751
|
+
notes if notes else None,
|
|
1752
|
+
now.isoformat(),
|
|
1753
|
+
),
|
|
1754
|
+
)
|
|
1755
|
+
except Exception as e:
|
|
1756
|
+
print(
|
|
1757
|
+
f"Warning: Could not upsert assessment for {req_id}: {e}",
|
|
1758
|
+
file=sys.stderr,
|
|
1759
|
+
)
|
|
1760
|
+
|
|
1761
|
+
# -- Generate findings for failed checks --
|
|
1762
|
+
if ivv_status == "fail":
|
|
1763
|
+
finding_id = (
|
|
1764
|
+
f"IVV-F-{project_id[:8]}-{req_id}-"
|
|
1765
|
+
f"{now.strftime('%Y%m%d')}"
|
|
1766
|
+
)
|
|
1767
|
+
severity = _map_priority_to_severity(
|
|
1768
|
+
req.get("priority", "medium")
|
|
1769
|
+
)
|
|
1770
|
+
finding = {
|
|
1771
|
+
"finding_id": finding_id,
|
|
1772
|
+
"severity": severity,
|
|
1773
|
+
"process_area": req["process_area"],
|
|
1774
|
+
"title": f"IV&V Finding: {req['title']}",
|
|
1775
|
+
"description": (
|
|
1776
|
+
f"Requirement {req_id} ({req['title']}) failed "
|
|
1777
|
+
f"IV&V assessment. {evidence}"
|
|
1778
|
+
),
|
|
1779
|
+
"recommendation": (
|
|
1780
|
+
f"Address the following: "
|
|
1781
|
+
f"{req.get('evidence_required', req['description'])}"
|
|
1782
|
+
),
|
|
1783
|
+
}
|
|
1784
|
+
findings.append(finding)
|
|
1785
|
+
|
|
1786
|
+
try:
|
|
1787
|
+
conn.execute(
|
|
1788
|
+
"""INSERT OR IGNORE INTO ivv_findings
|
|
1789
|
+
(project_id, finding_id, severity, process_area,
|
|
1790
|
+
title, description, recommendation, status)
|
|
1791
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1792
|
+
(
|
|
1793
|
+
project_id,
|
|
1794
|
+
finding_id,
|
|
1795
|
+
severity,
|
|
1796
|
+
req["process_area"],
|
|
1797
|
+
finding["title"],
|
|
1798
|
+
finding["description"],
|
|
1799
|
+
finding["recommendation"],
|
|
1800
|
+
"open",
|
|
1801
|
+
),
|
|
1802
|
+
)
|
|
1803
|
+
except Exception as e:
|
|
1804
|
+
print(
|
|
1805
|
+
f"Warning: Could not insert finding {finding_id}: {e}",
|
|
1806
|
+
file=sys.stderr,
|
|
1807
|
+
)
|
|
1808
|
+
|
|
1809
|
+
conn.commit()
|
|
1810
|
+
|
|
1811
|
+
# -- Build summary by process area (9 areas) --
|
|
1812
|
+
summary = {}
|
|
1813
|
+
for area in PROCESS_AREAS:
|
|
1814
|
+
summary[area] = {
|
|
1815
|
+
"total": 0,
|
|
1816
|
+
"pass": 0,
|
|
1817
|
+
"partial": 0,
|
|
1818
|
+
"fail": 0,
|
|
1819
|
+
"not_assessed": 0,
|
|
1820
|
+
"not_applicable": 0,
|
|
1821
|
+
"deferred": 0,
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
for r in results:
|
|
1825
|
+
area = r["process_area"]
|
|
1826
|
+
if area not in summary:
|
|
1827
|
+
summary[area] = {
|
|
1828
|
+
"total": 0, "pass": 0, "partial": 0,
|
|
1829
|
+
"fail": 0, "not_assessed": 0,
|
|
1830
|
+
"not_applicable": 0, "deferred": 0,
|
|
1831
|
+
}
|
|
1832
|
+
summary[area]["total"] += 1
|
|
1833
|
+
st = r["status"]
|
|
1834
|
+
if st in summary[area]:
|
|
1835
|
+
summary[area][st] += 1
|
|
1836
|
+
|
|
1837
|
+
# -- Calculate scores per area --
|
|
1838
|
+
area_scores = {}
|
|
1839
|
+
for area in PROCESS_AREAS:
|
|
1840
|
+
s = summary.get(area, {})
|
|
1841
|
+
total = s.get("total", 0)
|
|
1842
|
+
na = s.get("not_applicable", 0)
|
|
1843
|
+
deferred = s.get("deferred", 0)
|
|
1844
|
+
assessable = total - na - deferred
|
|
1845
|
+
if assessable > 0:
|
|
1846
|
+
score = 100.0 * (
|
|
1847
|
+
s.get("pass", 0) + s.get("partial", 0) * 0.5
|
|
1848
|
+
) / assessable
|
|
1849
|
+
else:
|
|
1850
|
+
score = 0.0
|
|
1851
|
+
code = PROCESS_AREA_CODES.get(area, area[:4].upper())
|
|
1852
|
+
area_scores[code] = round(score, 1)
|
|
1853
|
+
|
|
1854
|
+
# Verification score: average of REQ, DES, CODE, RTM, SEC, BLD, PROC
|
|
1855
|
+
verification_scores = [
|
|
1856
|
+
area_scores[code]
|
|
1857
|
+
for code in VERIFICATION_AREAS
|
|
1858
|
+
if code in area_scores
|
|
1859
|
+
]
|
|
1860
|
+
verification_score = (
|
|
1861
|
+
round(sum(verification_scores) / len(verification_scores), 1)
|
|
1862
|
+
if verification_scores
|
|
1863
|
+
else 0.0
|
|
1864
|
+
)
|
|
1865
|
+
|
|
1866
|
+
# Validation score: average of TEST, INT
|
|
1867
|
+
validation_scores = [
|
|
1868
|
+
area_scores[code]
|
|
1869
|
+
for code in VALIDATION_AREAS
|
|
1870
|
+
if code in area_scores
|
|
1871
|
+
]
|
|
1872
|
+
validation_score = (
|
|
1873
|
+
round(sum(validation_scores) / len(validation_scores), 1)
|
|
1874
|
+
if validation_scores
|
|
1875
|
+
else 0.0
|
|
1876
|
+
)
|
|
1877
|
+
|
|
1878
|
+
# Overall score: 0.6 * verification + 0.4 * validation
|
|
1879
|
+
overall_score = round(
|
|
1880
|
+
0.6 * verification_score + 0.4 * validation_score, 1
|
|
1881
|
+
)
|
|
1882
|
+
|
|
1883
|
+
# -- Gate evaluation: 0 critical findings = PASS --
|
|
1884
|
+
critical_findings = [
|
|
1885
|
+
f for f in findings if f["severity"] == "critical"
|
|
1886
|
+
]
|
|
1887
|
+
gate_passed = len(critical_findings) == 0
|
|
1888
|
+
gate_result = {
|
|
1889
|
+
"evaluated": gate,
|
|
1890
|
+
"passed": gate_passed,
|
|
1891
|
+
"critical_findings_count": len(critical_findings),
|
|
1892
|
+
"critical_findings": [
|
|
1893
|
+
f"{f['finding_id']}: {f['title']}"
|
|
1894
|
+
for f in critical_findings
|
|
1895
|
+
],
|
|
1896
|
+
"reason": (
|
|
1897
|
+
"PASS: 0 critical IV&V findings"
|
|
1898
|
+
if gate_passed
|
|
1899
|
+
else (
|
|
1900
|
+
f"FAIL: {len(critical_findings)} critical IV&V finding(s): "
|
|
1901
|
+
f"{', '.join(f['finding_id'] for f in critical_findings)}"
|
|
1902
|
+
)
|
|
1903
|
+
),
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
# -- Update ivv_certifications table --
|
|
1907
|
+
open_count = len(findings)
|
|
1908
|
+
critical_count = len(critical_findings)
|
|
1909
|
+
cert_status = "in_progress"
|
|
1910
|
+
if gate_passed and overall_score >= 80.0:
|
|
1911
|
+
cert_status = "submitted"
|
|
1912
|
+
elif not gate_passed:
|
|
1913
|
+
cert_status = "denied"
|
|
1914
|
+
|
|
1915
|
+
try:
|
|
1916
|
+
conn.execute(
|
|
1917
|
+
"""INSERT OR REPLACE INTO ivv_certifications
|
|
1918
|
+
(project_id, certification_type, status,
|
|
1919
|
+
verification_score, validation_score, overall_score,
|
|
1920
|
+
ivv_authority, open_findings_count,
|
|
1921
|
+
critical_findings_count, updated_at)
|
|
1922
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
1923
|
+
(
|
|
1924
|
+
project_id,
|
|
1925
|
+
"IV&V",
|
|
1926
|
+
cert_status,
|
|
1927
|
+
verification_score,
|
|
1928
|
+
validation_score,
|
|
1929
|
+
overall_score,
|
|
1930
|
+
"icdev-ivv-engine",
|
|
1931
|
+
open_count,
|
|
1932
|
+
critical_count,
|
|
1933
|
+
now.isoformat(),
|
|
1934
|
+
),
|
|
1935
|
+
)
|
|
1936
|
+
conn.commit()
|
|
1937
|
+
except Exception as e:
|
|
1938
|
+
print(
|
|
1939
|
+
f"Warning: Could not update ivv_certifications: {e}",
|
|
1940
|
+
file=sys.stderr,
|
|
1941
|
+
)
|
|
1942
|
+
|
|
1943
|
+
# -- Generate CUI-marked Markdown report --
|
|
1944
|
+
cui_config = _load_cui_config()
|
|
1945
|
+
doc_header = cui_config.get(
|
|
1946
|
+
"document_header", "CUI // SP-CTI"
|
|
1947
|
+
).strip()
|
|
1948
|
+
doc_footer = cui_config.get(
|
|
1949
|
+
"document_footer", "CUI // SP-CTI"
|
|
1950
|
+
).strip()
|
|
1951
|
+
|
|
1952
|
+
lines = [
|
|
1953
|
+
doc_header,
|
|
1954
|
+
"",
|
|
1955
|
+
"# IV&V Assessment Report -- IEEE 1012",
|
|
1956
|
+
"",
|
|
1957
|
+
f"**Project:** {project.get('name', project_id)} ({project_id})",
|
|
1958
|
+
f"**Assessment Date:** {now.strftime('%Y-%m-%d %H:%M UTC')}",
|
|
1959
|
+
"**Assessor:** ICDEV IV&V Engine (automated)",
|
|
1960
|
+
f"**Process Area Scope:** {process_area}",
|
|
1961
|
+
"**IEEE 1012 Version:** IEEE 1012-2016",
|
|
1962
|
+
(
|
|
1963
|
+
f"**Source Standards:** "
|
|
1964
|
+
f"{metadata.get('source', 'IEEE 1012-2016, DoDI 5000.87, DoDI 8510.01')}"
|
|
1965
|
+
),
|
|
1966
|
+
"**Classification:** CUI // SP-CTI",
|
|
1967
|
+
"",
|
|
1968
|
+
"---",
|
|
1969
|
+
"",
|
|
1970
|
+
"## Executive Summary",
|
|
1971
|
+
"",
|
|
1972
|
+
]
|
|
1973
|
+
|
|
1974
|
+
# Summary table
|
|
1975
|
+
lines.append(
|
|
1976
|
+
"| Process Area | Total | Pass | Partial | Fail "
|
|
1977
|
+
"| Not Assessed | N/A | Deferred | Score |"
|
|
1978
|
+
)
|
|
1979
|
+
lines.append(
|
|
1980
|
+
"|--------------|-------|------|---------|------"
|
|
1981
|
+
"|--------------|-----|----------|-------|"
|
|
1982
|
+
)
|
|
1983
|
+
|
|
1984
|
+
grand_total = {
|
|
1985
|
+
"total": 0, "pass": 0, "partial": 0,
|
|
1986
|
+
"fail": 0, "not_assessed": 0,
|
|
1987
|
+
"not_applicable": 0, "deferred": 0,
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
for area in PROCESS_AREAS:
|
|
1991
|
+
s = summary.get(area, {})
|
|
1992
|
+
if s.get("total", 0) == 0:
|
|
1993
|
+
continue
|
|
1994
|
+
code = PROCESS_AREA_CODES.get(area, "")
|
|
1995
|
+
score = area_scores.get(code, 0.0)
|
|
1996
|
+
lines.append(
|
|
1997
|
+
f"| {area} | {s['total']} | {s['pass']} | "
|
|
1998
|
+
f"{s['partial']} | {s['fail']} | "
|
|
1999
|
+
f"{s['not_assessed']} | {s['not_applicable']} | "
|
|
2000
|
+
f"{s['deferred']} | {score:.1f}% |"
|
|
2001
|
+
)
|
|
2002
|
+
for key in grand_total:
|
|
2003
|
+
grand_total[key] += s.get(key, 0)
|
|
2004
|
+
|
|
2005
|
+
lines.append(
|
|
2006
|
+
f"| **Total** | **{grand_total['total']}** | "
|
|
2007
|
+
f"**{grand_total['pass']}** | "
|
|
2008
|
+
f"**{grand_total['partial']}** | "
|
|
2009
|
+
f"**{grand_total['fail']}** | "
|
|
2010
|
+
f"**{grand_total['not_assessed']}** | "
|
|
2011
|
+
f"**{grand_total['not_applicable']}** | "
|
|
2012
|
+
f"**{grand_total['deferred']}** | |"
|
|
2013
|
+
)
|
|
2014
|
+
lines.append("")
|
|
2015
|
+
|
|
2016
|
+
# Scores section
|
|
2017
|
+
lines.extend([
|
|
2018
|
+
"## IV&V Scores",
|
|
2019
|
+
"",
|
|
2020
|
+
"| Metric | Score |",
|
|
2021
|
+
"|--------|-------|",
|
|
2022
|
+
f"| Verification Score | {verification_score:.1f}% |",
|
|
2023
|
+
f"| Validation Score | {validation_score:.1f}% |",
|
|
2024
|
+
f"| **Overall IV&V Score** | **{overall_score:.1f}%** |",
|
|
2025
|
+
"",
|
|
2026
|
+
(
|
|
2027
|
+
"*Scoring: Overall = 0.6 x Verification + 0.4 x Validation. "
|
|
2028
|
+
"Per-area = 100 x (pass + partial x 0.5) / assessable.*"
|
|
2029
|
+
),
|
|
2030
|
+
"",
|
|
2031
|
+
])
|
|
2032
|
+
|
|
2033
|
+
# Area score breakdown
|
|
2034
|
+
lines.extend([
|
|
2035
|
+
"### Score Breakdown by Area",
|
|
2036
|
+
"",
|
|
2037
|
+
"| Area Code | Score |",
|
|
2038
|
+
"|-----------|-------|",
|
|
2039
|
+
])
|
|
2040
|
+
for area in PROCESS_AREAS:
|
|
2041
|
+
code = PROCESS_AREA_CODES.get(area, "")
|
|
2042
|
+
if code in area_scores:
|
|
2043
|
+
category = (
|
|
2044
|
+
"Verification" if code in VERIFICATION_AREAS
|
|
2045
|
+
else "Validation"
|
|
2046
|
+
)
|
|
2047
|
+
lines.append(
|
|
2048
|
+
f"| {code} ({area}) | {area_scores[code]:.1f}% "
|
|
2049
|
+
f"[{category}] |"
|
|
2050
|
+
)
|
|
2051
|
+
lines.append("")
|
|
2052
|
+
|
|
2053
|
+
# Gate evaluation section
|
|
2054
|
+
if gate:
|
|
2055
|
+
gate_label = "PASS" if gate_result["passed"] else "**FAIL**"
|
|
2056
|
+
lines.extend([
|
|
2057
|
+
"## IV&V Gate Evaluation",
|
|
2058
|
+
"",
|
|
2059
|
+
f"**Gate Result:** {gate_label}",
|
|
2060
|
+
"**Criteria:** 0 critical IV&V findings",
|
|
2061
|
+
(
|
|
2062
|
+
f"**Critical Findings:** "
|
|
2063
|
+
f"{gate_result['critical_findings_count']}"
|
|
2064
|
+
),
|
|
2065
|
+
"",
|
|
2066
|
+
])
|
|
2067
|
+
if gate_result["critical_findings"]:
|
|
2068
|
+
lines.append("**Critical Findings:**")
|
|
2069
|
+
for cf in gate_result["critical_findings"]:
|
|
2070
|
+
lines.append(f"- {cf}")
|
|
2071
|
+
lines.append("")
|
|
2072
|
+
|
|
2073
|
+
# Certification status
|
|
2074
|
+
lines.extend([
|
|
2075
|
+
"## IV&V Certification Status",
|
|
2076
|
+
"",
|
|
2077
|
+
f"**Status:** {cert_status.replace('_', ' ').title()}",
|
|
2078
|
+
f"**Open Findings:** {open_count}",
|
|
2079
|
+
f"**Critical Findings:** {critical_count}",
|
|
2080
|
+
"",
|
|
2081
|
+
])
|
|
2082
|
+
|
|
2083
|
+
# Findings section
|
|
2084
|
+
if findings:
|
|
2085
|
+
lines.extend([
|
|
2086
|
+
"---",
|
|
2087
|
+
"",
|
|
2088
|
+
"## IV&V Findings",
|
|
2089
|
+
"",
|
|
2090
|
+
])
|
|
2091
|
+
for f in findings:
|
|
2092
|
+
lines.extend([
|
|
2093
|
+
f"### {f['finding_id']}",
|
|
2094
|
+
"",
|
|
2095
|
+
f"**Severity:** {f['severity'].upper()} ",
|
|
2096
|
+
f"**Process Area:** {f['process_area']} ",
|
|
2097
|
+
f"**Title:** {f['title']}",
|
|
2098
|
+
"",
|
|
2099
|
+
f"**Description:** {f['description']}",
|
|
2100
|
+
"",
|
|
2101
|
+
f"**Recommendation:** {f['recommendation']}",
|
|
2102
|
+
"",
|
|
2103
|
+
"---",
|
|
2104
|
+
"",
|
|
2105
|
+
])
|
|
2106
|
+
|
|
2107
|
+
lines.extend(["---", ""])
|
|
2108
|
+
|
|
2109
|
+
# -- Detailed findings per process area --
|
|
2110
|
+
lines.append("## Detailed Assessment Results")
|
|
2111
|
+
lines.append("")
|
|
2112
|
+
|
|
2113
|
+
for area in PROCESS_AREAS:
|
|
2114
|
+
area_results = [
|
|
2115
|
+
r for r in results if r["process_area"] == area
|
|
2116
|
+
]
|
|
2117
|
+
if not area_results:
|
|
2118
|
+
continue
|
|
2119
|
+
|
|
2120
|
+
code = PROCESS_AREA_CODES.get(area, "")
|
|
2121
|
+
score = area_scores.get(code, 0.0)
|
|
2122
|
+
lines.append(f"### {area} ({code}) -- {score:.1f}%")
|
|
2123
|
+
lines.append("")
|
|
2124
|
+
|
|
2125
|
+
for r in area_results:
|
|
2126
|
+
status_display = r["status"].replace("_", " ").title()
|
|
2127
|
+
priority_display = r["priority"].upper()
|
|
2128
|
+
vtype_display = r["verification_type"].title()
|
|
2129
|
+
nist_str = (
|
|
2130
|
+
", ".join(r["nist_controls"])
|
|
2131
|
+
if r["nist_controls"]
|
|
2132
|
+
else "N/A"
|
|
2133
|
+
)
|
|
2134
|
+
|
|
2135
|
+
lines.extend([
|
|
2136
|
+
f"#### {r['requirement_id']}: {r['title']}",
|
|
2137
|
+
"",
|
|
2138
|
+
f"**Type:** {vtype_display} ",
|
|
2139
|
+
f"**Priority:** {priority_display} ",
|
|
2140
|
+
f"**Status:** {status_display} ",
|
|
2141
|
+
f"**Automation Level:** {r['automation_level']} ",
|
|
2142
|
+
f"**NIST Controls:** {nist_str}",
|
|
2143
|
+
"",
|
|
2144
|
+
f"**Evidence:** {r['evidence']}",
|
|
2145
|
+
"",
|
|
2146
|
+
])
|
|
2147
|
+
if r["details"]:
|
|
2148
|
+
lines.append(f"**Details:** {r['details']}")
|
|
2149
|
+
lines.append("")
|
|
2150
|
+
if r["notes"]:
|
|
2151
|
+
lines.append(f"**Notes:** {r['notes']}")
|
|
2152
|
+
lines.append("")
|
|
2153
|
+
|
|
2154
|
+
lines.extend(["---", ""])
|
|
2155
|
+
|
|
2156
|
+
# Append CUI footer
|
|
2157
|
+
lines.extend([doc_footer, ""])
|
|
2158
|
+
content = "\n".join(lines)
|
|
2159
|
+
|
|
2160
|
+
# -- Write output file --
|
|
2161
|
+
if output_path:
|
|
2162
|
+
out_dir = Path(output_path)
|
|
2163
|
+
else:
|
|
2164
|
+
dir_path = project.get("directory_path", "")
|
|
2165
|
+
if dir_path:
|
|
2166
|
+
out_dir = Path(dir_path) / "compliance"
|
|
2167
|
+
else:
|
|
2168
|
+
out_dir = BASE_DIR / ".tmp" / "compliance" / project_id
|
|
2169
|
+
out_dir.mkdir(parents=True, exist_ok=True)
|
|
2170
|
+
|
|
2171
|
+
area_suffix = (
|
|
2172
|
+
process_area.lower().replace(" ", "_").replace("/", "_")
|
|
2173
|
+
if process_area != "all"
|
|
2174
|
+
else "all"
|
|
2175
|
+
)
|
|
2176
|
+
out_file = (
|
|
2177
|
+
out_dir
|
|
2178
|
+
/ f"ivv_1012_{project_id}_{area_suffix}_"
|
|
2179
|
+
f"{now.strftime('%Y%m%d_%H%M%S')}.md"
|
|
2180
|
+
)
|
|
2181
|
+
|
|
2182
|
+
with open(out_file, "w", encoding="utf-8") as f:
|
|
2183
|
+
f.write(content)
|
|
2184
|
+
|
|
2185
|
+
# -- Log audit event --
|
|
2186
|
+
_log_audit_event(
|
|
2187
|
+
conn,
|
|
2188
|
+
project_id,
|
|
2189
|
+
f"IV&V assessment completed ({process_area})",
|
|
2190
|
+
{
|
|
2191
|
+
"process_area": process_area,
|
|
2192
|
+
"requirements_assessed": len(results),
|
|
2193
|
+
"findings_generated": len(findings),
|
|
2194
|
+
"verification_score": verification_score,
|
|
2195
|
+
"validation_score": validation_score,
|
|
2196
|
+
"overall_score": overall_score,
|
|
2197
|
+
"summary": {k: v for k, v in grand_total.items()},
|
|
2198
|
+
"gate_result": gate_result,
|
|
2199
|
+
"certification_status": cert_status,
|
|
2200
|
+
"output_file": str(out_file),
|
|
2201
|
+
},
|
|
2202
|
+
out_file,
|
|
2203
|
+
)
|
|
2204
|
+
|
|
2205
|
+
# -- Console output --
|
|
2206
|
+
print("IV&V assessment completed:")
|
|
2207
|
+
print(f" File: {out_file}")
|
|
2208
|
+
print(f" Scope: {process_area}")
|
|
2209
|
+
print(f" Requirements assessed: {len(results)}")
|
|
2210
|
+
print(f" Findings generated: {len(findings)}")
|
|
2211
|
+
|
|
2212
|
+
for area in PROCESS_AREAS:
|
|
2213
|
+
s = summary.get(area, {})
|
|
2214
|
+
if s.get("total", 0) == 0:
|
|
2215
|
+
continue
|
|
2216
|
+
code = PROCESS_AREA_CODES.get(area, "")
|
|
2217
|
+
score = area_scores.get(code, 0.0)
|
|
2218
|
+
print(
|
|
2219
|
+
f" {area} ({code}): "
|
|
2220
|
+
f"PASS={s['pass']} "
|
|
2221
|
+
f"PARTIAL={s['partial']} "
|
|
2222
|
+
f"FAIL={s['fail']} "
|
|
2223
|
+
f"NOT_ASSESSED={s['not_assessed']} "
|
|
2224
|
+
f"Score={score:.1f}%"
|
|
2225
|
+
)
|
|
2226
|
+
|
|
2227
|
+
print(f"\n Verification Score: {verification_score:.1f}%")
|
|
2228
|
+
print(f" Validation Score: {validation_score:.1f}%")
|
|
2229
|
+
print(f" Overall IV&V Score: {overall_score:.1f}%")
|
|
2230
|
+
print(f" Certification: {cert_status}")
|
|
2231
|
+
|
|
2232
|
+
if gate:
|
|
2233
|
+
print(f"\n Gate: {gate_result['reason']}")
|
|
2234
|
+
|
|
2235
|
+
return {
|
|
2236
|
+
"output_file": str(out_file),
|
|
2237
|
+
"results": results,
|
|
2238
|
+
"findings": findings,
|
|
2239
|
+
"summary": summary,
|
|
2240
|
+
"scores": {
|
|
2241
|
+
"area_scores": area_scores,
|
|
2242
|
+
"verification_score": verification_score,
|
|
2243
|
+
"validation_score": validation_score,
|
|
2244
|
+
"overall_score": overall_score,
|
|
2245
|
+
},
|
|
2246
|
+
"gate_result": gate_result,
|
|
2247
|
+
"certification_status": cert_status,
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
finally:
|
|
2251
|
+
conn.close()
|
|
2252
|
+
|
|
2253
|
+
|
|
2254
|
+
# -----------------------------------------------------------------
|
|
2255
|
+
# Public alias
|
|
2256
|
+
# -----------------------------------------------------------------
|
|
2257
|
+
|
|
2258
|
+
def assess_project(project_id, process_area="all", **kwargs):
|
|
2259
|
+
"""Alias for run_ivv_assessment for convenient programmatic use."""
|
|
2260
|
+
return run_ivv_assessment(
|
|
2261
|
+
project_id, process_area=process_area, **kwargs
|
|
2262
|
+
)
|
|
2263
|
+
|
|
2264
|
+
|
|
2265
|
+
# -----------------------------------------------------------------
|
|
2266
|
+
# CLI entrypoint
|
|
2267
|
+
# -----------------------------------------------------------------
|
|
2268
|
+
|
|
2269
|
+
if __name__ == "__main__":
|
|
2270
|
+
parser = argparse.ArgumentParser(
|
|
2271
|
+
description="Run IV&V assessment per IEEE 1012"
|
|
2272
|
+
)
|
|
2273
|
+
parser.add_argument(
|
|
2274
|
+
"--project-id", required=True, help="Project ID"
|
|
2275
|
+
)
|
|
2276
|
+
parser.add_argument(
|
|
2277
|
+
"--process-area",
|
|
2278
|
+
default="all",
|
|
2279
|
+
choices=["all"] + PROCESS_AREAS,
|
|
2280
|
+
help="Process area to assess (default: all)",
|
|
2281
|
+
)
|
|
2282
|
+
parser.add_argument(
|
|
2283
|
+
"--project-dir",
|
|
2284
|
+
help="Project directory for automated file-based checks",
|
|
2285
|
+
)
|
|
2286
|
+
parser.add_argument(
|
|
2287
|
+
"--gate",
|
|
2288
|
+
action="store_true",
|
|
2289
|
+
help="Evaluate IV&V gate (0 critical findings = pass)",
|
|
2290
|
+
)
|
|
2291
|
+
parser.add_argument(
|
|
2292
|
+
"--output-dir",
|
|
2293
|
+
help="Output directory for the assessment report",
|
|
2294
|
+
)
|
|
2295
|
+
parser.add_argument(
|
|
2296
|
+
"--db-path",
|
|
2297
|
+
type=Path,
|
|
2298
|
+
default=DB_PATH,
|
|
2299
|
+
help="Override database path",
|
|
2300
|
+
)
|
|
2301
|
+
parser.add_argument("--json", action="store_true", dest="json_output", help="JSON output")
|
|
2302
|
+
args = parser.parse_args()
|
|
2303
|
+
|
|
2304
|
+
try:
|
|
2305
|
+
result = run_ivv_assessment(
|
|
2306
|
+
project_id=args.project_id,
|
|
2307
|
+
process_area=args.process_area,
|
|
2308
|
+
project_dir=args.project_dir,
|
|
2309
|
+
gate=args.gate,
|
|
2310
|
+
output_path=args.output_dir,
|
|
2311
|
+
db_path=args.db_path,
|
|
2312
|
+
)
|
|
2313
|
+
print(json.dumps({
|
|
2314
|
+
"output_file": result.get("output_file"),
|
|
2315
|
+
"scores": result.get("scores"),
|
|
2316
|
+
"summary": result.get("summary"),
|
|
2317
|
+
"gate_result": result.get("gate_result"),
|
|
2318
|
+
"certification_status": result.get("certification_status"),
|
|
2319
|
+
"findings_count": len(result.get("findings", [])),
|
|
2320
|
+
}, indent=2))
|
|
2321
|
+
|
|
2322
|
+
if args.gate and not result["gate_result"]["passed"]:
|
|
2323
|
+
sys.exit(1)
|
|
2324
|
+
|
|
2325
|
+
except (FileNotFoundError, ValueError) as e:
|
|
2326
|
+
print(f"ERROR: {e}", file=sys.stderr)
|
|
2327
|
+
sys.exit(1)
|