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() {
|
constructor() {
|
||||||
this.files = new Map();
|
this.files = new Map();
|
||||||
this.cspHashes = { scripts: new Set(), styles: new Set() };
|
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") {
|
hash(content, algorithm = "sha256") {
|
||||||
@@ -165,6 +177,145 @@ class AssetProcessor {
|
|||||||
.trim();
|
.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) {
|
minifyJS(src) {
|
||||||
let out = src;
|
let out = src;
|
||||||
const preserveStrings = [];
|
const preserveStrings = [];
|
||||||
@@ -188,6 +339,8 @@ class AssetProcessor {
|
|||||||
out = out.replace(placeholder, original);
|
out = out.replace(placeholder, original);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
out = this.obfuscateJS(out);
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user