96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
|
|
SENSITIVE_KEYS = (
|
|
"pub_key",
|
|
"signature",
|
|
"key_b64",
|
|
"flash_key_b64",
|
|
"sig_b64",
|
|
"pub_key_jkt",
|
|
"new_pub_key",
|
|
"new_pub_key_jkt",
|
|
"device_mac",
|
|
"operator",
|
|
"host",
|
|
"port",
|
|
"firmware_version",
|
|
)
|
|
|
|
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[A-Za-z]")
|
|
_MAC_RE = re.compile(r"(?i)([0-9a-f]{2}:){5}[0-9a-f]{2}")
|
|
_UUID_RE = re.compile(
|
|
r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b"
|
|
)
|
|
_URL_RE = re.compile(r"https?://[^\s]+")
|
|
_DEVICE_ID_JSON_RE = re.compile(r'("device_id"\s*:\s*")([^"]+)(")', re.IGNORECASE)
|
|
_DEVICE_ID_KV_RE = re.compile(r"(\bdevice_id\b\s*[:=]\s*)([^\s,;]+)", re.IGNORECASE)
|
|
_JSON_KEY_RE = re.compile(
|
|
r'([\'"](?:(?:' + "|".join(SENSITIVE_KEYS) + r'))[\'"]\s*:\s*[\'"])([^\'"]+)([\'"])',
|
|
re.IGNORECASE,
|
|
)
|
|
_KV_RE = re.compile(r"(?i)\b(" + "|".join(SENSITIVE_KEYS) + r")=([^\s,;]+)")
|
|
_HEX_BYTES_RE = re.compile(r"\b(?:[0-9a-fA-F]{2}\s+){7,}[0-9a-fA-F]{2}\b")
|
|
_BIN_PATH_RE = re.compile(r"(/[^\\s]+?\\.(?:bin(?:\\.enc)?|enc))")
|
|
_BIN_NAME_RE = re.compile(r"\b[\w.-]+\\.(?:bin(?:\\.enc)?|enc)\b")
|
|
|
|
|
|
def _sanitize_filename(value: str) -> str:
|
|
if not value:
|
|
return "unknown"
|
|
return re.sub(r"[^A-Za-z0-9._-]+", "_", value)
|
|
|
|
|
|
def _preserve_device_ids(text: str) -> tuple[str, dict[str, str]]:
|
|
tokens: dict[str, str] = {}
|
|
|
|
def _tokenize(value: str) -> str:
|
|
token = f"__DEVICE_ID_{len(tokens)}__"
|
|
tokens[token] = value
|
|
return token
|
|
|
|
def _replace_json(match: re.Match[str]) -> str:
|
|
return f"{match.group(1)}{_tokenize(match.group(2))}{match.group(3)}"
|
|
|
|
def _replace_kv(match: re.Match[str]) -> str:
|
|
return f"{match.group(1)}{_tokenize(match.group(2))}"
|
|
|
|
text = _DEVICE_ID_JSON_RE.sub(_replace_json, text)
|
|
text = _DEVICE_ID_KV_RE.sub(_replace_kv, text)
|
|
return text, tokens
|
|
|
|
|
|
def _redact_sensitive(text: str) -> str:
|
|
if not text:
|
|
return text
|
|
text, device_tokens = _preserve_device_ids(text)
|
|
text = _ANSI_RE.sub("", text)
|
|
text = _MAC_RE.sub("<mac>", text)
|
|
text = _UUID_RE.sub("<id>", text)
|
|
text = _URL_RE.sub("<url>", text)
|
|
text = _JSON_KEY_RE.sub(
|
|
lambda m: f"{m.group(1)}<redacted:{len(m.group(2))}>{m.group(3)}", text
|
|
)
|
|
text = _KV_RE.sub(
|
|
lambda m: f"{m.group(1)}=<redacted:{len(m.group(2))}>", text
|
|
)
|
|
text = _HEX_BYTES_RE.sub("<redacted:hex>", text)
|
|
text = _BIN_PATH_RE.sub(lambda m: os.path.basename(m.group(1)), text)
|
|
text = _BIN_NAME_RE.sub("<bin>", text)
|
|
lowered = text.lower()
|
|
if "security features enabled" in lowered:
|
|
return "检测到限制,保持默认配置。"
|
|
if "compress and encrypt options are mutually exclusive" in lowered:
|
|
return "写入选项冲突,已使用默认方式。"
|
|
if "will flash input bytes uncompressed" in lowered:
|
|
return "将按默认方式写入。"
|
|
if "cannot verify written data" in lowered:
|
|
return "写入完成,校验已跳过。"
|
|
if "secure boot detected" in lowered or "secure download mode is enabled" in lowered:
|
|
return "检测到写入限制,请按工艺选择模式。"
|
|
for token, value in device_tokens.items():
|
|
text = text.replace(token, value)
|
|
return text
|