mirror of
https://github.com/Floriansylvain/compact-portfolio.git
synced 2026-08-19 11:43:19 +02:00
feat: add reserved words loading and identifier obfuscation to AssetProcessor
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"javascript": [
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"export",
|
||||
"extends",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"instanceof",
|
||||
"let",
|
||||
"new",
|
||||
"return",
|
||||
"super",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with",
|
||||
"yield",
|
||||
"true",
|
||||
"false",
|
||||
"null",
|
||||
"undefined",
|
||||
"NaN",
|
||||
"Infinity",
|
||||
"console",
|
||||
"window",
|
||||
"document",
|
||||
"Array",
|
||||
"Object",
|
||||
"String",
|
||||
"Number",
|
||||
"Boolean",
|
||||
"Date",
|
||||
"Math",
|
||||
"JSON",
|
||||
"setTimeout",
|
||||
"setInterval",
|
||||
"clearTimeout",
|
||||
"clearInterval",
|
||||
"addEventListener",
|
||||
"removeEventListener",
|
||||
"querySelector",
|
||||
"querySelectorAll",
|
||||
"getElementById",
|
||||
"getElementsByClassName",
|
||||
"getElementsByTagName",
|
||||
"createElement",
|
||||
"appendChild",
|
||||
"removeChild",
|
||||
"innerHTML",
|
||||
"innerText",
|
||||
"textContent",
|
||||
"value",
|
||||
"length",
|
||||
"push",
|
||||
"pop",
|
||||
"shift",
|
||||
"unshift",
|
||||
"slice",
|
||||
"splice",
|
||||
"indexOf",
|
||||
"includes",
|
||||
"forEach",
|
||||
"map",
|
||||
"filter",
|
||||
"reduce",
|
||||
"find",
|
||||
"some",
|
||||
"every",
|
||||
"sort",
|
||||
"reverse",
|
||||
"join",
|
||||
"split",
|
||||
"matchMedia",
|
||||
"matches",
|
||||
"classList",
|
||||
"add",
|
||||
"remove",
|
||||
"contains",
|
||||
"toggle",
|
||||
"setAttribute",
|
||||
"getAttribute",
|
||||
"removeAttribute",
|
||||
"target",
|
||||
"isIntersecting",
|
||||
"IntersectionObserver",
|
||||
"unobserve",
|
||||
"observe",
|
||||
"threshold",
|
||||
"rootMargin",
|
||||
"key",
|
||||
"click",
|
||||
"keydown",
|
||||
"DOMContentLoaded",
|
||||
"onload",
|
||||
"Function",
|
||||
"of"
|
||||
]
|
||||
}
|
||||
@@ -11,6 +11,18 @@ class AssetProcessor {
|
||||
constructor() {
|
||||
this.files = new Map();
|
||||
this.cspHashes = { scripts: new Set(), styles: new Set() };
|
||||
this.reservedWords = this.loadReservedWords();
|
||||
}
|
||||
|
||||
loadReservedWords() {
|
||||
try {
|
||||
const reservedWordsPath = path.join(ROOT, "reserved-words.json");
|
||||
const data = fs.readFileSync(reservedWordsPath, "utf8");
|
||||
return new Set(JSON.parse(data).javascript);
|
||||
} catch (error) {
|
||||
console.warn("Could not load reserved words, using fallback");
|
||||
return new Set(["console", "window", "document"]); // minimal fallback
|
||||
}
|
||||
}
|
||||
|
||||
hash(content, algorithm = "sha256") {
|
||||
@@ -165,6 +177,145 @@ class AssetProcessor {
|
||||
.trim();
|
||||
}
|
||||
|
||||
generateObfuscatedName(index) {
|
||||
const chars =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
let result = "";
|
||||
let num = index;
|
||||
|
||||
result += chars[num % 52];
|
||||
num = Math.floor(num / 52);
|
||||
|
||||
while (num > 0) {
|
||||
result += chars[num % 62];
|
||||
num = Math.floor(num / 62);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
obfuscateJS(src) {
|
||||
let out = src;
|
||||
const preserveStrings = [];
|
||||
const preserveRegex = [];
|
||||
const preserveTemplates = [];
|
||||
let stringIndex = 0;
|
||||
let regexIndex = 0;
|
||||
let templateIndex = 0;
|
||||
|
||||
out = out.replace(/`((?:\\.|[^`\\])*)`/g, (match) => {
|
||||
const placeholder = `__TEMPLATE_${templateIndex++}__`;
|
||||
preserveTemplates.push({ placeholder, original: match });
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
out = out.replace(/(["'])((?:\\.|(?!\1)[^\\])*?)\1/g, (match) => {
|
||||
const placeholder = `__STRING_${stringIndex++}__`;
|
||||
preserveStrings.push({ placeholder, original: match });
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
out = out.replace(
|
||||
/\/(?![\/\*])([^\/\\\n]|\\.)*\/[gimuy]*/g,
|
||||
(match) => {
|
||||
const placeholder = `__REGEX_${regexIndex++}__`;
|
||||
preserveRegex.push({ placeholder, original: match });
|
||||
return placeholder;
|
||||
}
|
||||
);
|
||||
|
||||
const identifiers = new Set();
|
||||
|
||||
const identifierRegex = /\b[a-zA-Z_$][a-zA-Z0-9_$]*\b/g;
|
||||
let match;
|
||||
while ((match = identifierRegex.exec(out)) !== null) {
|
||||
const identifier = match[0];
|
||||
if (
|
||||
!this.reservedWords.has(identifier) &&
|
||||
!identifier.startsWith("__")
|
||||
) {
|
||||
identifiers.add(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
const obfuscationMap = new Map();
|
||||
let nameIndex = 0;
|
||||
|
||||
Array.from(identifiers)
|
||||
.sort()
|
||||
.forEach((identifier) => {
|
||||
obfuscationMap.set(
|
||||
identifier,
|
||||
this.generateObfuscatedName(nameIndex++)
|
||||
);
|
||||
});
|
||||
|
||||
obfuscationMap.forEach((obfuscated, original) => {
|
||||
const patterns = [
|
||||
new RegExp(
|
||||
`\\b(const|let|var)\\s+${original.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
)}\\b`,
|
||||
"g"
|
||||
),
|
||||
new RegExp(
|
||||
`\\bfunction\\s+${original.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
)}\\b`,
|
||||
"g"
|
||||
),
|
||||
new RegExp(
|
||||
`\\(([^)]*\\b)${original.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
)}\\b([^)]*)\\)`,
|
||||
"g"
|
||||
),
|
||||
new RegExp(
|
||||
`\\.${original.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`,
|
||||
"g"
|
||||
),
|
||||
new RegExp(
|
||||
`(?<!for\\s*\\([^)]*\\s)\\b${original.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
)}(?!\\s+of\\b)\\b`,
|
||||
"g"
|
||||
),
|
||||
];
|
||||
|
||||
patterns.forEach((pattern) => {
|
||||
out = out.replace(pattern, (match) => {
|
||||
return match.replace(
|
||||
new RegExp(
|
||||
`\\b${original.replace(
|
||||
/[.*+?^${}()|[\]\\]/g,
|
||||
"\\$&"
|
||||
)}\\b`,
|
||||
"g"
|
||||
),
|
||||
obfuscated
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
preserveTemplates.forEach(({ placeholder, original }) => {
|
||||
out = out.replace(placeholder, original);
|
||||
});
|
||||
|
||||
preserveRegex.forEach(({ placeholder, original }) => {
|
||||
out = out.replace(placeholder, original);
|
||||
});
|
||||
|
||||
preserveStrings.forEach(({ placeholder, original }) => {
|
||||
out = out.replace(placeholder, original);
|
||||
});
|
||||
|
||||
return out;
|
||||
}
|
||||
minifyJS(src) {
|
||||
let out = src;
|
||||
const preserveStrings = [];
|
||||
@@ -188,6 +339,8 @@ class AssetProcessor {
|
||||
out = out.replace(placeholder, original);
|
||||
});
|
||||
|
||||
out = this.obfuscateJS(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user