feat(web): add IwoooS S4.9 preflight
This commit is contained in:
@@ -2786,6 +2786,51 @@
|
||||
"guard": "No secret value collection, workflow mutation, runner enablement, or write token use."
|
||||
}
|
||||
}
|
||||
},
|
||||
"s49OwnerResponsePreflight": {
|
||||
"title": "S4.9 Owner Response Intake Preflight",
|
||||
"subtitle": "S2.48 surfaces the 6 S4.9 intake preflight checks in IwoooS: known attestation item, complete fields, allowed decision, redacted evidence, no execution request, and no accepted state until all five items are covered. This is preflight display only: no request send, received marking, or audit event creation.",
|
||||
"checkLabel": "Preflight",
|
||||
"failureLabel": "If it fails",
|
||||
"guardLabel": "Still forbidden",
|
||||
"items": {
|
||||
"knownAttestationItem": {
|
||||
"title": "Match a known S4.7 item",
|
||||
"body": "The owner response must map to public-only / local gap, org/user endpoint, 110 adjacent source, canonical owner, or legacy disposition.",
|
||||
"failure": "Unclear mapping can only request owner correction.",
|
||||
"guard": "Do not treat vague text as coverage attestation or auto-map it to an item."
|
||||
},
|
||||
"requiredOwnerFields": {
|
||||
"title": "Required owner fields complete",
|
||||
"body": "Each response needs owner role/team, decision, decision reason, affected scope, evidence refs, and followup owner.",
|
||||
"failure": "Missing fields can only request more evidence.",
|
||||
"guard": "No verbal OK and no approval record from incomplete responses."
|
||||
},
|
||||
"allowedDecision": {
|
||||
"title": "Decision is allowed",
|
||||
"body": "The decision must fit the acceptable decisions for the matching template so free text is not misread as authorization.",
|
||||
"failure": "Invalid decisions request owner correction.",
|
||||
"guard": "Do not upgrade OK / looks fine language into migration or primary approval."
|
||||
},
|
||||
"redactedEvidenceOnly": {
|
||||
"title": "Redacted evidence refs only",
|
||||
"body": "Evidence may only point to repo docs, snapshots, or redacted metadata pointers; no tokens, secrets, cookies, sessions, private keys, or private URL credentials.",
|
||||
"failure": "Sensitive payloads go to quarantine.",
|
||||
"guard": "No raw secret storage, DB dump import, git object pack, or repo archive collection."
|
||||
},
|
||||
"noExecutionRequest": {
|
||||
"title": "No execution request",
|
||||
"body": "Responses must not request Gitea/GitHub writes, repo creation, visibility changes, refs sync/delete/force-push, workflow/secret/runner changes, scans, or runtime actions.",
|
||||
"failure": "Embedded execution asks are rejected.",
|
||||
"guard": "No Gitea writes, GitHub repo creation, refs sync, or runtime gate opening."
|
||||
},
|
||||
"allFiveItemsBeforeAccepted": {
|
||||
"title": "All five items before Accepted",
|
||||
"body": "S4.9 cannot be accepted until all five response templates have acceptable owner responses.",
|
||||
"failure": "Partial responses remain waiting or request more evidence.",
|
||||
"guard": "Visible preflight is not request sent, received, accepted, or audit emitted."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tickets": {
|
||||
|
||||
@@ -2787,6 +2787,51 @@
|
||||
"guard": "不收 secret value、不改 workflow、不啟用 runner、不使用 write token。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"s49OwnerResponsePreflight": {
|
||||
"title": "S4.9 Owner Response 收件前 Preflight",
|
||||
"subtitle": "S2.48 把 S4.9 的 6 個收件前檢查拉到 IwoooS:先確認回覆是否對應已知 attestation item、欄位完整、decision 合法、evidence 脫敏、沒有執行要求,且五個 items 到齊前不得 accepted。這裡只顯示 preflight,不寄送 request、不標記 received、不建立 audit event。",
|
||||
"checkLabel": "Preflight",
|
||||
"failureLabel": "不通過時",
|
||||
"guardLabel": "仍禁止",
|
||||
"items": {
|
||||
"knownAttestationItem": {
|
||||
"title": "對應 S4.7 已知 item",
|
||||
"body": "owner response 必須明確對應 public-only / local gap、org/user endpoint、110 adjacent source、canonical owner 或 legacy disposition 其中之一。",
|
||||
"failure": "不明確時只能 request owner correction,不能進 accepted。",
|
||||
"guard": "不把模糊回覆當作 coverage attestation,也不自動補成某個 item。"
|
||||
},
|
||||
"requiredOwnerFields": {
|
||||
"title": "必填欄位完整",
|
||||
"body": "每筆回覆都要有 owner role/team、decision、decision reason、受影響 scope、evidence refs 與 followup owner。",
|
||||
"failure": "欄位不足只能 request more evidence。",
|
||||
"guard": "不接受口頭 OK、不用缺欄位回覆建立 approval record。"
|
||||
},
|
||||
"allowedDecision": {
|
||||
"title": "Decision 在允許值內",
|
||||
"body": "decision 必須落在該 template 允許的 acceptable decisions,避免自由文字被誤讀成授權。",
|
||||
"failure": "decision 不合規時 request owner correction。",
|
||||
"guard": "不把同意、可進行、看起來沒問題升級成 migration 或 primary approval。"
|
||||
},
|
||||
"redactedEvidenceOnly": {
|
||||
"title": "只接受脫敏 evidence refs",
|
||||
"body": "evidence 只能指向 repo 內文件、snapshot 或脫敏 metadata pointer,不貼 token、secret、cookie、session、private key 或私有 URL 憑證。",
|
||||
"failure": "出現敏感 payload 只能 quarantine sensitive payload。",
|
||||
"guard": "不保存 raw secret、不匯入 DB dump、不收 git object pack 或 repo archive。"
|
||||
},
|
||||
"noExecutionRequest": {
|
||||
"title": "不得夾帶執行要求",
|
||||
"body": "回覆不得要求 Gitea/GitHub 寫入、repo 建立、visibility 修改、refs sync/delete/force-push、workflow/secret/runner 變更、scan 或 runtime action。",
|
||||
"failure": "夾帶執行要求時 reject execution request。",
|
||||
"guard": "不寫 Gitea、不建 GitHub repo、不同步 refs、不開 runtime gate。"
|
||||
},
|
||||
"allFiveItemsBeforeAccepted": {
|
||||
"title": "五個 items 到齊前不得 Accepted",
|
||||
"body": "S4.9 要被標示 accepted 前,五個 response templates 都必須收到可驗收的 owner response。",
|
||||
"failure": "部分回覆只能維持 waiting 或 request more evidence。",
|
||||
"guard": "preflight 可見不代表 request sent、received、accepted 或 audit emitted。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tickets": {
|
||||
|
||||
@@ -45,6 +45,13 @@ type OwnerResponseNextActionFocusItem = {
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
type S49OwnerResponsePreflightCheck = {
|
||||
key: string
|
||||
check: string
|
||||
icon: typeof ShieldCheck
|
||||
tone: 'steady' | 'warn' | 'locked'
|
||||
}
|
||||
|
||||
type Pillar = {
|
||||
key: string
|
||||
icon: typeof ShieldCheck
|
||||
@@ -337,6 +344,15 @@ const ownerResponseNextActionFocusItems: OwnerResponseNextActionFocusItem[] = [
|
||||
{ key: 'workflowSecretOwnerResponse', focus: 'S4.12', icon: Lock, tone: 'locked' },
|
||||
]
|
||||
|
||||
const s49OwnerResponsePreflightChecks: S49OwnerResponsePreflightCheck[] = [
|
||||
{ key: 'knownAttestationItem', check: 'P1', icon: SearchCheck, tone: 'warn' },
|
||||
{ key: 'requiredOwnerFields', check: 'P2', icon: ListChecks, tone: 'warn' },
|
||||
{ key: 'allowedDecision', check: 'P3', icon: CheckCircle2, tone: 'warn' },
|
||||
{ key: 'redactedEvidenceOnly', check: 'P4', icon: Lock, tone: 'locked' },
|
||||
{ key: 'noExecutionRequest', check: 'P5', icon: AlertTriangle, tone: 'locked' },
|
||||
{ key: 'allFiveItemsBeforeAccepted', check: 'P6', icon: ShieldCheck, tone: 'locked' },
|
||||
]
|
||||
|
||||
const posturePillars: Pillar[] = [
|
||||
{ key: 'exposure', icon: Radar, tone: 'warn' },
|
||||
{ key: 'sourceControl', icon: GitBranch, tone: 'warn' },
|
||||
@@ -878,6 +894,38 @@ function OwnerResponseNextActionFocusCard({ item }: { item: OwnerResponseNextAct
|
||||
)
|
||||
}
|
||||
|
||||
function S49OwnerResponsePreflightCard({ item }: { item: S49OwnerResponsePreflightCheck }) {
|
||||
const t = useTranslations('iwooos.s49OwnerResponsePreflight')
|
||||
const Icon = item.icon
|
||||
return (
|
||||
<div style={{ ...band, minHeight: 178, 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('checkLabel')}</span>
|
||||
</div>
|
||||
<span style={{ fontSize: 11, color: toneColors[item.tone] }}>{item.check}</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('failureLabel')}</div>
|
||||
<div style={{ fontSize: 11, color: toneColors[item.tone], lineHeight: 1.45 }}>
|
||||
{t(`items.${item.key}.failure` as never)}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, color: '#87867f', marginTop: 4 }}>{t('guardLabel')}</div>
|
||||
<div style={{ fontSize: 11, color: '#6f6d66', lineHeight: 1.45 }}>
|
||||
{t(`items.${item.key}.guard` as never)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function PillarCard({ item }: { item: Pillar }) {
|
||||
const t = useTranslations('iwooos.pillars')
|
||||
const Icon = item.icon
|
||||
@@ -2105,6 +2153,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('s49OwnerResponsePreflight.title')}</h2>
|
||||
<p style={{ fontSize: 12, color: '#6f6d66', margin: '6px 0 0', lineHeight: 1.55 }}>
|
||||
{t('s49OwnerResponsePreflight.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
{s49OwnerResponsePreflightChecks.map(item => (
|
||||
<S49OwnerResponsePreflightCard key={item.key} item={item} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section style={{ marginBottom: 14 }}>
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
<h2 style={{ fontSize: 16, margin: 0 }}>{t('journey.title')}</h2>
|
||||
|
||||
Reference in New Issue
Block a user