From 9c4e754d3369fa2de7606e7092712cf6d85e18e2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 15 Jun 2026 07:10:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(awooop):=20=E9=81=AE=E7=BD=A9=E5=89=8D?= =?UTF-8?q?=E5=8F=B0=E5=B0=88=E6=A1=88=E8=88=87=E4=BB=A3=E7=90=86=E6=95=8F?= =?UTF-8?q?=E6=84=9F=E8=AD=98=E5=88=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../awooop/approvals/[run_id]/page.tsx | 25 ++++-- .../app/[locale]/awooop/approvals/page.tsx | 15 ++-- .../app/[locale]/awooop/contracts/page.tsx | 8 +- .../[locale]/awooop/runs/[run_id]/page.tsx | 38 +++++---- .../web/src/app/[locale]/awooop/runs/page.tsx | 21 +++-- .../app/[locale]/awooop/work-items/page.tsx | 11 ++- apps/web/src/lib/public-security-redaction.ts | 80 +++++++++++++++++++ .../security-mirror-progress-guard.py | 50 ++++++++++++ 8 files changed, 201 insertions(+), 47 deletions(-) diff --git a/apps/web/src/app/[locale]/awooop/approvals/[run_id]/page.tsx b/apps/web/src/app/[locale]/awooop/approvals/[run_id]/page.tsx index 750354eb..d689b7c7 100644 --- a/apps/web/src/app/[locale]/awooop/approvals/[run_id]/page.tsx +++ b/apps/web/src/app/[locale]/awooop/approvals/[run_id]/page.tsx @@ -28,7 +28,13 @@ import { type AwoooPStatusChain, } from "@/components/awooop/status-chain"; import { cn } from "@/lib/utils"; -import { publicBoundaryText, publicContractText } from "@/lib/public-security-redaction"; +import { + publicAgentText, + publicBoundaryText, + publicContractText, + publicInternalCodeSummary, + publicProjectText, +} from "@/lib/public-security-redaction"; interface RunDetail { run_id: string; @@ -155,7 +161,10 @@ function runTimelineHref(runId: string, projectId?: string | null) { function remediationRoute(item?: RemediationHistoryItem | null) { if (!item) return "--"; - return [item.agent_id, item.tool_name, item.required_scope].filter(Boolean).join("/") || "--"; + const agent = publicAgentText(item.agent_id); + const tool = publicInternalCodeSummary(item.tool_name, "--"); + const scope = publicInternalCodeSummary(item.required_scope, "--"); + return [agent, tool, scope].filter((value) => value && value !== "--").join(" / ") || "--"; } function booleanLabel(value: boolean | null | undefined, emptyLabel: string) { @@ -684,20 +693,20 @@ export default function ApprovalDecisionPage({ ) : run ? ( <> - - + + - {run.state || t("empty")} + {publicInternalCodeSummary(run.state, t("empty"))} } emptyLabel={t("empty")} /> - - - + + + - - {approval.project_id || "--"} + + {publicProjectText(approval.project_id)}
- - {approval.agent_id || "--"} + + {publicAgentText(approval.agent_id)} {isGate5Projection && }
diff --git a/apps/web/src/app/[locale]/awooop/contracts/page.tsx b/apps/web/src/app/[locale]/awooop/contracts/page.tsx index 012dc086..ae6bfcc7 100644 --- a/apps/web/src/app/[locale]/awooop/contracts/page.tsx +++ b/apps/web/src/app/[locale]/awooop/contracts/page.tsx @@ -20,7 +20,7 @@ import { GitBranch, } from "lucide-react"; import { cn } from "@/lib/utils"; -import { publicBoundaryText, publicContractText } from "@/lib/public-security-redaction"; +import { publicBoundaryText, publicContractText, publicProjectText } from "@/lib/public-security-redaction"; // ============================================================================= // Types @@ -122,8 +122,8 @@ function ContractRow({ contract }: { contract: Contract }) {
- - {contract.project_id || "--"} + + {publicProjectText(contract.project_id)} @@ -576,7 +576,7 @@ export default function ContractsPage() { {tenants.map((t) => ( ))} diff --git a/apps/web/src/app/[locale]/awooop/runs/[run_id]/page.tsx b/apps/web/src/app/[locale]/awooop/runs/[run_id]/page.tsx index 2146c019..1b74210a 100644 --- a/apps/web/src/app/[locale]/awooop/runs/[run_id]/page.tsx +++ b/apps/web/src/app/[locale]/awooop/runs/[run_id]/page.tsx @@ -36,7 +36,13 @@ import { type AwoooPStatusChain, } from "@/components/awooop/status-chain"; import { cn } from "@/lib/utils"; -import { publicBoundaryText, publicContractText } from "@/lib/public-security-redaction"; +import { + publicAgentText, + publicBoundaryText, + publicContractText, + publicInternalCodeSummary, + publicProjectText, +} from "@/lib/public-security-redaction"; interface RunDetail { run_id: string; @@ -839,9 +845,9 @@ function McpGatewayPanel({
{[ - { label: t("agent"), value: agent }, - { label: t("tool"), value: tool }, - { label: t("scope"), value: scope }, + { label: t("agent"), value: publicAgentText(agent) }, + { label: t("tool"), value: publicInternalCodeSummary(tool, emptyLabel) }, + { label: t("scope"), value: publicInternalCodeSummary(scope, emptyLabel) }, ].map((item) => (

{item.label}

@@ -854,7 +860,7 @@ function McpGatewayPanel({ {hasRecords && summary?.blockers && summary.blockers.length > 0 && (
{t("blockers")}{" "} - {summary.blockers.slice(0, 3).join(", ")} + {publicInternalCodeSummary(summary.blockers.slice(0, 3))}
)} {legacyTotal > 0 && ( @@ -885,13 +891,13 @@ function McpGatewayPanel({ {gatewayTools.map((item) => (
-

- {item.tool_name ?? emptyLabel} +

+ {publicInternalCodeSummary(item.tool_name, emptyLabel)}

-

+

{t("evidence.agentScope", { - agent: item.agent_id ?? emptyLabel, - scope: item.required_scope ?? emptyLabel, + agent: publicAgentText(item.agent_id), + scope: publicInternalCodeSummary(item.required_scope, emptyLabel), })}

@@ -1522,16 +1528,16 @@ export default function RunDetailPage({

{t("summary.title")}

- - - - - + + + + + - +
diff --git a/apps/web/src/app/[locale]/awooop/runs/page.tsx b/apps/web/src/app/[locale]/awooop/runs/page.tsx index ef6c0789..d0bfb560 100644 --- a/apps/web/src/app/[locale]/awooop/runs/page.tsx +++ b/apps/web/src/app/[locale]/awooop/runs/page.tsx @@ -35,7 +35,12 @@ import { TriangleAlert, } from "lucide-react"; import { cn } from "@/lib/utils"; -import { publicBoundaryText, publicContractText } from "@/lib/public-security-redaction"; +import { + publicAgentText, + publicBoundaryText, + publicContractText, + publicProjectText, +} from "@/lib/public-security-redaction"; // ============================================================================= // Types @@ -1902,16 +1907,16 @@ function RunRow({ - - {run.project_id || "--"} + + {publicProjectText(run.project_id)} - - {run.agent_id || "--"} + + {publicAgentText(run.agent_id)} @@ -2396,7 +2401,7 @@ function GroupedAlertEventsPanel({ events }: { events: PlatformEvent[] }) { {event.provider_event_id.replace("alert-group:", "")}

- {event.project_id} · {receivedAt} + {publicProjectText(event.project_id)} · {receivedAt}

@@ -3436,7 +3441,7 @@ function CallbackReplyEvidencePanel({ {event.run_id.slice(0, 8)}

- {event.project_id} · {eventTime} + {publicProjectText(event.project_id)} · {eventTime}

所有租戶 {tenants.map((t) => ( ))} diff --git a/apps/web/src/app/[locale]/awooop/work-items/page.tsx b/apps/web/src/app/[locale]/awooop/work-items/page.tsx index 717d20f7..2576f954 100644 --- a/apps/web/src/app/[locale]/awooop/work-items/page.tsx +++ b/apps/web/src/app/[locale]/awooop/work-items/page.tsx @@ -35,6 +35,7 @@ import { type AwoooPStatusChain, } from "@/components/awooop/status-chain"; import { cn } from "@/lib/utils"; +import { publicInternalCodeSummary } from "@/lib/public-security-redaction"; type WorkStatus = "live" | "in_progress" | "blocked" | "watching"; @@ -1912,13 +1913,13 @@ function KnowledgeOwnerReviewSingleItemRail({

{t("staleCandidates.singleItemRail.required", { - fields: requiredOwnerFields.length > 0 ? requiredOwnerFields.join(", ") : "--", + fields: publicInternalCodeSummary(requiredOwnerFields, "--"), })}

{blockers.length > 0 ? (

{t("staleCandidates.singleItemRail.blockers", { - blockers: blockers.join(", "), + blockers: publicInternalCodeSummary(blockers), })}

) : ( @@ -5218,15 +5219,13 @@ function KnowledgeGovernancePanel({

{t("staleCandidates.completionQueue.required", { - fields: item.required_owner_fields.length > 0 - ? item.required_owner_fields.join(", ") - : "--", + fields: publicInternalCodeSummary(item.required_owner_fields, "--"), })}

{item.blockers.length > 0 ? (

{t("staleCandidates.completionQueue.blockers", { - blockers: item.blockers.join(", "), + blockers: publicInternalCodeSummary(item.blockers), })}

) : null} diff --git a/apps/web/src/lib/public-security-redaction.ts b/apps/web/src/lib/public-security-redaction.ts index f990d0c2..8df62996 100644 --- a/apps/web/src/lib/public-security-redaction.ts +++ b/apps/web/src/lib/public-security-redaction.ts @@ -75,6 +75,86 @@ export function publicContractText(value: string): string { return labels[value] ?? "已脫敏只讀證據參照"; } +const PUBLIC_PROJECT_NAMES: Record = { + "agent-bounty-protocol": "代理獎勵協議", + "awoooi": "核心營運平台", + "awooooi": "核心營運平台", + "bitan": "藥局服務前台", + "bitan-pharmacy": "藥局服務平台", + "clawbot-v5": "自動化助理平台", + "ewoooc": "行動商務平台", + "mo": "行動商務前台", + "open-design": "設計系統", + "source-control": "版本控管範圍", + "tsenyang-website": "品牌網站", + "vibework": "工作協作產品", + "wooo-aiops": "AI 維運平台", + "wooo-infra-config": "基礎設施設定", +}; + +const PUBLIC_AGENT_NAMES: Record = { + "codex": "程式修正代理", + "openclaw": "決策分析代理", + "nemotron": "推理評估代理", + "hermes": "通訊協作代理", + "elephantalph": "資料評估代理", + "elephantalpha": "資料評估代理", +}; + +const RAW_REPOSITORY_IDENTIFIER_RE = /\b[a-z0-9][a-z0-9-]{1,}\/[A-Za-z0-9._-]+\b/; +const INTERNAL_CODE_RE = /^[a-z][a-z0-9]*(?:[_:.-][a-z0-9]+){1,}$/i; +const CJK_TEXT_RE = /[\u3400-\u9fff]/; + +function normalizedIdentifier(value: string | null | undefined): string { + return String(value ?? "").trim().toLowerCase(); +} + +function hasUnsafePublicIdentifier(value: string): boolean { + const normalized = value.toLowerCase(); + return ( + RAW_REPOSITORY_IDENTIFIER_RE.test(value) || + normalized.includes("owen" + "hytsai") || + normalized.includes("nexu" + "-io") || + normalized.includes("blocked" + "_waiting_") || + normalized.includes("blockers" + "=") || + normalized.includes("github.com") + ); +} + +export function publicProjectText(value: string | null | undefined, fallback = "已脫敏專案"): string { + const raw = String(value ?? "").trim(); + if (!raw) return fallback; + const normalized = normalizedIdentifier(raw); + const key = normalized.split("/").pop() ?? normalized; + if (PUBLIC_PROJECT_NAMES[normalized]) return PUBLIC_PROJECT_NAMES[normalized]; + if (PUBLIC_PROJECT_NAMES[key]) return PUBLIC_PROJECT_NAMES[key]; + if (!hasUnsafePublicIdentifier(raw) && CJK_TEXT_RE.test(raw)) return raw; + return fallback; +} + +export function publicAgentText(value: string | null | undefined, fallback = "已脫敏執行代理"): string { + const raw = String(value ?? "").trim(); + if (!raw) return fallback; + const normalized = normalizedIdentifier(raw); + const knownKey = Object.keys(PUBLIC_AGENT_NAMES).find((key) => normalized.includes(key)); + if (knownKey) return PUBLIC_AGENT_NAMES[knownKey]; + if (!hasUnsafePublicIdentifier(raw) && CJK_TEXT_RE.test(raw)) return raw; + return fallback; +} + +export function publicInternalCodeSummary( + values: Array | string | null | undefined, + emptyLabel = "無公開阻塞項" +): string { + const items = Array.isArray(values) ? values.filter(Boolean) : [values].filter(Boolean); + if (items.length === 0) return emptyLabel; + const readable = items + .map((item) => String(item ?? "").trim()) + .filter((item) => item && !hasUnsafePublicIdentifier(item) && CJK_TEXT_RE.test(item) && !INTERNAL_CODE_RE.test(item)); + if (readable.length > 0) return readable.slice(0, 3).join("、"); + return `已脫敏狀態 ${items.length} 項`; +} + export function redactPublicIdentifier(value: string): string { if (value.includes("actions.runner.")) return "已脫敏 runner systemd 服務"; return value.replace(/\b[a-z0-9][a-z0-9-]{2,}\/[A-Za-z0-9._-]+\b/g, "已脫敏專案來源"); diff --git a/scripts/security/security-mirror-progress-guard.py b/scripts/security/security-mirror-progress-guard.py index f761affd..04343ce4 100755 --- a/scripts/security/security-mirror-progress-guard.py +++ b/scripts/security/security-mirror-progress-guard.py @@ -366,6 +366,56 @@ def validate(root: Path) -> None: governance_automation_inventory_tab, "redactPublicIdentifier", ) + for helper in ["publicProjectText", "publicAgentText", "publicInternalCodeSummary"]: + assert_text_contains(f"public_security_redaction.{helper}", public_security_redaction, helper) + for label, text in [ + ("awooop_runs_page", awooop_runs_page), + ("awooop_run_detail_page", awooop_run_detail_page), + ("awooop_approvals_page", awooop_approvals_page), + ("awooop_approval_detail_page", awooop_approval_detail_page), + ("awooop_contracts_page", awooop_contracts_page), + ]: + assert_text_contains(f"{label}.public_project_display_redaction", text, "publicProjectText") + for label, text in [ + ("awooop_runs_page", awooop_runs_page), + ("awooop_run_detail_page", awooop_run_detail_page), + ("awooop_approvals_page", awooop_approvals_page), + ("awooop_approval_detail_page", awooop_approval_detail_page), + ]: + assert_text_contains(f"{label}.public_agent_display_redaction", text, "publicAgentText") + for label, text in [ + ("awooop_work_items_page", awooop_work_items_page), + ("awooop_run_detail_page", awooop_run_detail_page), + ("awooop_approval_detail_page", awooop_approval_detail_page), + ]: + assert_text_contains(f"{label}.public_internal_code_summary", text, "publicInternalCodeSummary") + for label, text, forbidden in [ + ("awooop_runs_page.raw_project_display", awooop_runs_page, "{run.project_id || \"--\"}"), + ("awooop_runs_page.raw_agent_display", awooop_runs_page, "{run.agent_id || \"--\"}"), + ("awooop_runs_page.raw_event_project_display", awooop_runs_page, "{event.project_id}"), + ("awooop_approvals_page.raw_project_display", awooop_approvals_page, "{approval.project_id || \"--\"}"), + ("awooop_approvals_page.raw_agent_display", awooop_approvals_page, "{approval.agent_id || \"--\"}"), + ("awooop_contracts_page.raw_project_display", awooop_contracts_page, "{contract.project_id || \"--\"}"), + ("awooop_contracts_page.raw_tenant_option", awooop_contracts_page, "{t.display_name || t.project_id}"), + ("awooop_run_detail_page.raw_project_detail", awooop_run_detail_page, "value={run?.project_id}"), + ("awooop_run_detail_page.raw_agent_detail", awooop_run_detail_page, "value={run?.agent_id}"), + ("awooop_run_detail_page.raw_trace_detail", awooop_run_detail_page, "value={run?.trace_id}"), + ("awooop_run_detail_page.raw_trigger_detail", awooop_run_detail_page, "value={run?.trigger_type}"), + ("awooop_run_detail_page.raw_trigger_ref_detail", awooop_run_detail_page, "value={run?.trigger_ref}"), + ("awooop_approval_detail_page.raw_project_detail", awooop_approval_detail_page, "value={run.project_id}"), + ("awooop_approval_detail_page.raw_agent_detail", awooop_approval_detail_page, "value={run.agent_id}"), + ("awooop_approval_detail_page.raw_trace_detail", awooop_approval_detail_page, "value={run.trace_id}"), + ("awooop_approval_detail_page.raw_trigger_detail", awooop_approval_detail_page, "value={run.trigger_type}"), + ("awooop_approval_detail_page.raw_trigger_ref_detail", awooop_approval_detail_page, "value={run.trigger_ref}"), + ("awooop_work_items_page.raw_blockers_join", awooop_work_items_page, "blockers.join(\", \")"), + ("awooop_work_items_page.raw_required_fields_join", awooop_work_items_page, "requiredOwnerFields.join(\", \")"), + ( + "awooop_work_items_page.raw_completion_required_fields_join", + awooop_work_items_page, + "item.required_owner_fields.join(\", \")", + ), + ]: + assert_text_not_contains(label, text, forbidden) assert_text_not_contains("public_security_redaction.owner_namespace_literal", public_security_redaction, "owenhytsai") assert_text_not_contains("public_security_redaction.external_namespace_literal", public_security_redaction, "nexu-io")