Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
Change Log

v5.4.0
---
* Add support for `import attributes`. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1256
* Replaced `mkdirp` dependency with native `fs.mkdirSync({ recursive: true })`. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1275. Thank you https://github.com/roli-lpci!

v5.3.1
---
* Fixed class expression name references inside class body being incorrectly resolved to an import binding with the same name, causing broken code at runtime. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1386
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "5.3.1",
"version": "5.4.0",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
Expand All @@ -21,10 +21,11 @@
},
"types": "typings/index.d.ts",
"dependencies": {
"@javascript-obfuscator/escodegen": "2.3.1",
"@javascript-obfuscator/escodegen": "2.4.0",
"@javascript-obfuscator/estraverse": "5.4.0",
"@vercel/blob": ">=0.23.0",
"acorn": "8.15.0",
"acorn-import-attributes": "^1.9.5",
"assert": "2.1.0",
"chalk": "4.1.2",
"chance": "1.1.13",
Expand Down
5 changes: 4 additions & 1 deletion src/ASTParserFacade.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import * as acorn from 'acorn';
import * as ESTree from 'estree';
import chalk, { Chalk } from 'chalk';
import { importAttributesOrAssertions } from 'acorn-import-attributes';

const AcornParser = acorn.Parser.extend(importAttributesOrAssertions);

/**
* Facade over AST parser `acorn`
Expand Down Expand Up @@ -67,7 +70,7 @@ export class ASTParserFacade {
sourceType
};

const program: acorn.Node & ESTree.Program = <acorn.Node & ESTree.Program>acorn.parse(sourceCode, config);
const program: acorn.Node & ESTree.Program = <acorn.Node & ESTree.Program>AcornParser.parse(sourceCode, config);

if (comments.length) {
program.comments = comments;
Expand Down
9 changes: 9 additions & 0 deletions src/declarations/acorn-import-attributes.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module 'acorn-import-attributes' {
import { Parser } from 'acorn';

type TParserExtension = (BaseParser: typeof Parser) => typeof Parser;

export const importAttributes: TParserExtension;
export const importAssertions: TParserExtension;
export const importAttributesOrAssertions: TParserExtension;
}
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,116 @@ describe('JavaScriptObfuscator', () => {
});
});

describe('Import attributes', () => {
describe('Variant #1: import with "with" syntax', () => {
const importAttributesRegExp: RegExp = /from\s*'\.\/config\.json'\s*with\s*\{\s*type\s*:\s*'json'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `import config from './config.json' with { type: 'json' };\nconsole.log(config);`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve import attributes in obfuscated code', () => {
assert.match(obfuscatedCode, importAttributesRegExp);
});
});

describe('Variant #2: import with multiple attributes', () => {
const importAttributesRegExp: RegExp = /from\s*'\.\/data\.json'\s*with\s*\{\s*type\s*:\s*'json'\s*,\s*integrity\s*:\s*'sha384-abc'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `import data from './data.json' with { type: 'json', integrity: 'sha384-abc' };\nconsole.log(data);`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve multiple import attributes in obfuscated code', () => {
assert.match(obfuscatedCode, importAttributesRegExp);
});
});

describe('Variant #3: import with "assert" syntax (deprecated)', () => {
const importAssertionsRegExp: RegExp = /from\s*'\.\/config\.json'\s*assert\s*\{\s*type\s*:\s*'json'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `import config from './config.json' assert { type: 'json' };\nconsole.log(config);`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve import assertions in obfuscated code', () => {
assert.match(obfuscatedCode, importAssertionsRegExp);
});
});

describe('Variant #4: export with "with" syntax', () => {
const exportAttributesRegExp: RegExp = /from\s*'\.\/config\.json'\s*with\s*\{\s*type\s*:\s*'json'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `export { default as config } from './config.json' with { type: 'json' };`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve export attributes in obfuscated code', () => {
assert.match(obfuscatedCode, exportAttributesRegExp);
});
});

describe('Variant #5: export all with "with" syntax', () => {
const exportAllAttributesRegExp: RegExp = /export\s*\*\s*from\s*'\.\/utils\.js'\s*with\s*\{\s*type\s*:\s*'javascript'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `export * from './utils.js' with { type: 'javascript' };`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve export all attributes in obfuscated code', () => {
assert.match(obfuscatedCode, exportAllAttributesRegExp);
});
});

describe('Variant #6: side-effect import with "with" syntax', () => {
const sideEffectImportRegExp: RegExp = /import\s*'\.\/styles\.css'\s*with\s*\{\s*type\s*:\s*'css'\s*\}/;

let obfuscatedCode: string;

before(() => {
const code: string = `import './styles.css' with { type: 'css' };`;

obfuscatedCode = JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET
}).getObfuscatedCode();
});

it('should preserve side-effect import attributes in obfuscated code', () => {
assert.match(obfuscatedCode, sideEffectImportRegExp);
});
});
});

describe('getOptionsByPreset', () => {
describe('Variant #1: base behaviour', () => {
const optionsPresetName: TOptionsPreset = OptionsPreset.HighObfuscation;
Expand Down
74 changes: 74 additions & 0 deletions test/unit-tests/javascript-obfuscator/ASTParserFacade.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,79 @@ describe('ASTParserFacade', () => {
});
});
});

describe('Import attributes', () => {
describe('Variant #1: import with "with" syntax', () => {
const sourceCode: string = `import config from "./config.json" with { type: "json" };`;

let astTree: any;

before(() => {
astTree = ASTParserFacade.parse(sourceCode, { ecmaVersion });
});

it('should parse import with "with" syntax without error', () => {
assert.exists(astTree);
assert.equal(astTree.type, 'Program');
assert.equal(astTree.body[0].type, 'ImportDeclaration');
});

it('should preserve import attributes in AST', () => {
const importDecl = astTree.body[0];
assert.exists(importDecl.attributes);
assert.isArray(importDecl.attributes);
assert.equal(importDecl.attributes.length, 1);
assert.equal(importDecl.attributes[0].key.name, 'type');
assert.equal(importDecl.attributes[0].value.value, 'json');
});
});

describe('Variant #2: import with "assert" syntax (deprecated)', () => {
const sourceCode: string = `import config from "./config.json" assert { type: "json" };`;

let astTree: any;

before(() => {
astTree = ASTParserFacade.parse(sourceCode, { ecmaVersion });
});

it('should parse import with "assert" syntax without error', () => {
assert.exists(astTree);
assert.equal(astTree.type, 'Program');
assert.equal(astTree.body[0].type, 'ImportDeclaration');
});
});

describe('Variant #3: export with "with" syntax', () => {
const sourceCode: string = `export { default as config } from "./config.json" with { type: "json" };`;

let astTree: any;

before(() => {
astTree = ASTParserFacade.parse(sourceCode, { ecmaVersion });
});

it('should parse export with "with" syntax without error', () => {
assert.exists(astTree);
assert.equal(astTree.type, 'Program');
assert.equal(astTree.body[0].type, 'ExportNamedDeclaration');
});
});

describe('Variant #4: dynamic import with "with" syntax', () => {
const sourceCode: string = `const config = await import("./config.json", { with: { type: "json" } });`;

let astTree: any;

before(() => {
astTree = ASTParserFacade.parse(sourceCode, { ecmaVersion });
});

it('should parse dynamic import with "with" syntax without error', () => {
assert.exists(astTree);
assert.equal(astTree.type, 'Program');
});
});
});
});
});
13 changes: 9 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,10 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==

"@javascript-obfuscator/escodegen@2.3.1":
version "2.3.1"
resolved "https://registry.yarnpkg.com/@javascript-obfuscator/escodegen/-/escodegen-2.3.1.tgz#a534e73740830d6c7546ca686773b40b09a6b9d1"
integrity sha512-Z0HEAVwwafOume+6LFXirAVZeuEMKWuPzpFbQhCEU9++BMz0IwEa9bmedJ+rMn/IlXRBID9j3gQ0XYAa6jM10g==
"@javascript-obfuscator/escodegen@2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@javascript-obfuscator/escodegen/-/escodegen-2.4.0.tgz#9d2b19b94793106cafa5cd5041c4e40d42943fb7"
integrity sha512-h9cJ/qb3Y3c1jMQPWypt2CGTFgP34V5OtWLqoOCjV6CT/DUXMZFpoTAfDHpuUrRP0oxNd0UwnVAsPtPuYsoXxQ==
dependencies:
"@javascript-obfuscator/estraverse" "^5.3.0"
esprima "^4.0.1"
Expand Down Expand Up @@ -1017,6 +1017,11 @@ abbrev@^2.0.0:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==

acorn-import-attributes@^1.9.5:
version "1.9.5"
resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef"
integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==

acorn-import-phases@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7"
Expand Down
Loading