feat(web): show owner response intake safety
This commit is contained in:
@@ -3059,6 +3059,70 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ownerResponseIntakeSafetyBoard": {
|
||||
"title": "人工回覆安全驗收閘道",
|
||||
"subtitle": "把收件後怎麼判定可收、補證、隔離或拒收先攤開。現在匯入=0、隔離=0、拒收=0;這只是驗收規則可見,不會自動匯入、通知、修復或升高限制。",
|
||||
"laneLabel": "驗收分流",
|
||||
"ruleLabel": "判定方式",
|
||||
"guardLabel": "仍不會做",
|
||||
"boundaryTitle": "驗收閘道維持的保護線",
|
||||
"summary": {
|
||||
"rules": {
|
||||
"label": "安全規則",
|
||||
"detail": "六條驗收分流先可見,避免收件後臨時判斷。"
|
||||
},
|
||||
"ingested": {
|
||||
"label": "已匯入",
|
||||
"detail": "目前為 0;還沒有任何人工回覆進入正式匯入。"
|
||||
},
|
||||
"quarantined": {
|
||||
"label": "已隔離",
|
||||
"detail": "目前為 0;若含機密明文值才會進隔離。"
|
||||
},
|
||||
"rejected": {
|
||||
"label": "已拒收",
|
||||
"detail": "目前為 0;拒收規則只是先讓邊界透明。"
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"redactedEvidenceOnly": {
|
||||
"title": "只接受脫敏證據",
|
||||
"body": "人工回覆必須能對照來源、負責人、範圍與遮罩後證據,才能進入驗收。",
|
||||
"rule": "缺少可驗證脫敏證據時,只能標記補證,不得匯入正式狀態。",
|
||||
"guard": "不把自由文字、截圖或未遮罩內容當成已接受回覆。"
|
||||
},
|
||||
"ownerScopeCompletion": {
|
||||
"title": "負責人範圍要齊全",
|
||||
"body": "每包回覆都要有負責人、專案庫或範圍、決策、證據指標與回滾關聯。",
|
||||
"rule": "範圍不完整時維持等待或補證,不得推動主要來源或收斂升級。",
|
||||
"guard": "不把單一負責人口頭確認當成全部 S4.9-S4.12 驗收完成。"
|
||||
},
|
||||
"secretValueQuarantine": {
|
||||
"title": "機密明文直接隔離",
|
||||
"body": "任何權杖、密碼、私鑰、webhook 機密或可重用憑證值都不能進一般收件。",
|
||||
"rule": "出現機密明文值時只能隔離並要求重提脫敏版本。",
|
||||
"guard": "不保存、不展示、不複製、不轉送、不旋轉任何機密明文值。"
|
||||
},
|
||||
"repoMutationRequest": {
|
||||
"title": "專案庫動作要求先拒收",
|
||||
"body": "人工回覆若夾帶建立、刪除、改可見性或轉移專案庫要求,必須與收件驗收分離。",
|
||||
"rule": "專案庫動作只能另走人工批准與回滾方案,不進收件作戰板。",
|
||||
"guard": "不建立 GitHub 專案庫、不停用 Gitea、不改專案庫可見性。"
|
||||
},
|
||||
"refsMutationRequest": {
|
||||
"title": "分支 / 標籤動作要求先拒收",
|
||||
"body": "人工回覆可以描述分支 / 標籤真相,但不能在同一包裡要求同步、刪除或強制推送。",
|
||||
"rule": "含分支 / 標籤異動要求時先拒收動作部分,只保留脫敏事實供人工審查。",
|
||||
"guard": "不同步、不刪除、不強制推送任何分支或標籤參照。"
|
||||
},
|
||||
"runtimeExecutionRequest": {
|
||||
"title": "執行要求一律另開閘門",
|
||||
"body": "任何 Kali、SSH、主機更新、掃描、修復或部署要求都不能混在負責人回覆驗收裡。",
|
||||
"rule": "執行要求只能另走人工批准、維護窗口、回滾與後驗證流程。",
|
||||
"guard": "不呼叫 Kali、不開 SSH、不更新主機、不建立執行期閘門。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"awooopReadOnlyLandingReadiness": {
|
||||
"title": "AwoooP Read-Only Landing Readiness",
|
||||
"subtitle": "S2.51 turns the AwoooP main-line read-only consumption path for IwoooS / security mirror state into an intake readiness board. This is landing readiness, not production_landing_enabled, and it does not connect an execution router.",
|
||||
|
||||
@@ -3060,6 +3060,70 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ownerResponseIntakeSafetyBoard": {
|
||||
"title": "人工回覆安全驗收閘道",
|
||||
"subtitle": "把收件後怎麼判定可收、補證、隔離或拒收先攤開。現在匯入=0、隔離=0、拒收=0;這只是驗收規則可見,不會自動匯入、通知、修復或升高限制。",
|
||||
"laneLabel": "驗收分流",
|
||||
"ruleLabel": "判定方式",
|
||||
"guardLabel": "仍不會做",
|
||||
"boundaryTitle": "驗收閘道維持的保護線",
|
||||
"summary": {
|
||||
"rules": {
|
||||
"label": "安全規則",
|
||||
"detail": "六條驗收分流先可見,避免收件後臨時判斷。"
|
||||
},
|
||||
"ingested": {
|
||||
"label": "已匯入",
|
||||
"detail": "目前為 0;還沒有任何人工回覆進入正式匯入。"
|
||||
},
|
||||
"quarantined": {
|
||||
"label": "已隔離",
|
||||
"detail": "目前為 0;若含機密明文值才會進隔離。"
|
||||
},
|
||||
"rejected": {
|
||||
"label": "已拒收",
|
||||
"detail": "目前為 0;拒收規則只是先讓邊界透明。"
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"redactedEvidenceOnly": {
|
||||
"title": "只接受脫敏證據",
|
||||
"body": "人工回覆必須能對照來源、負責人、範圍與遮罩後證據,才能進入驗收。",
|
||||
"rule": "缺少可驗證脫敏證據時,只能標記補證,不得匯入正式狀態。",
|
||||
"guard": "不把自由文字、截圖或未遮罩內容當成已接受回覆。"
|
||||
},
|
||||
"ownerScopeCompletion": {
|
||||
"title": "負責人範圍要齊全",
|
||||
"body": "每包回覆都要有負責人、專案庫或範圍、決策、證據指標與回滾關聯。",
|
||||
"rule": "範圍不完整時維持等待或補證,不得推動主要來源或收斂升級。",
|
||||
"guard": "不把單一負責人口頭確認當成全部 S4.9-S4.12 驗收完成。"
|
||||
},
|
||||
"secretValueQuarantine": {
|
||||
"title": "機密明文直接隔離",
|
||||
"body": "任何權杖、密碼、私鑰、webhook 機密或可重用憑證值都不能進一般收件。",
|
||||
"rule": "出現機密明文值時只能隔離並要求重提脫敏版本。",
|
||||
"guard": "不保存、不展示、不複製、不轉送、不旋轉任何機密明文值。"
|
||||
},
|
||||
"repoMutationRequest": {
|
||||
"title": "專案庫動作要求先拒收",
|
||||
"body": "人工回覆若夾帶建立、刪除、改可見性或轉移專案庫要求,必須與收件驗收分離。",
|
||||
"rule": "專案庫動作只能另走人工批准與回滾方案,不進收件作戰板。",
|
||||
"guard": "不建立 GitHub 專案庫、不停用 Gitea、不改專案庫可見性。"
|
||||
},
|
||||
"refsMutationRequest": {
|
||||
"title": "分支 / 標籤動作要求先拒收",
|
||||
"body": "人工回覆可以描述分支 / 標籤真相,但不能在同一包裡要求同步、刪除或強制推送。",
|
||||
"rule": "含分支 / 標籤異動要求時先拒收動作部分,只保留脫敏事實供人工審查。",
|
||||
"guard": "不同步、不刪除、不強制推送任何分支或標籤參照。"
|
||||
},
|
||||
"runtimeExecutionRequest": {
|
||||
"title": "執行要求一律另開閘門",
|
||||
"body": "任何 Kali、SSH、主機更新、掃描、修復或部署要求都不能混在負責人回覆驗收裡。",
|
||||
"rule": "執行要求只能另走人工批准、維護窗口、回滾與後驗證流程。",
|
||||
"guard": "不呼叫 Kali、不開 SSH、不更新主機、不建立執行期閘門。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"awooopReadOnlyLandingReadiness": {
|
||||
"title": "AwoooP 只讀接入就緒度",
|
||||
"subtitle": "S2.51 把 AwoooP 主線要如何只讀消費 IwoooS / 資安鏡像狀態整理成接入準備面板。這是接入就緒度,不是 production landing enabled,也不接 execution router。",
|
||||
|
||||
@@ -139,6 +139,14 @@ type OwnerResponseCollectionPacket = {
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
type OwnerResponseIntakeSafetyRule = {
|
||||
key: string
|
||||
lane: string
|
||||
value: string
|
||||
icon: typeof ShieldCheck
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
type CoverageGroup = {
|
||||
key: string
|
||||
count: string
|
||||
@@ -584,6 +592,34 @@ const ownerResponseCollectionBoundaries = [
|
||||
'workflow_modification_authorized=false',
|
||||
]
|
||||
|
||||
const ownerResponseIntakeSafetyRules: OwnerResponseIntakeSafetyRule[] = [
|
||||
{ key: 'redactedEvidenceOnly', lane: 'I1', value: '0', icon: ShieldCheck, tone: 'warn' },
|
||||
{ key: 'ownerScopeCompletion', lane: 'I2', value: '0', icon: ClipboardCheck, tone: 'warn' },
|
||||
{ key: 'secretValueQuarantine', lane: 'I3', value: '0', icon: Lock, tone: 'locked' },
|
||||
{ key: 'repoMutationRequest', lane: 'I4', value: '0', icon: GitBranch, tone: 'locked' },
|
||||
{ key: 'refsMutationRequest', lane: 'I5', value: '0', icon: SearchCheck, tone: 'locked' },
|
||||
{ key: 'runtimeExecutionRequest', lane: 'I6', value: '0', icon: AlertTriangle, tone: 'locked' },
|
||||
]
|
||||
|
||||
const ownerResponseIntakeSafetyBoundaries = [
|
||||
'owner_response_intake_safety_rule_count=6',
|
||||
'owner_response_payload_ingested_count=0',
|
||||
'owner_response_quarantine_count=0',
|
||||
'owner_response_rejection_count=0',
|
||||
'owner_response_auto_accept_allowed=false',
|
||||
'owner_response_secret_value_quarantine_required=true',
|
||||
'owner_response_mutation_request_allowed=false',
|
||||
'runtime_execution_authorized=false',
|
||||
'active_runtime_gate_count=0',
|
||||
'action_buttons_allowed=false',
|
||||
'not_authorization=true',
|
||||
'secret_value_collection_allowed=false',
|
||||
'repo_creation_authorized=false',
|
||||
'refs_sync_authorized=false',
|
||||
'workflow_modification_authorized=false',
|
||||
'kali_execute_authorized=false',
|
||||
]
|
||||
|
||||
const coverageGroups: CoverageGroup[] = [
|
||||
{
|
||||
key: 'signals',
|
||||
@@ -1809,6 +1845,126 @@ function OwnerResponseCollectionBoard() {
|
||||
)
|
||||
}
|
||||
|
||||
function OwnerResponseIntakeSafetyRuleCard({ item }: { item: OwnerResponseIntakeSafetyRule }) {
|
||||
const t = useTranslations('iwooos.ownerResponseIntakeSafetyBoard')
|
||||
const Icon = item.icon
|
||||
const textWrap = { overflowWrap: 'anywhere' as const, wordBreak: 'break-word' as const }
|
||||
return (
|
||||
<div style={{ ...band, minHeight: 190, padding: 16, ...textWrap }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
|
||||
<Icon size={18} color={toneColors[item.tone]} style={{ flex: '0 0 auto' }} />
|
||||
<span style={{ fontSize: 11, color: '#87867f' }}>{t('laneLabel')}</span>
|
||||
</div>
|
||||
<span style={{ fontSize: 11, color: toneColors[item.tone] }}>{item.lane}</span>
|
||||
</div>
|
||||
<div style={{ fontSize: 28, fontWeight: 700, color: toneColors[item.tone], marginTop: 12, lineHeight: 1 }}>
|
||||
{item.value}
|
||||
</div>
|
||||
<h2 style={{ fontSize: 14, margin: '10px 0 6px', color: '#141413' }}>
|
||||
{t(`items.${item.key}.title` as never)}
|
||||
</h2>
|
||||
<p style={{ fontSize: 12, lineHeight: 1.55, color: '#6f6d66', margin: 0, ...textWrap }}>
|
||||
{t(`items.${item.key}.body` as never)}
|
||||
</p>
|
||||
<div style={{ marginTop: 10, display: 'grid', gap: 5 }}>
|
||||
<span style={{ fontSize: 11, color: '#87867f' }}>{t('ruleLabel')}</span>
|
||||
<span style={{ fontSize: 11, color: toneColors[item.tone], lineHeight: 1.45, ...textWrap }}>
|
||||
{t(`items.${item.key}.rule` as never)}
|
||||
</span>
|
||||
<span style={{ fontSize: 11, color: '#87867f', marginTop: 4 }}>{t('guardLabel')}</span>
|
||||
<span style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, ...textWrap }}>
|
||||
{t(`items.${item.key}.guard` as never)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function OwnerResponseIntakeSafetyBoard() {
|
||||
const t = useTranslations('iwooos.ownerResponseIntakeSafetyBoard')
|
||||
const summaryItems = [
|
||||
{ key: 'rules', value: '6', tone: 'warn' as const },
|
||||
{ key: 'ingested', value: '0', tone: 'locked' as const },
|
||||
{ key: 'quarantined', value: '0', tone: 'locked' as const },
|
||||
{ key: 'rejected', value: '0', tone: 'locked' as const },
|
||||
]
|
||||
return (
|
||||
<section style={{ marginBottom: 14 }} data-testid="iwooos-owner-response-intake-safety-board">
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
<h2 style={{ fontSize: 16, margin: 0 }}>{t('title')}</h2>
|
||||
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55 }}>
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
gap: 10,
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
{summaryItems.map(item => (
|
||||
<div key={item.key} style={{ ...band, padding: 14, minHeight: 96 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
||||
<span style={{ fontSize: 11, color: '#87867f' }}>{t(`summary.${item.key}.label` as never)}</span>
|
||||
<ToneDot tone={item.tone} />
|
||||
</div>
|
||||
<div style={{ fontSize: 24, fontWeight: 700, lineHeight: 1, marginTop: 9, color: '#141413' }}>
|
||||
{item.value}
|
||||
</div>
|
||||
<p style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45, margin: '8px 0 0' }}>
|
||||
{t(`summary.${item.key}.detail` as never)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
{ownerResponseIntakeSafetyRules.map(item => (
|
||||
<OwnerResponseIntakeSafetyRuleCard key={item.key} item={item} />
|
||||
))}
|
||||
</div>
|
||||
<div style={{ ...band, marginTop: 12, padding: 16 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
|
||||
<ShieldCheck size={16} color={toneColors.locked} />
|
||||
<h3 style={{ fontSize: 14, margin: 0 }}>{t('boundaryTitle')}</h3>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(230px, 1fr))',
|
||||
gap: 7,
|
||||
}}
|
||||
>
|
||||
{ownerResponseIntakeSafetyBoundaries.map(item => (
|
||||
<span
|
||||
key={item}
|
||||
style={{
|
||||
border: '0.5px solid #eee9dd',
|
||||
borderRadius: 8,
|
||||
padding: '7px 9px',
|
||||
color: '#4f4c45',
|
||||
fontSize: 11,
|
||||
lineHeight: 1.4,
|
||||
overflowWrap: 'anywhere',
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function CoverageCard({ item }: { item: CoverageGroup }) {
|
||||
const t = useTranslations('iwooos.coverage')
|
||||
const Icon = item.icon
|
||||
@@ -2944,6 +3100,8 @@ export default function IwoooSPage({ params }: { params: { locale: string } }) {
|
||||
|
||||
<OwnerResponseCollectionBoard />
|
||||
|
||||
<OwnerResponseIntakeSafetyBoard />
|
||||
|
||||
<section style={{ marginBottom: 14 }}>
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
<h2 style={{ fontSize: 16, margin: 0 }}>{t('awooopReadOnlyLandingReadiness.title')}</h2>
|
||||
|
||||
Reference in New Issue
Block a user