Files
awoooi/apps/api/src/services/public_redaction.py
Your Name 2afb7c0ab9
All checks were successful
Code Review / ai-code-review (push) Successful in 34s
CD Pipeline / tests (push) Successful in 1m35s
CD Pipeline / build-and-deploy (push) Successful in 4m47s
CD Pipeline / post-deploy-checks (push) Successful in 1m34s
fix(governance): harden agent evidence redaction
2026-06-13 10:32:20 +08:00

103 lines
4.0 KiB
Python

"""
Public response redaction helpers.
These helpers preserve committed evidence semantics while preventing internal
LAN topology from being returned to browser-facing API responses.
"""
from __future__ import annotations
import re
from typing import Any
_ENDPOINT_ALIASES = {
"192.168.0.110:3001": "host:public-gateway/gitea",
"192.168.0.110:3002": "host:public-gateway/grafana",
"192.168.0.110:3100": "host:public-gateway/langfuse",
"192.168.0.110:5000": "host:public-gateway/registry",
"192.168.0.110:9000": "host:public-gateway/sentry",
"192.168.0.112:8080": "host:kali-readonly/scanner",
"192.168.0.188:11434": "host:observability-a/ollama",
"192.168.0.188:8088": "host:observability-a/openclaw-legacy",
"192.168.0.188:8089": "host:observability-a/openclaw",
"192.168.0.188:3301": "host:observability-a/signoz",
"192.168.0.188:5432": "host:observability-a/postgres",
"192.168.0.188:6380": "host:observability-a/redis",
}
_HOST_ALIASES = {
"192.168.0.110": "host:public-gateway",
"192.168.0.111": "host:dev-a",
"192.168.0.112": "host:kali-readonly",
"192.168.0.120": "host:k3s-control-a",
"192.168.0.121": "host:k3s-control-b",
"192.168.0.125": "host:edge-vip",
"192.168.0.168": "host:dev-b",
"192.168.0.188": "host:observability-a",
}
_PRIVATE_LAN_RE = re.compile(r"192\.168\.0\.\d{1,3}(?::\d{1,5})?")
_WORK_CONTEXT_REPLACEMENTS = {
"工作視窗": "內部協作環境",
"對話內容": "內部協作內容",
"批准!繼續": "內部短訊指令",
"批准!": "內部短訊指令",
"In app browser": "內部瀏覽器狀態",
"My request for Codex": "內部協作請求",
"browser_context": "redacted_browser_context",
"codex_user_message": "redacted_user_message",
"prompt_text": "redacted_prompt_text",
"source_thread_id": "redacted_thread_id",
"codex_delegation": "redacted_delegation",
"raw prompt": "未脫敏提示內容",
"raw_prompt": "redacted_prompt",
"private reasoning": "私有推理內容",
"private_reasoning": "redacted_private_reasoning",
"chain of thought": "推理鏈內容",
"chain_of_thought": "redacted_chain_of_thought",
"raw payload": "原始載荷",
"raw_payload": "redacted_payload",
"raw Telegram payload": "原始 Telegram 載荷",
"raw_telegram_payload": "redacted_telegram_payload",
"raw tool output": "原始工具輸出",
"raw_tool_output": "redacted_tool_output",
"authorization header": "授權標頭",
"authorization_header": "redacted_authorization_header",
"secret value": "機密明文",
"secret_value": "redacted_secret_value",
"work window transcript": "內部協作逐字稿",
"work_window_transcript": "redacted_work_window_transcript",
"internal collaboration transcript": "內部協作逐字稿",
}
def redact_public_lan_text(value: str) -> str:
"""Replace internal LAN addresses with public-safe asset aliases."""
redacted = value
for endpoint, alias in _ENDPOINT_ALIASES.items():
redacted = redacted.replace(f"http://{endpoint}", alias)
redacted = redacted.replace(f"https://{endpoint}", alias)
redacted = redacted.replace(endpoint, alias)
for host, alias in _HOST_ALIASES.items():
redacted = redacted.replace(f"http://{host}", alias)
redacted = redacted.replace(f"https://{host}", alias)
redacted = redacted.replace(host, alias)
redacted = _PRIVATE_LAN_RE.sub("host:internal-node", redacted)
for phrase, replacement in _WORK_CONTEXT_REPLACEMENTS.items():
redacted = redacted.replace(phrase, replacement)
return redacted
def redact_public_lan_topology(value: Any) -> Any:
"""Recursively redact internal LAN topology from JSON-compatible values."""
if isinstance(value, str):
return redact_public_lan_text(value)
if isinstance(value, list):
return [redact_public_lan_topology(item) for item in value]
if isinstance(value, dict):
return {key: redact_public_lan_topology(nested) for key, nested in value.items()}
return value