|
-
- {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")
|