From 7ca3624a1fd9027e8cd79d9ab0c10da1ab7ee1cb Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Mon, 11 May 2026 23:13:22 +0200 Subject: [PATCH] fix(mc-browser): scope fallback + severity/domain filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add scope/risk_score/implementation_effort fallbacks to prevent 'undefined is not an object' crash in ControlDetail - Add severity filter (high/medium/low based on total_controls) - Add domain filter (L1 token prefix match) - Fix sort options (source → canonical_name) Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/api/sdk/v1/master-controls/route.ts | 23 ++++++++++++++++++- .../app/sdk/master-controls/page.tsx | 15 ++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/admin-compliance/app/api/sdk/v1/master-controls/route.ts b/admin-compliance/app/api/sdk/v1/master-controls/route.ts index bc6f47f..4dd70f5 100644 --- a/admin-compliance/app/api/sdk/v1/master-controls/route.ts +++ b/admin-compliance/app/api/sdk/v1/master-controls/route.ts @@ -60,8 +60,23 @@ async function handleControls(params: URLSearchParams) { idx++ } + const severity = params.get('severity') || '' + if (severity) { + if (severity === 'high') { where += ` AND mc.total_controls > 100` } + else if (severity === 'medium') { where += ` AND mc.total_controls BETWEEN 20 AND 100` } + else if (severity === 'low') { where += ` AND mc.total_controls < 20` } + } + + const domain = params.get('domain') || '' + if (domain) { + where += ` AND mc.canonical_name LIKE $${idx}` + args.push(`${domain}%`) + idx++ + } + const sortCol = sort === 'control_id' ? 'mc.master_control_id' : - sort === 'created_at' ? 'mc.created_at' : 'mc.master_control_id' + sort === 'created_at' ? 'mc.created_at' : + sort === 'source' ? 'mc.canonical_name' : 'mc.master_control_id' args.push(limit, offset) const res = await pool.query(` @@ -102,6 +117,9 @@ async function handleControls(params: URLSearchParams) { total_controls: r.total_controls, phases_covered: r.phases_covered, created_at: r.created_at, + scope: { platforms: [], components: [], data_classes: [] }, + risk_score: null, + implementation_effort: null, })) return NextResponse.json(controls) @@ -203,6 +221,9 @@ async function handleDetail(params: URLSearchParams) { open_anchors: [], target_audience: [], source_citation: null, + scope: { platforms: [], components: [], data_classes: [] }, + risk_score: null, + implementation_effort: null, created_at: mc.created_at, }) } diff --git a/admin-compliance/app/sdk/master-controls/page.tsx b/admin-compliance/app/sdk/master-controls/page.tsx index f357909..a2a593f 100644 --- a/admin-compliance/app/sdk/master-controls/page.tsx +++ b/admin-compliance/app/sdk/master-controls/page.tsx @@ -35,11 +35,22 @@ export default function MasterControlsPage() { ) } - // DETAIL mode + // DETAIL mode — add fallback fields that ControlDetail expects if (state.mode === 'detail' && state.selectedControl) { + const safeCtrl = { + ...state.selectedControl, + scope: state.selectedControl.scope || { platforms: [], components: [], data_classes: [] }, + target_audience: state.selectedControl.target_audience || [], + requirements: state.selectedControl.requirements || [], + test_procedure: state.selectedControl.test_procedure || [], + evidence: state.selectedControl.evidence || [], + open_anchors: state.selectedControl.open_anchors || [], + risk_score: state.selectedControl.risk_score || null, + implementation_effort: state.selectedControl.implementation_effort || null, + } return ( { state.setMode('list'); state.setSelectedControl(null) }} onEdit={() => {}} onDelete={async () => {}}