From a185254e197cd1dc1115961641218ec0c9646b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Wed, 21 Oct 2026 15:12:41 +0800 Subject: [PATCH] =?UTF-8?q?chore(scripts):=20=E4=BE=A7=E8=BD=A6=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=8A=A0=E5=9B=BA=E2=80=94=E2=80=94site-id=20UUID=20?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E4=B8=8E=20audit=20=E9=9D=9E=20CDATA=20?= =?UTF-8?q?=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 评审两条非阻塞加固:--site-id 拼入 SQL 前过 UUID 形态白名单(非 UUID 拒绝并 明确报错);--audit 的 attachment_url 抽取正则同时匹配 CDATA 与裸文本两形态。 对真实数据重跑数字不变(859 条 / 855 对 836 命中,输出哈希一致)。 Co-Authored-By: Claude Code --- scripts/export-media-map.mjs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/export-media-map.mjs b/scripts/export-media-map.mjs index fbe8a3f..02557d9 100644 --- a/scripts/export-media-map.mjs +++ b/scripts/export-media-map.mjs @@ -37,7 +37,14 @@ const DEFAULT_CONTAINER = 'infrastructure-postgres-1' const DEFAULT_USER = 'cms' const DEFAULT_DB = 'cms' -/** 唯一一条对 cms 执行的 SQL(只读 SELECT;source_url IS NOT NULL——867 中 8 行 NULL 不带出) */ +/** site-id 白名单校验:UUID 形态(评审加固)——SQL 里 site_id 以字面量内插,非 UUID 输入在拼 SQL 前拒绝 */ +function assertValidSiteId(siteId) { + if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(siteId)) { + throw new Error(`--site-id 不是合法 UUID(36 位形态):${siteId}`) + } +} + +/** 唯一一条对 cms 执行的 SQL(只读 SELECT;site_id 已过 UUID 校验;source_url IS NOT NULL——867 中 8 行 NULL 不带出) */ function exportSql(siteId) { return ( `SELECT source_url, file_path FROM media_assets ` + @@ -90,9 +97,10 @@ async function auditAgainstWxr(mapPath, wxrPath) { const sidecarByExact = new Set(map.map((e) => e.sourceUrl)) const sidecarByNormalized = new Set(map.map((e) => normalizeUrl(e.sourceUrl))) const wxr = await readFile(wxrPath, 'utf8') - const attachmentUrls = [...wxr.matchAll(/<\/wp:attachment_url>/g)].map( - (m) => m[1], - ) + // CDATA 与裸文本两种形态都匹配(评审加固:WXR 导出器未必总包 CDATA);分支互斥,m[1] ?? m[2] + const attachmentUrls = [ + ...wxr.matchAll(/(?:|([^<]*))<\/wp:attachment_url>/g), + ].map((m) => m[1] ?? m[2] ?? '') const seen = new Set() const hitsExact = [] // 归一前即完全相等 const hitsNormalized = [] // 仅归一后相等(大小写/尾斜杠救回) @@ -156,6 +164,7 @@ function parseArgs(argv) { async function main() { const args = parseArgs(process.argv.slice(2)) + assertValidSiteId(args.siteId) const sql = exportSql(args.siteId) process.stderr.write(`[export-media-map] 只读查询:docker exec ${args.container} psql -U ${args.user} -d ${args.db}\n`)