Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 50 additions & 23 deletions backend/routers/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@

router = APIRouter()

# ZT Pillar -> CMMC domains mapping (DoD ZT Strategy alignment)
ZT_PILLAR_DOMAINS = {
"User": ["AC", "IA", "PS"],
"Device": ["CM", "MA", "PE"],
"Network": ["SC", "AC"],
"Application": ["CM", "CA", "SI"],
"Data": ["MP", "SC", "AU"],
"Visibility & Analytics": ["AU", "IR", "RA"],
"Automation & Orchestration": ["IR", "SI", "CA"],
}


def get_status_emoji(status: str) -> str:
"""Map implementation status to a visual emoji for better scannability."""
Expand Down Expand Up @@ -52,6 +63,17 @@ def get_confidence_stars(confidence: float) -> str:
return "⭐" * stars + "☆" * (5 - stars)


def get_maturity_pct(implemented: int, partial: int, total: int) -> float:
"""
Calculate ZT maturity percentage based on weighted compliance.
Partial implementations are weighted at 0.5, aligning with the project's
orchestrator agent and dashboard reporting conventions.
"""
if total <= 0:
return 0.0
return round((implemented + 0.5 * partial) / total * 100, 1)


@router.get("/ssp", summary="Generate System Security Plan (SSP) in Markdown")
async def generate_ssp(
system_name: str = "AGI Corp CMMC System",
Expand Down Expand Up @@ -83,24 +105,40 @@ async def generate_ssp(
status_counts["partial"] += 1

total_controls = len(controls)
implemented_pct = (
compliance_pct = (
(status_counts["implemented"] / total_controls * 100)
if total_controls > 0
else 0
)
progress_bar = get_progress_bar(compliance_pct)
Comment on lines +108 to +113
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align Overall Compliance math with pillar maturity math

At Line 108-113, Overall Compliance is implemented-only, but pillar status (Line 138) uses weighted partials (0.5). This can present conflicting compliance signals in the same SSP.

Suggested fix
-    compliance_pct = (
-        (status_counts["implemented"] / total_controls * 100)
-        if total_controls > 0
-        else 0
-    )
+    compliance_pct = get_maturity_pct(
+        status_counts["implemented"],
+        status_counts["partial"],
+        total_controls,
+    )

Also applies to: 150-150

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/routers/reports.py` around lines 108 - 113, Overall Compliance
currently computes compliance_pct using only implemented controls
(compliance_pct = status_counts["implemented"] / total_controls * 100); update
this to match the pillar maturity logic by counting partials at half weight
(i.e., implemented + 0.5 * partial) divided by total_controls before multiplying
by 100, then pass that to get_progress_bar; update the same calculation location
referenced by the second occurrence (the other compliance_pct calculation around
the other summary) so both overall and pillar math use the same weighted partial
logic, and keep references to status_counts, total_controls, compliance_pct and
get_progress_bar to locate the changes.


sprs_estimate = 110 - (
status_counts["not_implemented"] * 1 + status_counts["partial"] * 0.5
)
sprs_estimate = max(-203, round(sprs_estimate, 0))

total_controls_count = len(controls)
compliance_pct = (
(status_counts["implemented"] / total_controls_count * 100)
if total_controls_count > 0
else 0
)
progress_bar = get_progress_bar(compliance_pct)
# Calculate ZT pillar progress
zt_pillar_rows = ""
for pillar, domains in ZT_PILLAR_DOMAINS.items():
pillar_controls = [c for c in controls.values() if c.domain in domains]
if not pillar_controls:
status_display = "N/A"
else:
p_total = len(pillar_controls)
p_implemented = 0
p_partial = 0
for pc in pillar_controls:
pa = assessments_dict.get(pc.id)
if pa:
if pa.status == "implemented":
p_implemented += 1
elif pa.status in ["partial", "partially_implemented"]:
p_partial += 1

p_compliance = get_maturity_pct(p_implemented, p_partial, p_total)
status_display = get_progress_bar(p_compliance, width=8)

zt_pillar_rows += f"| {pillar} | {', '.join(domains)} | {status_display} |\n"

ssp = f"""# System Security Plan (SSP)
## {system_name}
Expand All @@ -109,7 +147,7 @@ async def generate_ssp(
**Generated:** {datetime.now(UTC).strftime('%Y-%m-%d %H:%M UTC')}
**Framework:** CMMC 2.0 Level 2 / NIST SP 800-171 Rev 2
**SPRS Score Estimate:** {sprs_estimate}
**Overall Compliance:** {get_progress_bar(implemented_pct)}
**Overall Compliance:** {progress_bar}

---

Expand Down Expand Up @@ -142,13 +180,7 @@ async def generate_ssp(

| ZT Pillar | CMMC Domains | Status |
|-----------|--------------|--------|
| User | AC, IA, PS | See assessment |
| Device | CM, MA, PE | See assessment |
| Network | SC, AC | See assessment |
| Application | CM, CA, SI | See assessment |
| Data | MP, SC, AU | See assessment |
| Visibility & Analytics | AU, IR, RA | See assessment |
| Automation & Orchestration | IR, SI, CA | See assessment |
{zt_pillar_rows.strip()}

## 3. Assessment Findings

Expand Down Expand Up @@ -300,13 +332,8 @@ async def get_dashboard(
round(implemented / total_controls * 100, 1) if total_controls else 0
),
"zt_pillars": [
{"pillar": "User", "domains": ["AC", "IA", "PS"]},
{"pillar": "Device", "domains": ["CM", "MA", "PE"]},
{"pillar": "Network", "domains": ["SC", "AC"]},
{"pillar": "Application", "domains": ["CM", "CA", "SI"]},
{"pillar": "Data", "domains": ["MP", "SC", "AU"]},
{"pillar": "Visibility & Analytics", "domains": ["AU", "IR", "RA"]},
{"pillar": "Automation & Orchestration", "domains": ["IR", "SI", "CA"]},
{"pillar": pillar, "domains": domains}
for pillar, domains in ZT_PILLAR_DOMAINS.items()
],
"agents": [
{"name": "orchestrator", "endpoint": "/api/orchestrator"},
Expand Down
Loading