From 0d0d232ee7a563525652ef047b3a69166fb2240d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:38:05 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20SSP=20r?= =?UTF-8?q?eport=20with=20dynamic=20Zero=20Trust=20maturity=20and=20naviga?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace static "See assessment" with dynamic ZT pillar progress bars - Add "Back to Top" navigation links to the SSP Markdown report - Add dynamic findings summary line (e.g., "Showing X of Y findings") - Refactor dashboard and reports to use shared ZT_PILLAR_DOMAINS constant - Update get_status_emoji for better visual scannability (🛑 -> 🚫) - Standardize maturity calculation with 0.5 weight for partial implementations - Add verification tests in tests/test_palette_ux.py Co-authored-by: AGI-Corporation <186229839+AGI-Corporation@users.noreply.github.com> --- backend/routers/reports.py | 68 ++++++++++++++++++++++++++++---------- tests/test_palette_ux.py | 7 ++++ 2 files changed, 57 insertions(+), 18 deletions(-) diff --git a/backend/routers/reports.py b/backend/routers/reports.py index 5f2660b..f73ce97 100644 --- a/backend/routers/reports.py +++ b/backend/routers/reports.py @@ -23,6 +23,16 @@ router = APIRouter() +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.""" @@ -31,7 +41,7 @@ def get_status_emoji(status: str) -> str: "partial": "🟡", "partially_implemented": "🟡", "planned": "📝", - "not_implemented": "🛑", + "not_implemented": "🚫", "na": "⚪", "not_started": "⚪", } @@ -52,6 +62,18 @@ def get_confidence_stars(confidence: float) -> str: return "⭐" * stars + "☆" * (5 - stars) +def get_maturity_pct(assessments: List[AssessmentRecord]) -> float: + """Standardized maturity calculation (0.5 weight for partial).""" + if not assessments: + return 0.0 + total = len(assessments) + implemented = sum(1 for a in assessments if a.status == "implemented") + partial = sum( + 1 for a in assessments if a.status in ["partial", "partially_implemented"] + ) + return ((implemented + (partial * 0.5)) / total) * 100 + + @router.get("/ssp", summary="Generate System Security Plan (SSP) in Markdown") async def generate_ssp( system_name: str = "AGI Corp CMMC System", @@ -102,6 +124,17 @@ async def generate_ssp( ) progress_bar = get_progress_bar(compliance_pct) + # Calculate ZT Pillar Maturity + zt_maturity = {} + for pillar, domains in ZT_PILLAR_DOMAINS.items(): + pillar_assessments = [ + a + for a in assessments + if a.control_id.split(".")[0] in domains + or (controls.get(a.control_id) and controls[a.control_id].domain in domains) + ] + zt_maturity[pillar] = get_maturity_pct(pillar_assessments) + ssp = f"""# System Security Plan (SSP) ## {system_name} @@ -136,23 +169,27 @@ async def generate_ssp( | Not Implemented | {get_status_emoji('not_implemented')} {status_counts['not_implemented']} | | N/A | {get_status_emoji('na')} {status_counts['na']} | +[↑ Back to Top](#system-security-plan-ssp) + ## 2. Control Implementation Summary ### Zero Trust Pillar Alignment -| 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 | CMMC Domains | Maturity | +|-----------|--------------|----------| +| User | AC, IA, PS | {get_progress_bar(zt_maturity["User"])} | +| Device | CM, MA, PE | {get_progress_bar(zt_maturity["Device"])} | +| Network | SC, AC | {get_progress_bar(zt_maturity["Network"])} | +| Application | CM, CA, SI | {get_progress_bar(zt_maturity["Application"])} | +| Data | MP, SC, AU | {get_progress_bar(zt_maturity["Data"])} | +| Visibility & Analytics | AU, IR, RA | {get_progress_bar(zt_maturity["Visibility & Analytics"])} | +| Automation & Orchestration | IR, SI, CA | {get_progress_bar(zt_maturity["Automation & Orchestration"])} | + +[↑ Back to Top](#system-security-plan-ssp) ## 3. Assessment Findings -*Note: Only the first 20 assessment findings are displayed in this summary.* +*Note: Only the first 20 assessment findings are displayed in this summary. Showing {min(20, len(assessments))} of {len(assessments)} findings.* """ @@ -300,13 +337,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"}, diff --git a/tests/test_palette_ux.py b/tests/test_palette_ux.py index 04bed3a..a5ff687 100644 --- a/tests/test_palette_ux.py +++ b/tests/test_palette_ux.py @@ -73,3 +73,10 @@ async def test_ssp_ux_elements(): assert "⭐⭐⭐⭐⭐" in content # 0.5 confidence should have 3 stars: ⭐⭐⭐☆☆ (based on int(0.5 * 5 + 0.5) = 3) assert "⭐⭐⭐☆☆" in content + + # Palette UX Enhancements check + assert "🚫" in content or "not_implemented" in content + assert "Zero Trust Pillar Alignment" in content + assert "Maturity" in content + assert "[↑ Back to Top](#system-security-plan-ssp)" in content + assert "Showing" in content and "findings" in content From 91b10e583f266c71d6a7f406bd7cac440491f75e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:40:48 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20SSP=20r?= =?UTF-8?q?eport=20with=20dynamic=20Zero=20Trust=20maturity=20and=20naviga?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace static "See assessment" with dynamic ZT pillar progress bars - Add "Back to Top" navigation links to the SSP Markdown report - Add dynamic findings summary line (e.g., "Showing X of Y findings") - Refactor dashboard and reports to use shared ZT_PILLAR_DOMAINS constant - Update get_status_emoji for better visual scannability (🛑 -> 🚫) - Standardize maturity calculation with 0.5 weight for partial implementations - Add verification tests in tests/test_palette_ux.py Co-authored-by: AGI-Corporation <186229839+AGI-Corporation@users.noreply.github.com> From de6f2cafdf32ca214a57d41ef8dd5b492dd1e52a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:44:24 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20SSP=20r?= =?UTF-8?q?eport=20with=20dynamic=20Zero=20Trust=20maturity=20and=20naviga?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace static "See assessment" with dynamic ZT pillar progress bars - Add "Back to Top" navigation links to the SSP Markdown report - Add dynamic findings summary line (e.g., "Showing X of Y findings") - Refactor dashboard and reports to use shared ZT_PILLAR_DOMAINS constant - Update get_status_emoji for better visual scannability (🛑 -> 🚫) - Standardize maturity calculation with 0.5 weight for partial implementations - Clean up unused imports and fix PEP8 linting issues in reports router - Add verification tests in tests/test_palette_ux.py Co-authored-by: AGI-Corporation <186229839+AGI-Corporation@users.noreply.github.com> --- backend/routers/reports.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/backend/routers/reports.py b/backend/routers/reports.py index f73ce97..dccf6f5 100644 --- a/backend/routers/reports.py +++ b/backend/routers/reports.py @@ -8,17 +8,15 @@ import csv import io -import json from datetime import UTC, date, datetime -from typing import Any, Dict, List +from typing import List from fastapi import APIRouter, Depends from fastapi.responses import PlainTextResponse from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession -from backend.db.database import (AssessmentRecord, ControlRecord, - EvidenceRecord, get_db, +from backend.db.database import (AssessmentRecord, ControlRecord, get_db, get_latest_assessments) router = APIRouter() @@ -116,14 +114,6 @@ async def generate_ssp( ) 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 Maturity zt_maturity = {} for pillar, domains in ZT_PILLAR_DOMAINS.items(): @@ -138,10 +128,10 @@ async def generate_ssp( ssp = f"""# System Security Plan (SSP) ## {system_name} -**Classification:** {classification} +**Classification:** {classification} **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} +**Framework:** CMMC 2.0 Level 2 / NIST SP 800-171 Rev 2 +**SPRS Score Estimate:** {sprs_estimate} **Overall Compliance:** {get_progress_bar(implemented_pct)} --- @@ -189,7 +179,8 @@ async def generate_ssp( ## 3. Assessment Findings -*Note: Only the first 20 assessment findings are displayed in this summary. Showing {min(20, len(assessments))} of {len(assessments)} findings.* +*Note: Only the first 20 assessment findings are displayed in this summary. +Showing {min(20, len(assessments))} of {len(assessments)} findings.* """ @@ -286,7 +277,7 @@ async def generate_poam( content=csv_content, media_type="text/csv", headers={ - "Content-Disposition": f'attachment; filename="poam_{system_name.replace(" ","_")}.csv"' + "Content-Disposition": f'attachment; filename="poam_{system_name.replace(" ", "_")}.csv"' }, ) From 94921620ddfa58b347f2f99efb15fcb608dd8813 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:54:41 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20SSP=20r?= =?UTF-8?q?eport=20with=20dynamic=20Zero=20Trust=20maturity=20and=20naviga?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace static "See assessment" with dynamic ZT pillar progress bars - Add "Back to Top" navigation links to the SSP Markdown report - Add dynamic findings summary line (e.g., "Showing X of Y findings") - Refactor dashboard and reports to use shared ZT_PILLAR_DOMAINS constant - Update get_status_emoji for better visual scannability (🛑 -> 🚫) - Standardize maturity calculation with 0.5 weight for partial implementations - Clean up unused imports and fix PEP8 linting issues in reports router - Add verification tests in tests/test_palette_ux.py Co-authored-by: AGI-Corporation <186229839+AGI-Corporation@users.noreply.github.com> --- backend/routers/reports.py | 10 +++++++--- tests/test_palette_ux.py | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/routers/reports.py b/backend/routers/reports.py index dccf6f5..61c5e17 100644 --- a/backend/routers/reports.py +++ b/backend/routers/reports.py @@ -9,15 +9,19 @@ import csv import io from datetime import UTC, date, datetime -from typing import List +from typing import List, Optional from fastapi import APIRouter, Depends from fastapi.responses import PlainTextResponse from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession -from backend.db.database import (AssessmentRecord, ControlRecord, get_db, - get_latest_assessments) +from backend.db.database import ( + AssessmentRecord, + ControlRecord, + get_db, + get_latest_assessments, +) router = APIRouter() diff --git a/tests/test_palette_ux.py b/tests/test_palette_ux.py index a5ff687..bac19f6 100644 --- a/tests/test_palette_ux.py +++ b/tests/test_palette_ux.py @@ -3,7 +3,6 @@ import pytest from httpx import ASGITransport, AsyncClient -from sqlalchemy.ext.asyncio import AsyncSession from backend.db.database import AssessmentRecord, Base, engine, init_db from backend.main import app