Files

139 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
};