feat(web): add IwoooS host review outcomes
This commit is contained in:
@@ -1447,6 +1447,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"hostEvidenceReviewOutcomes": {
|
||||
"title": "Host Evidence Review Outcome Lanes",
|
||||
"subtitle": "After preflight, evidence can only move into these read-only lanes. This shows possible human review outcomes and does not create approval records, runtime gates, or host actions.",
|
||||
"laneLabel": "Outcome lane",
|
||||
"nextLabel": "Next step",
|
||||
"items": {
|
||||
"readyForHumanReview": {
|
||||
"title": "Ready for human review",
|
||||
"body": "Evidence becomes a human review candidate only when metadata pointer, dependency order, scope, and owner pointer are readable.",
|
||||
"next": "display candidate only; received=0, accepted=0"
|
||||
},
|
||||
"needsScopeEvidence": {
|
||||
"title": "Needs scope evidence",
|
||||
"body": "Scan or finding evidence that cannot map to scope boundary returns to the scope lane.",
|
||||
"next": "collect scope, no scan"
|
||||
},
|
||||
"needsOwnerDecision": {
|
||||
"title": "Needs owner decision",
|
||||
"body": "Host change, update, SSH, or blocking evidence without decision pointer returns to owner decision lane.",
|
||||
"next": "collect decision record, no host action"
|
||||
},
|
||||
"quarantineDependencySkip": {
|
||||
"title": "Quarantine dependency skip",
|
||||
"body": "Evidence that skips the S2.17 order or has incomplete prerequisites is quarantined for human interpretation.",
|
||||
"next": "show quarantine reason, do not advance counters"
|
||||
},
|
||||
"rejectRawPayload": {
|
||||
"title": "Reject raw payload",
|
||||
"body": "Full scan output, unredacted findings, host dumps, or log bundles do not enter IwoooS.",
|
||||
"next": "request redacted summary instead"
|
||||
},
|
||||
"rejectCredentialPlaintext": {
|
||||
"title": "Reject credential plaintext",
|
||||
"body": "Passwords, tokens, private keys, sessions, or plaintext credentials are rejected and quarantined.",
|
||||
"next": "do not store, forward, or display plaintext"
|
||||
},
|
||||
"waitingRuntimeGate": {
|
||||
"title": "Waiting runtime gate",
|
||||
"body": "Even after human review allows action, the work waits for a later runtime gate and is not executed by this lane.",
|
||||
"next": "active runtime gates remain 0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nextGate": {
|
||||
"title": "Next High-level Gate",
|
||||
"body": "S4.9 Gitea owner attestation response is the recommended next owner evidence. Headline progress should only increase after owner responses, redacted payload ingestion, active runtime gates, or GitHub primary readiness actually change."
|
||||
|
||||
@@ -1448,6 +1448,49 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"hostEvidenceReviewOutcomes": {
|
||||
"title": "主機 Evidence Review Outcome Lanes",
|
||||
"subtitle": "預檢後的 evidence 只能進這些只讀分流。這裡顯示人工審查可能結果,不會建立 approval record、runtime gate 或主機動作。",
|
||||
"laneLabel": "結果分流",
|
||||
"nextLabel": "下一步",
|
||||
"items": {
|
||||
"readyForHumanReview": {
|
||||
"title": "Ready for human review",
|
||||
"body": "metadata pointer、依賴順序、scope 與 owner pointer 都可讀時,才可標示為人工審查候選。",
|
||||
"next": "只顯示候選;received=0、accepted=0"
|
||||
},
|
||||
"needsScopeEvidence": {
|
||||
"title": "Needs scope evidence",
|
||||
"body": "scan 或 finding evidence 無法對到 scope boundary 時,回到補 scope lane。",
|
||||
"next": "補 scope,不進 scan"
|
||||
},
|
||||
"needsOwnerDecision": {
|
||||
"title": "Needs owner decision",
|
||||
"body": "主機變更、更新、SSH 或 blocking 類 evidence 缺 decision pointer 時,回到 owner decision lane。",
|
||||
"next": "補 decision record,不啟動主機動作"
|
||||
},
|
||||
"quarantineDependencySkip": {
|
||||
"title": "Quarantine dependency skip",
|
||||
"body": "跳過 S2.17 收件順序或前置依賴不完整時,先隔離再人工判讀。",
|
||||
"next": "隔離原因可見,不推 counter"
|
||||
},
|
||||
"rejectRawPayload": {
|
||||
"title": "Reject raw payload",
|
||||
"body": "完整掃描輸出、未脫敏 finding、host dump 或 log bundle 不進 IwoooS。",
|
||||
"next": "要求改交脫敏摘要"
|
||||
},
|
||||
"rejectCredentialPlaintext": {
|
||||
"title": "Reject credential plaintext",
|
||||
"body": "任何帳密、token、private key、session 或憑證明文都直接拒收並隔離。",
|
||||
"next": "不保存、不轉送、不顯示明文"
|
||||
},
|
||||
"waitingRuntimeGate": {
|
||||
"title": "Waiting runtime gate",
|
||||
"body": "即使人工審查後可採取行動,也只能等待後續 runtime gate,不由 outcome lane 執行。",
|
||||
"next": "active runtime gates 仍為 0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nextGate": {
|
||||
"title": "下一個高層 Gate",
|
||||
"body": "S4.9 Gitea owner attestation response 是目前建議先收的 owner evidence。任何 headline 提升都要等 owner response、redacted payload ingestion、active runtime gate 或 GitHub primary readiness 有真實變化。"
|
||||
|
||||
@@ -109,6 +109,13 @@ type HostEvidenceIntakeCheck = {
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
type HostEvidenceReviewOutcomeLane = {
|
||||
key: string
|
||||
lane: string
|
||||
icon: typeof ShieldCheck
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
const postureMetrics: PostureMetric[] = [
|
||||
{ key: 'overall', value: '58%', tone: 'warn' },
|
||||
{ key: 'framework', value: '80-85%', tone: 'steady' },
|
||||
@@ -250,6 +257,16 @@ const hostEvidenceIntakeChecks: HostEvidenceIntakeCheck[] = [
|
||||
{ key: 'counterFreeze', check: 'P7', icon: ShieldCheck, tone: 'locked' },
|
||||
]
|
||||
|
||||
const hostEvidenceReviewOutcomeLanes: HostEvidenceReviewOutcomeLane[] = [
|
||||
{ key: 'readyForHumanReview', lane: 'R1', icon: CheckCircle2, tone: 'steady' },
|
||||
{ key: 'needsScopeEvidence', lane: 'R2', icon: Radar, tone: 'warn' },
|
||||
{ key: 'needsOwnerDecision', lane: 'R3', icon: ClipboardCheck, tone: 'warn' },
|
||||
{ key: 'quarantineDependencySkip', lane: 'R4', icon: GitBranch, tone: 'warn' },
|
||||
{ key: 'rejectRawPayload', lane: 'R5', icon: FileWarning, tone: 'locked' },
|
||||
{ key: 'rejectCredentialPlaintext', lane: 'R6', icon: Lock, tone: 'locked' },
|
||||
{ key: 'waitingRuntimeGate', lane: 'R7', icon: Clock3, tone: 'locked' },
|
||||
]
|
||||
|
||||
const evidenceItems = [
|
||||
'iwooos-posture-projection.snapshot.json',
|
||||
'security-rollout-policy.snapshot.json',
|
||||
@@ -636,6 +653,34 @@ function HostEvidenceIntakeCard({ item }: { item: HostEvidenceIntakeCheck }) {
|
||||
)
|
||||
}
|
||||
|
||||
function HostEvidenceReviewOutcomeCard({ item }: { item: HostEvidenceReviewOutcomeLane }) {
|
||||
const t = useTranslations('iwooos.hostEvidenceReviewOutcomes')
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<div style={{ ...band, minHeight: 190, padding: 16 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
|
||||
<Icon size={18} color={toneColors[item.tone]} />
|
||||
<span style={{ fontSize: 11, color: '#87867f' }}>{t('laneLabel')}</span>
|
||||
</div>
|
||||
<span style={{ fontSize: 11, color: '#9b978b' }}>{item.lane}</span>
|
||||
</div>
|
||||
<h2 style={{ fontSize: 14, margin: '12px 0 6px', color: '#141413' }}>
|
||||
{t(`items.${item.key}.title` as never)}
|
||||
</h2>
|
||||
<p style={{ fontSize: 12, lineHeight: 1.55, color: '#6f6d66', margin: 0 }}>
|
||||
{t(`items.${item.key}.body` as never)}
|
||||
</p>
|
||||
<div style={{ marginTop: 10, display: 'grid', gap: 5 }}>
|
||||
<div style={{ fontSize: 11, color: '#87867f' }}>{t('nextLabel')}</div>
|
||||
<div style={{ fontSize: 11, color: toneColors[item.tone], lineHeight: 1.45 }}>
|
||||
{t(`items.${item.key}.next` as never)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function IwoooSPage({ params }: { params: { locale: string } }) {
|
||||
const t = useTranslations('iwooos')
|
||||
|
||||
@@ -808,6 +853,26 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ marginBottom: 14 }}>
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
<h2 style={{ fontSize: 16, margin: 0 }}>{t('hostEvidenceReviewOutcomes.title')}</h2>
|
||||
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55 }}>
|
||||
{t('hostEvidenceReviewOutcomes.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
{hostEvidenceReviewOutcomeLanes.map(item => (
|
||||
<HostEvidenceReviewOutcomeCard key={item.key} item={item} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
style={{
|
||||
display: 'grid',
|
||||
|
||||
Reference in New Issue
Block a user