305 lines
9.9 KiB
JavaScript
305 lines
9.9 KiB
JavaScript
const path = require("path");
|
|
|
|
function step(id, script, options = {}) {
|
|
return {
|
|
id,
|
|
script,
|
|
timeoutMs: 120000,
|
|
...options
|
|
};
|
|
}
|
|
|
|
const STEP_LIBRARY = {
|
|
normalize: step("normalize", "normalize-structure.js", { timeoutMs: 60000 }),
|
|
prune: step("prune", "prune-fake-branches.js", { timeoutMs: 60000 }),
|
|
inlineDispatchers: step("inline_dispatchers", "inline-dispatchers.js", { timeoutMs: 120000 }),
|
|
flatten: step("flatten", "flatten-array-control-flow.js", { timeoutMs: 180000 }),
|
|
ifToSwitch: step("if_to_switch", "if-chain-to-switch.js", { timeoutMs: 90000 }),
|
|
inlineLiterals: step("inline_literals", "inline-literals.js", { timeoutMs: 90000 }),
|
|
rename: step("rename_identifiers", "rename-identifiers.js", { timeoutMs: 120000 }),
|
|
reese84HeavyPass: step("reese84_heavy_pass", "patterns/reese84-heavy-pass.js", { timeoutMs: 30000 }),
|
|
dingxiangArrayPass: step("dingxiang_array_pass", "patterns/dingxiang-array-pass.js", { timeoutMs: 30000 }),
|
|
geetest4GuardedPass: step("geetest4_guarded_pass", "patterns/geetest4-guarded-pass.js", { timeoutMs: 30000 }),
|
|
tonghuashunOrderPass: step("tonghuashun_order_pass", "patterns/tonghuashun-order-pass.js", { timeoutMs: 30000 }),
|
|
yidunDispatcherPass: step("yidun_dispatcher_pass", "patterns/yidun-dispatcher-pass.js", { timeoutMs: 30000 }),
|
|
xiaohongshuWrapperPass: step("xiaohongshu_wrapper_pass", "patterns/xiaohongshu-wrapper-pass.js", { timeoutMs: 30000 }),
|
|
obVariantPass: step("ob_variant_pass", "patterns/ob-variant-pass.js", { timeoutMs: 360000 })
|
|
};
|
|
|
|
const BASE_PIPELINE = [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize
|
|
];
|
|
|
|
const PATTERNS = [
|
|
{
|
|
id: "generic",
|
|
displayName: "generic layered pipeline",
|
|
reference: null,
|
|
hintTokens: [],
|
|
contentRegexes: [],
|
|
families: ["generic"],
|
|
notes: [
|
|
"Use only the generic passes.",
|
|
"Escalate to a site adapter when residue is stable and reproducible."
|
|
],
|
|
steps: BASE_PIPELINE
|
|
},
|
|
{
|
|
id: "reese84",
|
|
displayName: "reese84",
|
|
reference: "references/patterns/reese84.md",
|
|
hintTokens: ["reese84"],
|
|
contentRegexes: [/reese84/i],
|
|
families: ["vm-heavy", "dispatcher-heavy"],
|
|
notes: [
|
|
"Skip late inline-literals by default because large reese84 samples can stall there.",
|
|
"Prefer comparing dispatcher residue and flattened handlers against the reference decode."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.reese84HeavyPass,
|
|
STEP_LIBRARY.rename
|
|
]
|
|
},
|
|
{
|
|
id: "dingxiang",
|
|
displayName: "dingxiang",
|
|
reference: "references/patterns/dingxiang.md",
|
|
hintTokens: ["\u9876\u8c61", "dingxiang", "dx"],
|
|
contentRegexes: [/dingxiang/i],
|
|
families: ["param-array", "dispatcher-heavy"],
|
|
notes: [
|
|
"Resolve IIFE parameter backed arrays before judging the flattener result.",
|
|
"Do not force a second inline-literals pass unless the file stays small."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.dingxiangArrayPass,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize
|
|
]
|
|
},
|
|
{
|
|
id: "geetest4",
|
|
displayName: "geetest4",
|
|
reference: "references/patterns/geetest4.md",
|
|
hintTokens: ["\u6781\u9a8c4", "geetest4", "geetest"],
|
|
contentRegexes: [/geetest/i],
|
|
families: ["guarded-switch", "vm-heavy"],
|
|
notes: [
|
|
"Keep the guarded-if adapter outside the generic flattener unless the pattern becomes broadly reusable.",
|
|
"Current focus is reducing direct loop-switch residue before touching naming."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.geetest4GuardedPass,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize
|
|
]
|
|
},
|
|
{
|
|
id: "tonghuashun",
|
|
displayName: "tonghuashun",
|
|
reference: "references/patterns/tonghuashun.md",
|
|
hintTokens: ["\u540c\u82b1\u987a", "tonghuashun", "10jqka"],
|
|
contentRegexes: [/10jqka/i, /split\(\s*["']\|["']\s*\)/],
|
|
families: ["split-order-source", "dispatcher-wrapper"],
|
|
notes: [
|
|
"Add order-source adapters around nested wrapper objects instead of bloating the generic flattener.",
|
|
"This pipeline intentionally skips inline-literals because it is not the main blocker here."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.tonghuashunOrderPass,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize
|
|
]
|
|
},
|
|
{
|
|
id: "yidun",
|
|
displayName: "yidun",
|
|
reference: "references/patterns/yidun.md",
|
|
hintTokens: ["\u7f51\u6613\u6613\u76fe", "yidun", "163"],
|
|
contentRegexes: [/yidun/i],
|
|
families: ["dispatcher-heavy"],
|
|
notes: [
|
|
"One inline-literals pass can still help here, but keep it early enough to avoid blowups.",
|
|
"Compare remaining dispatcher wrappers against the reference decode."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.yidunDispatcherPass,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.inlineLiterals,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.rename
|
|
]
|
|
},
|
|
{
|
|
id: "xiaohongshu",
|
|
displayName: "xiaohongshu",
|
|
reference: "references/patterns/xiaohongshu.md",
|
|
hintTokens: ["\u5c0f\u7ea2\u4e66", "xiaohongshu", "xhs"],
|
|
contentRegexes: [/xiaohongshu/i, /\bxhs\b/i],
|
|
families: ["wrapper-light"],
|
|
notes: [
|
|
"Treat this as a lighter wrapper cleanup path.",
|
|
"Prefer readability and structural cleanup over aggressive evaluation."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.xiaohongshuWrapperPass,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.xiaohongshuWrapperPass,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.rename
|
|
]
|
|
},
|
|
{
|
|
id: "cn-bidding-ob",
|
|
displayName: "cn-bidding-ob",
|
|
reference: "references/patterns/cn-bidding-ob.md",
|
|
hintTokens: ["\u4e2d\u56fd\u62db\u6807\u6295\u6807\u516c\u5171\u670d\u52a1\u5e73\u53f0", "\u62db\u6807\u6295\u6807", "ob"],
|
|
contentRegexes: [/\b_0x[a-f0-9]+\b/i],
|
|
families: ["ob-variant"],
|
|
notes: [
|
|
"Keep OB-family tweaks in a dedicated adapter so the common pipeline stays conservative.",
|
|
"Track remaining loop-switch residue after the OB adapter stage."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.obVariantPass,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.obVariantPass,
|
|
STEP_LIBRARY.normalize
|
|
]
|
|
},
|
|
{
|
|
id: "mps-ob",
|
|
displayName: "mps-ob",
|
|
reference: "references/patterns/mps-ob.md",
|
|
hintTokens: ["\u4e2d\u534e\u4eba\u6c11\u5171\u548c\u56fd\u516c\u5b89\u90e8", "\u516c\u5b89\u90e8", "mps", "ob"],
|
|
contentRegexes: [/\b_0x[a-f0-9]+\b/i],
|
|
families: ["ob-variant"],
|
|
notes: [
|
|
"Share the OB-family adapter but keep a separate rule document because the residue profile is different from the bidding sample.",
|
|
"Compare remaining loop-switch blocks directly against the reference decode."
|
|
],
|
|
steps: [
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.inlineDispatchers,
|
|
STEP_LIBRARY.obVariantPass,
|
|
STEP_LIBRARY.flatten,
|
|
STEP_LIBRARY.ifToSwitch,
|
|
STEP_LIBRARY.prune,
|
|
STEP_LIBRARY.obVariantPass,
|
|
STEP_LIBRARY.normalize,
|
|
STEP_LIBRARY.rename
|
|
]
|
|
}
|
|
];
|
|
|
|
function normalizeHint(value) {
|
|
return String(value || "").toLowerCase();
|
|
}
|
|
|
|
function collectDetection(pattern, inputPath, hint, sourceText) {
|
|
let score = 0;
|
|
const reasons = [];
|
|
const haystacks = [normalizeHint(inputPath), normalizeHint(hint)];
|
|
for (const token of pattern.hintTokens) {
|
|
const normalized = normalizeHint(token);
|
|
if (!normalized) {
|
|
continue;
|
|
}
|
|
if (haystacks.some((value) => value.includes(normalized))) {
|
|
score += 10;
|
|
reasons.push(`hint:${token}`);
|
|
}
|
|
}
|
|
for (const regex of pattern.contentRegexes) {
|
|
if (regex.test(sourceText)) {
|
|
score += 3;
|
|
reasons.push(`content:${regex}`);
|
|
}
|
|
}
|
|
return {
|
|
id: pattern.id,
|
|
score,
|
|
reasons
|
|
};
|
|
}
|
|
|
|
function detectPatterns({ inputPath, hint = "", sourceText }) {
|
|
const detections = PATTERNS
|
|
.filter((pattern) => pattern.id !== "generic")
|
|
.map((pattern) => collectDetection(pattern, inputPath, hint, sourceText))
|
|
.filter((entry) => entry.score > 0)
|
|
.sort((left, right) => right.score - left.score || left.id.localeCompare(right.id));
|
|
|
|
const bestId = detections.length > 0 ? detections[0].id : "generic";
|
|
return {
|
|
bestId,
|
|
detections,
|
|
selected: getPattern(bestId)
|
|
};
|
|
}
|
|
|
|
function getPattern(id) {
|
|
return PATTERNS.find((pattern) => pattern.id === id) || PATTERNS[0];
|
|
}
|
|
|
|
function buildPipeline(patternId) {
|
|
const pattern = getPattern(patternId);
|
|
return pattern.steps.map((entry) => ({
|
|
...entry,
|
|
scriptPath: path.join(__dirname, entry.script)
|
|
}));
|
|
}
|
|
|
|
module.exports = {
|
|
BASE_PIPELINE,
|
|
PATTERNS,
|
|
STEP_LIBRARY,
|
|
buildPipeline,
|
|
detectPatterns,
|
|
getPattern
|
|
};
|