reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
const MODULE_PRIORITY = [
|
||||
"prototype-builder",
|
||||
"descriptor-guard",
|
||||
"native-protector",
|
||||
"navigator-module",
|
||||
"document-module",
|
||||
"document-all-module",
|
||||
"storage-module",
|
||||
"fingerprint-module",
|
||||
"performance-module",
|
||||
"crypto-module",
|
||||
"audio-fingerprint-module",
|
||||
"webrtc-module",
|
||||
"worker-module",
|
||||
"stack-clean-module",
|
||||
];
|
||||
|
||||
function toArray(value) {
|
||||
if (Array.isArray(value)) return value;
|
||||
if (value && Array.isArray(value.logs)) return value.logs;
|
||||
return [];
|
||||
}
|
||||
|
||||
function unique(values) {
|
||||
return Array.from(new Set(values));
|
||||
}
|
||||
|
||||
function extractPathList(input, key, type) {
|
||||
if (Array.isArray(input[key])) return unique(input[key]);
|
||||
return unique(
|
||||
toArray(input)
|
||||
.filter((item) => item && item.type === type && typeof item.path === "string")
|
||||
.map((item) => item.path)
|
||||
);
|
||||
}
|
||||
|
||||
function inferModulesFromPath(path) {
|
||||
const modules = [];
|
||||
const value = String(path || "");
|
||||
|
||||
if (/__proto__|prototype|constructor/.test(value)) modules.push("prototype-builder");
|
||||
if (/getOwnPropertyDescriptor|defineProperty|descriptor|ownKeys/.test(value)) modules.push("descriptor-guard");
|
||||
if (/toString|name|length|caller|arguments/.test(value)) modules.push("native-protector");
|
||||
if (/navigator|plugins|mimeTypes|webdriver|getBattery|userAgentData/.test(value)) {
|
||||
modules.push("navigator-module");
|
||||
if (/getBattery/.test(value)) modules.push("fingerprint-module");
|
||||
}
|
||||
if (/document\.all/.test(value)) {
|
||||
modules.push("document-all-module");
|
||||
modules.push("document-module");
|
||||
} else if (/document|createElement|querySelector|cookie/.test(value)) {
|
||||
modules.push("document-module");
|
||||
}
|
||||
if (/localStorage|sessionStorage|indexedDB|cookieStore/.test(value)) modules.push("storage-module");
|
||||
if (/canvas|webgl|screen|devicePixelRatio|getContext|toDataURL/.test(value)) modules.push("fingerprint-module");
|
||||
if (/performance|Performance/.test(value)) modules.push("performance-module");
|
||||
if (/crypto|msCrypto|getRandomValues|subtle/.test(value)) modules.push("crypto-module");
|
||||
if (/AudioContext|OfflineAudioContext|Oscillator|Analyser/.test(value)) modules.push("audio-fingerprint-module");
|
||||
if (/RTCPeerConnection|RTCDataChannel|RTCIceCandidate|RTCSessionDescription/.test(value)) modules.push("webrtc-module");
|
||||
if (/Worker|SharedWorker|MessagePort|BroadcastChannel/.test(value)) modules.push("worker-module");
|
||||
if (/Error|stack|prepareStackTrace/.test(value)) modules.push("stack-clean-module");
|
||||
|
||||
return unique(modules);
|
||||
}
|
||||
|
||||
function scoreEvidence(entry) {
|
||||
if (!entry || !entry.type) return 0;
|
||||
if (entry.type === "invoke-error") return 5;
|
||||
if (entry.type === "missing") return 4;
|
||||
if (entry.type === "descriptor" || entry.type === "prototype") return 3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function analyzeGapLog(input = {}) {
|
||||
const logs = toArray(input);
|
||||
const missingPaths = extractPathList(input, "missingPaths", "missing");
|
||||
const descriptorAccess = extractPathList(input, "descriptorAccess", "descriptor");
|
||||
const prototypeAccess = extractPathList(input, "prototypeAccess", "prototype");
|
||||
const invocationErrors = extractPathList(input, "invocationErrors", "invoke-error");
|
||||
const groupedByModule = Object.create(null);
|
||||
|
||||
for (const entry of logs) {
|
||||
const modules = inferModulesFromPath(entry.path);
|
||||
if (!modules.length) continue;
|
||||
for (const moduleName of modules) {
|
||||
if (!groupedByModule[moduleName]) {
|
||||
groupedByModule[moduleName] = { score: 0, evidence: [] };
|
||||
}
|
||||
groupedByModule[moduleName].score += scoreEvidence(entry);
|
||||
groupedByModule[moduleName].evidence.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
const recommendedModules = Object.keys(groupedByModule).sort((left, right) => {
|
||||
const leftPriority = MODULE_PRIORITY.indexOf(left);
|
||||
const rightPriority = MODULE_PRIORITY.indexOf(right);
|
||||
const leftScore = groupedByModule[left].score;
|
||||
const rightScore = groupedByModule[right].score;
|
||||
if (rightScore !== leftScore) return rightScore - leftScore;
|
||||
return leftPriority - rightPriority;
|
||||
});
|
||||
|
||||
const nextActions = [];
|
||||
if (recommendedModules.includes("prototype-builder")) {
|
||||
nextActions.push("先修复构造器、原型链和 instanceof 关系,再处理对象值。");
|
||||
}
|
||||
if (recommendedModules.includes("descriptor-guard")) {
|
||||
nextActions.push("把 getOwnPropertyDescriptor/defineProperty/propertyIsEnumerable 的结果对齐。");
|
||||
}
|
||||
if (recommendedModules.includes("native-protector")) {
|
||||
nextActions.push("统一接管 Function.prototype.toString,并补齐 getter/setter/name/length 保护。");
|
||||
}
|
||||
if (recommendedModules.includes("document-all-module")) {
|
||||
nextActions.push("单独评估 document.all,明确是 native-addon、V8 API 还是 fallback 路线。");
|
||||
}
|
||||
if (recommendedModules.includes("storage-module")) {
|
||||
nextActions.push("把 cookie/localStorage/sessionStorage 的真实状态同步进运行时。");
|
||||
}
|
||||
if (recommendedModules.includes("fingerprint-module")) {
|
||||
nextActions.push("把 canvas/webgl/screen/battery 等指纹点单独建模,不要和普通 navigator 属性混补。");
|
||||
}
|
||||
|
||||
return {
|
||||
totalLogs: logs.length,
|
||||
missingPaths,
|
||||
descriptorAccess,
|
||||
prototypeAccess,
|
||||
invocationErrors,
|
||||
recommendedModules,
|
||||
groupedByModule,
|
||||
nextActions: unique(nextActions),
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
analyzeGapLog,
|
||||
inferModulesFromPath,
|
||||
};
|
||||
Reference in New Issue
Block a user