fix(awooop): 遮罩前台專案與代理敏感識別
This commit is contained in:
@@ -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 ? (
|
||||
<>
|
||||
<DetailRow label={t("details.runId")} value={run.run_id} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.project")} value={run.project_id} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.agent")} value={run.agent_id} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.project")} value={publicProjectText(run.project_id)} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.agent")} value={publicAgentText(run.agent_id)} emptyLabel={t("empty")} />
|
||||
<DetailRow
|
||||
label={t("details.state")}
|
||||
value={
|
||||
<span className={cn("inline-flex border px-2 py-0.5 text-xs font-semibold", stateClass)}>
|
||||
{run.state || t("empty")}
|
||||
{publicInternalCodeSummary(run.state, t("empty"))}
|
||||
</span>
|
||||
}
|
||||
emptyLabel={t("empty")}
|
||||
/>
|
||||
<DetailRow label={t("details.traceId")} value={run.trace_id} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.trigger")} value={run.trigger_type} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.triggerRef")} value={run.trigger_ref} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.traceId")} value={run.trace_id ? "已脫敏追蹤碼" : null} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.trigger")} value={publicInternalCodeSummary(run.trigger_type, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailRow label={t("details.triggerRef")} value={publicInternalCodeSummary(run.trigger_ref, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailRow
|
||||
label={t("details.cost")}
|
||||
value={run.cost_usd === undefined ? null : `$${Number(run.cost_usd ?? 0).toFixed(4)}`}
|
||||
|
||||
@@ -30,7 +30,12 @@ import {
|
||||
type AwoooPStatusChain,
|
||||
} from "@/components/awooop/status-chain";
|
||||
import type { IncidentTimelineResponse } from "@/lib/api-client";
|
||||
import { publicBoundaryText, publicContractText } from "@/lib/public-security-redaction";
|
||||
import {
|
||||
publicAgentText,
|
||||
publicBoundaryText,
|
||||
publicContractText,
|
||||
publicProjectText,
|
||||
} from "@/lib/public-security-redaction";
|
||||
|
||||
// =============================================================================
|
||||
// Types
|
||||
@@ -565,14 +570,14 @@ function ApprovalRow({ approval }: { approval: Approval }) {
|
||||
</Link>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{approval.project_id || "--"}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{publicProjectText(approval.project_id)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex min-w-[180px] flex-col items-start">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{approval.agent_id || "--"}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{publicAgentText(approval.agent_id)}
|
||||
</span>
|
||||
{isGate5Projection && <Gate5ProjectionBadge />}
|
||||
</div>
|
||||
|
||||
@@ -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 }) {
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{contract.project_id || "--"}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{publicProjectText(contract.project_id)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
@@ -576,7 +576,7 @@ export default function ContractsPage() {
|
||||
<option value="">所有租戶</option>
|
||||
{tenants.map((t) => (
|
||||
<option key={t.project_id} value={t.project_id}>
|
||||
{t.display_name || t.project_id}
|
||||
{publicProjectText(t.project_id)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@@ -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({
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
|
||||
{[
|
||||
{ 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) => (
|
||||
<div key={item.label} className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
|
||||
@@ -854,7 +860,7 @@ function McpGatewayPanel({
|
||||
{hasRecords && summary?.blockers && summary.blockers.length > 0 && (
|
||||
<div className="border-t border-[#eee9dd] bg-[#fff0ef] px-4 py-3 text-sm text-[#9f2f25]">
|
||||
<span className="font-semibold">{t("blockers")}</span>{" "}
|
||||
<span className="font-mono text-xs">{summary.blockers.slice(0, 3).join(", ")}</span>
|
||||
<span className="text-xs">{publicInternalCodeSummary(summary.blockers.slice(0, 3))}</span>
|
||||
</div>
|
||||
)}
|
||||
{legacyTotal > 0 && (
|
||||
@@ -885,13 +891,13 @@ function McpGatewayPanel({
|
||||
{gatewayTools.map((item) => (
|
||||
<div key={`${item.agent_id ?? "gateway"}:${item.tool_name ?? "unknown"}`} className="grid gap-3 px-3 py-3 md:grid-cols-[1fr_112px]">
|
||||
<div className="min-w-0">
|
||||
<p className="truncate font-mono text-xs font-semibold text-[#141413]" title={item.tool_name ?? emptyLabel}>
|
||||
{item.tool_name ?? emptyLabel}
|
||||
<p className="truncate text-xs font-semibold text-[#141413]">
|
||||
{publicInternalCodeSummary(item.tool_name, emptyLabel)}
|
||||
</p>
|
||||
<p className="mt-1 truncate text-xs text-[#77736a]" title={item.agent_id ?? emptyLabel}>
|
||||
<p className="mt-1 truncate text-xs text-[#77736a]">
|
||||
{t("evidence.agentScope", {
|
||||
agent: item.agent_id ?? emptyLabel,
|
||||
scope: item.required_scope ?? emptyLabel,
|
||||
agent: publicAgentText(item.agent_id),
|
||||
scope: publicInternalCodeSummary(item.required_scope, emptyLabel),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
@@ -1522,16 +1528,16 @@ export default function RunDetailPage({
|
||||
<h3 className="text-sm font-semibold text-[#141413]">{t("summary.title")}</h3>
|
||||
</div>
|
||||
<div className="px-4">
|
||||
<DetailField label={t("summary.project")} value={run?.project_id} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.agent")} value={run?.agent_id} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.traceId")} value={run?.trace_id} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.trigger")} value={run?.trigger_type} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.triggerRef")} value={run?.trigger_ref} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.project")} value={run ? publicProjectText(run.project_id) : null} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.agent")} value={run ? publicAgentText(run.agent_id) : null} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.traceId")} value={run?.trace_id ? "已脫敏追蹤碼" : null} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.trigger")} value={publicInternalCodeSummary(run?.trigger_type, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.triggerRef")} value={publicInternalCodeSummary(run?.trigger_ref, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.cost")} value={run ? `$${Number(run.cost_usd ?? 0).toFixed(4)}` : null} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.attempts")} value={run ? `${run.attempt_count}/${run.max_attempts}` : null} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.created")} value={formatTime(run?.created_at, locale, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.completed")} value={formatTime(run?.completed_at, locale, t("empty"))} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.error")} value={run?.error_detail || run?.error_code} emptyLabel={t("empty")} />
|
||||
<DetailField label={t("summary.error")} value={run?.error_detail || run?.error_code ? "已脫敏錯誤摘要" : null} emptyLabel={t("empty")} />
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
|
||||
@@ -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({
|
||||
</Link>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{run.project_id || "--"}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{publicProjectText(run.project_id)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<IncidentIdsCell run={run} />
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
{run.agent_id || "--"}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{publicAgentText(run.agent_id)}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
@@ -2396,7 +2401,7 @@ function GroupedAlertEventsPanel({ events }: { events: PlatformEvent[] }) {
|
||||
{event.provider_event_id.replace("alert-group:", "")}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#77736a]">
|
||||
{event.project_id} · {receivedAt}
|
||||
{publicProjectText(event.project_id)} · {receivedAt}
|
||||
</p>
|
||||
</div>
|
||||
<span className="shrink-0 border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 text-xs text-[#5f5b52]">
|
||||
@@ -3436,7 +3441,7 @@ function CallbackReplyEvidencePanel({
|
||||
{event.run_id.slice(0, 8)}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-[#77736a]">
|
||||
{event.project_id} · {eventTime}
|
||||
{publicProjectText(event.project_id)} · {eventTime}
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
@@ -4435,7 +4440,7 @@ export default function RunsPage() {
|
||||
<option value="">所有租戶</option>
|
||||
{tenants.map((t) => (
|
||||
<option key={t.project_id} value={t.project_id}>
|
||||
{t.display_name || t.project_id}
|
||||
{publicProjectText(t.project_id)}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
@@ -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({
|
||||
<div className="min-w-0 break-words border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1.5">
|
||||
<p>
|
||||
{t("staleCandidates.singleItemRail.required", {
|
||||
fields: requiredOwnerFields.length > 0 ? requiredOwnerFields.join(", ") : "--",
|
||||
fields: publicInternalCodeSummary(requiredOwnerFields, "--"),
|
||||
})}
|
||||
</p>
|
||||
{blockers.length > 0 ? (
|
||||
<p className="text-[#9f2f25]">
|
||||
{t("staleCandidates.singleItemRail.blockers", {
|
||||
blockers: blockers.join(", "),
|
||||
blockers: publicInternalCodeSummary(blockers),
|
||||
})}
|
||||
</p>
|
||||
) : (
|
||||
@@ -5218,15 +5219,13 @@ function KnowledgeGovernancePanel({
|
||||
</p>
|
||||
<p className="break-words">
|
||||
{t("staleCandidates.completionQueue.required", {
|
||||
fields: item.required_owner_fields.length > 0
|
||||
? item.required_owner_fields.join(", ")
|
||||
: "--",
|
||||
fields: publicInternalCodeSummary(item.required_owner_fields, "--"),
|
||||
})}
|
||||
</p>
|
||||
{item.blockers.length > 0 ? (
|
||||
<p className="break-words text-[#9f2f25]">
|
||||
{t("staleCandidates.completionQueue.blockers", {
|
||||
blockers: item.blockers.join(", "),
|
||||
blockers: publicInternalCodeSummary(item.blockers),
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
@@ -75,6 +75,86 @@ export function publicContractText(value: string): string {
|
||||
return labels[value] ?? "已脫敏只讀證據參照";
|
||||
}
|
||||
|
||||
const PUBLIC_PROJECT_NAMES: Record<string, string> = {
|
||||
"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<string, string> = {
|
||||
"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> | 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, "已脫敏專案來源");
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user