fix(mc-browser): scope fallback + severity/domain filters

- 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) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-11 23:13:22 +02:00
parent 397de741c1
commit 7ca3624a1f
2 changed files with 35 additions and 3 deletions
@@ -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,
})
}
@@ -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 (
<ControlDetail
ctrl={state.selectedControl}
ctrl={safeCtrl}
onBack={() => { state.setMode('list'); state.setSelectedControl(null) }}
onEdit={() => {}}
onDelete={async () => {}}