reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,313 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
function parseArgs(argv) {
const args = { input: null, param: null, position: null, json: false, markdown: false };
for (let i = 2; i < argv.length; i++) {
const a = argv[i];
if (a === '--input' || a === '--curl-file' || a === '-i') args.input = argv[++i];
else if (a === '--param' || a === '-p') args.param = argv[++i];
else if (a === '--position') args.position = argv[++i];
else if (a === '--json') args.json = true;
else if (a === '--markdown') args.markdown = true;
else if (a === '--help' || a === '-h') args.help = true;
else throw new Error(`未知参数:${a}`);
}
if (!args.json && !args.markdown) args.markdown = true;
return args;
}
function usage() {
return `用法:\n node scripts/parse_curl.js --input request.curl --param sign --position header --markdown\n node scripts/parse_curl.js --input request.curl --json\n\n不提供 --input 时会从标准输入读取内容。脚本会同时列出请求样本中的所有可疑加密参数;这些值只能作为浏览器样本 / fixture,最终产物必须通过补环境重新生成,不能直接复用。`;
}
function readInput(input) {
if (input) return fs.readFileSync(input, 'utf8');
return fs.readFileSync(0, 'utf8');
}
function normalizeCurl(s) {
return s.replace(/\r\n/g, '\n')
.replace(/\\\n/g, ' ')
.replace(/\^\n/g, ' ')
.replace(/`\n/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function tokenize(s) {
const out = [];
let cur = '';
let quote = null;
let esc = false;
for (let i = 0; i < s.length; i++) {
const ch = s[i];
if (esc) { cur += ch; esc = false; continue; }
if (ch === '\\' && quote !== "'") { esc = true; continue; }
if (quote) {
if (ch === quote) quote = null;
else cur += ch;
continue;
}
if (ch === "'" || ch === '"') { quote = ch; continue; }
if (/\s/.test(ch)) {
if (cur) { out.push(cur); cur = ''; }
continue;
}
cur += ch;
}
if (cur) out.push(cur);
return out;
}
function parseHeader(v) {
const idx = v.indexOf(':');
if (idx === -1) return null;
return [v.slice(0, idx).trim(), v.slice(idx + 1).trim()];
}
function parseCurl(text) {
const tokens = tokenize(normalizeCurl(text));
const result = { url: '', method: '', headers: {}, body: '', cookies: {}, rawTokens: tokens };
let sawCurl = false;
for (let i = 0; i < tokens.length; i++) {
const t = tokens[i];
if (t === 'curl' || t.endsWith('/curl')) { sawCurl = true; continue; }
if (['-X', '--request'].includes(t)) { result.method = (tokens[++i] || '').toUpperCase(); continue; }
if (['-H', '--header'].includes(t)) {
const h = parseHeader(tokens[++i] || '');
if (h) result.headers[h[0]] = h[1];
continue;
}
if (['-d', '--data', '--data-raw', '--data-binary', '--data-urlencode', '--form'].includes(t)) {
const val = tokens[++i] || '';
result.body = result.body ? result.body + '&' + val : val;
continue;
}
if (t === '-b' || t === '--cookie') {
result.headers.Cookie = tokens[++i] || '';
continue;
}
if (!t.startsWith('-') && /^https?:\/\//i.test(t) && !result.url) result.url = t;
}
if (!sawCurl && !/^https?:\/\//i.test(result.url)) throw new Error('输入内容不像有效的 cURL 命令');
if (!result.method) result.method = result.body ? 'POST' : 'GET';
const cookieHeader = Object.entries(result.headers).find(([k]) => k.toLowerCase() === 'cookie');
if (cookieHeader) {
for (const part of cookieHeader[1].split(';')) {
const idx = part.indexOf('=');
if (idx !== -1) result.cookies[part.slice(0, idx).trim()] = part.slice(idx + 1).trim();
}
}
return result;
}
function headerHas(headers, param) {
const p = param.toLowerCase();
return Object.keys(headers).some(k => k.toLowerCase() === p);
}
function bodyHas(body, param) {
if (!body) return false;
const p = param.toLowerCase();
try {
const obj = JSON.parse(body);
const stack = [obj];
while (stack.length) {
const cur = stack.pop();
if (cur && typeof cur === 'object') {
for (const [k, v] of Object.entries(cur)) {
if (k.toLowerCase() === p) return true;
if (v && typeof v === 'object') stack.push(v);
}
}
}
} catch (_) {}
try {
const sp = new URLSearchParams(body);
for (const k of sp.keys()) if (k.toLowerCase() === p) return true;
} catch (_) {}
return new RegExp(`(^|[&?\\s"'])${param.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*[:=]`, 'i').test(body);
}
function queryHas(url, param) {
try {
const u = new URL(url);
for (const k of u.searchParams.keys()) if (k.toLowerCase() === param.toLowerCase()) return true;
} catch (_) {}
return false;
}
function cookieHas(cookies, param) {
const p = param.toLowerCase();
return Object.keys(cookies).some(k => k.toLowerCase() === p);
}
function maskValue(value) {
const s = String(value ?? '');
if (!s) return '';
if (s.length <= 8) return '*'.repeat(s.length);
return `${s.slice(0, 4)}...${s.slice(-4)}(len=${s.length})`;
}
function sha256(value) {
try { return require('crypto').createHash('sha256').update(String(value)).digest('hex'); }
catch { return ''; }
}
const BENIGN_PARAM_NAMES = new Set(['accept','accept-language','accept-encoding','content-type','content-length','origin','referer','host','connection','user-agent','sec-ch-ua','sec-fetch-site','sec-fetch-mode','sec-fetch-dest']);
const STRONG_CRYPTO_NAME = /^(?:sign|signature|_signature|x[-_]?sign|x[-_]?s|x[-_]?t|a[_-]?bogus|h5st|mtgsig|w[_-]?rid|x[-_]?bogus|token|access[-_]?token|auth[-_]?token|x[-_]?token|csrf[-_]?token|csrftoken|verify|digest|hash|hmac|mac|nonce|sig)$/i;
const WEAK_CRYPTO_NAME = /(^|[-_])(sign|signature|token|csrf|verify|digest|hash|hmac|mac|nonce|timestamp|ts|random|device|fp|fingerprint)([-_]|$)/i;
function looksEncodedOrCrypto(value) {
const s = String(value || '');
if (s.length >= 32) return true;
if (s.length >= 24 && /^[A-Za-z0-9+/=_-]+$/.test(s)) return true;
if (s.length >= 24 && /^[a-f0-9]+$/i.test(s)) return true;
if (s.length >= 20 && /%[0-9a-f]{2}/i.test(s)) return true;
return false;
}
function scoreSuspiciousParam(name, value, position) {
const n = String(name || '').trim();
const v = String(value ?? '');
const reasons = [];
let score = 0;
if (!n || BENIGN_PARAM_NAMES.has(n.toLowerCase())) return { score: 0, reasons: ['常规请求字段,默认排除'] };
if (STRONG_CRYPTO_NAME.test(n)) { score += 75; reasons.push('参数名命中常见加密 / 签名 / token 规则'); }
else if (WEAK_CRYPTO_NAME.test(n)) { score += 45; reasons.push('参数名疑似签名、token、随机数、设备或指纹字段'); }
if (looksEncodedOrCrypto(v)) { score += 35; reasons.push('样本值长度或编码形态像签名 / token'); }
if (position === 'Header') score += 8;
if (position === 'Cookie') { score += 8; reasons.push('位于 Cookie,需区分登录态与 JS 生成型 Cookie'); }
if (/^\d{10,13}$/.test(v) && !STRONG_CRYPTO_NAME.test(n)) { score -= 20; reasons.push('值像纯时间戳,需结合链路确认'); }
if (v.length <= 6 && !STRONG_CRYPTO_NAME.test(n)) score -= 20;
return { score, reasons };
}
function addSuspiciousCandidate(map, name, value, position, sourcePath) {
if (value === undefined || value === null || value === '') return;
const { score, reasons } = scoreSuspiciousParam(name, value, position);
if (score < 35) return;
const key = `${position}:${String(name).toLowerCase()}:${sourcePath}`;
if (map.has(key)) return;
map.set(key, {
name: String(name),
position,
sourcePath,
samplePreview: maskValue(value),
valueLength: String(value).length,
valueSha256: sha256(value),
score,
reasons,
mustGenerate: true,
});
}
function collectJsonSuspicious(obj, map, prefix = 'body') {
if (!obj || typeof obj !== 'object') return;
for (const [k, v] of Object.entries(obj)) {
const p = `${prefix}.${k}`;
if (v && typeof v === 'object') collectJsonSuspicious(v, map, p);
else addSuspiciousCandidate(map, k, v, 'Body', p);
}
}
function collectBodySuspicious(body, map) {
if (!body) return;
try { collectJsonSuspicious(JSON.parse(body), map, 'body'); return; } catch (_) {}
try {
const sp = new URLSearchParams(body);
for (const [k, v] of sp.entries()) addSuspiciousCandidate(map, k, v, 'Body', `body.${k}`);
} catch (_) {}
const re = /["']?([A-Za-z_$][\w$-]{0,80})["']?\s*[:=]\s*["']?([^&\s"',}]{4,})/g;
let m;
while ((m = re.exec(body))) addSuspiciousCandidate(map, m[1], m[2], 'Body', `body.${m[1]}`);
}
function discoverSuspiciousParams(req) {
const map = new Map();
try {
const u = new URL(req.url);
for (const [k, v] of u.searchParams.entries()) addSuspiciousCandidate(map, k, v, 'Query', `query.${k}`);
} catch (_) {}
for (const [k, v] of Object.entries(req.headers || {})) addSuspiciousCandidate(map, k, v, 'Header', `header.${k}`);
for (const [k, v] of Object.entries(req.cookies || {})) addSuspiciousCandidate(map, k, v, 'Cookie', `cookie.${k}`);
collectBodySuspicious(req.body, map);
return [...map.values()].sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
}
function analyze(text, param, position) {
const req = parseCurl(text);
let paramPresence = null;
if (param) {
paramPresence = {
query: queryHas(req.url, param),
header: headerHas(req.headers, param),
body: bodyHas(req.body, param),
cookie: cookieHas(req.cookies, param),
};
if (position) {
const key = position.toLowerCase();
paramPresence.expectedPosition = key;
paramPresence.foundAtExpectedPosition = !!paramPresence[key];
}
}
const suspiciousParams = discoverSuspiciousParams(req);
return { request: req, param, position, paramPresence, suspiciousParams };
}
function renderMarkdown(result) {
const req = result.request;
const lines = [];
lines.push('# cURL 请求样本检查结果');
lines.push('');
lines.push(`- URL:${req.url || '未识别'}`);
lines.push(`- 请求方法:${req.method || '未识别'}`);
lines.push(`- 请求头数量:${Object.keys(req.headers).length}`);
lines.push(`- 是否有请求体:${req.body ? '是' : '否'}`);
lines.push(`- Cookie 数量:${Object.keys(req.cookies).length}`);
if (result.param) {
const p = result.paramPresence;
lines.push('');
lines.push(`## 目标参数 \`${result.param}\` 检查`);
lines.push(`- Query:${p.query ? '找到' : '未找到'}`);
lines.push(`- Header:${p.header ? '找到' : '未找到'}`);
lines.push(`- Body:${p.body ? '找到' : '未找到'}`);
lines.push(`- Cookie:${p.cookie ? '找到' : '未找到'}`);
if (result.position) lines.push(`- 期望位置 ${result.position}:${p.foundAtExpectedPosition ? '匹配' : '不匹配'}`);
}
lines.push('');
lines.push('## 发现的可疑加密参数(需用户确认)');
if (!result.suspiciousParams.length) {
lines.push('- 未发现明显可疑加密参数;请结合 Network / Hook / HAR 继续确认。');
} else {
lines.push('| 参数名 | 位置 | 样本值摘要 | 分数 | 命中原因 |');
lines.push('|---|---|---|---:|---|');
for (const item of result.suspiciousParams) {
lines.push(`| ${item.name} | ${item.position} | ${item.samplePreview} | ${item.score} | ${item.reasons.join(';')} |`);
}
lines.push('');
lines.push('请让用户确认哪些参数需要分析。进入正式补环境前,必须把确认后的参数写入任务摘要;未确认前不要开始补环境。');
}
lines.push('');
lines.push('> 注意:cURL 中已有的 sign / token / a_bogus / h5st 等值只能作为浏览器真实样本或 fixture 期望值,最终产物必须通过补环境调用目标 JS 入口重新生成,不能把样本值硬编码或直接复用。');
return lines.join('\n') + '\n';
}
try {
const args = parseArgs(process.argv);
if (args.help) { console.log(usage()); process.exit(0); }
const text = readInput(args.input);
const result = analyze(text, args.param, args.position);
if (args.json) console.log(JSON.stringify(result, null, 2));
if (args.markdown) process.stdout.write(renderMarkdown(result));
} catch (err) {
console.error(err.message || String(err));
console.error(usage());
process.exit(1);
}
module.exports = { parseCurl, analyze, discoverSuspiciousParams, maskValue };