mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
transformObjectKeys refactoring and bug fixes
This commit is contained in:
@@ -6,6 +6,7 @@ v0.24.0
|
||||
* Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
|
||||
* Now usage of some browser-related options with `target: 'node'` will cause a validation error
|
||||
* **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
|
||||
* Fixed many `transformObjectKeys` runtime errors
|
||||
* Fixed `Maximum call stack size exceeded` error on large strings when `splitString` option is enabled
|
||||
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
|
||||
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
|
||||
|
||||
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "0.24.0-rc.3",
|
||||
"version": "0.24.0-rc.4",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
@@ -11,6 +11,7 @@ export enum NodeType {
|
||||
CallExpression = 'CallExpression',
|
||||
CatchClause = 'CatchClause',
|
||||
ClassDeclaration = 'ClassDeclaration',
|
||||
ConditionalExpression = 'ConditionalExpression',
|
||||
ContinueStatement = 'ContinueStatement',
|
||||
ExportNamedDeclaration = 'ExportNamedDeclaration',
|
||||
ExpressionStatement = 'ExpressionStatement',
|
||||
|
||||
@@ -57,161 +57,55 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
return ObjectExpressionKeysTransformer.isProhibitedVariableDeclarationHostStatement(objectExpressionNode, hostStatement)
|
||||
|| ObjectExpressionKeysTransformer.isProhibitedFunctionHostStatement(objectExpressionNode, hostStatement)
|
||||
|| ObjectExpressionKeysTransformer.isProhibitedExpressionHostStatement(objectExpressionNode, hostStatement)
|
||||
|| ObjectExpressionKeysTransformer.isProhibitedReturnHostStatement(objectExpressionNode, hostStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix of https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
|
||||
* If object expression is placed inside any expression inside variable declaration with 2+ declarators
|
||||
* - should mark host node as prohibited
|
||||
*
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} hostStatement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isProhibitedVariableDeclarationHostStatement (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
if (!NodeGuards.isVariableDeclarationNode(hostStatement) || !hostStatement.declarations.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
|
||||
objectExpressionNode,
|
||||
hostStatement.declarations
|
||||
hostStatement
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} hostStatement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isProhibitedFunctionHostStatement (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
if (!NodeGuards.isFunctionNode(hostStatement) || !hostStatement.params.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hostNode: ESTree.Node | undefined = objectExpressionNode.parentNode;
|
||||
|
||||
if (!hostNode || !NodeGuards.isAssignmentPatternNode(hostNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
|
||||
objectExpressionNode,
|
||||
hostStatement.params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} hostStatement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isProhibitedReturnHostStatement (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
if (!NodeGuards.isReturnStatementNode(hostStatement) || !hostStatement.argument) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectExpressionKeysTransformer.isProhibitedSequenceExpressionNode(
|
||||
objectExpressionNode,
|
||||
hostStatement.argument
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} hostStatement
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isProhibitedExpressionHostStatement (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
hostStatement: ESTree.Statement
|
||||
): boolean {
|
||||
if (!NodeGuards.isExpressionStatementNode(hostStatement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectExpressionKeysTransformer.isProhibitedSequenceExpressionNode(
|
||||
objectExpressionNode,
|
||||
hostStatement.expression
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Statement} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isProhibitedSequenceExpressionNode (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
node: ESTree.Node
|
||||
): boolean {
|
||||
if (!NodeGuards.isSequenceExpressionNode(node) || !node.expressions.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ObjectExpressionKeysTransformer.isReferencedIdentifierName(
|
||||
objectExpressionNode,
|
||||
node.expressions
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ObjectExpression} objectExpressionNode
|
||||
* @param {Node[]} nodesToSearch
|
||||
* @param {Node} hostNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isReferencedIdentifierName (
|
||||
objectExpressionNode: ESTree.ObjectExpression,
|
||||
nodesToSearch: ESTree.Node[],
|
||||
hostNode: ESTree.Node,
|
||||
): boolean {
|
||||
if (nodesToSearch.length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const identifierNamesSet: string[] = [];
|
||||
|
||||
let isReferencedIdentifierName: boolean = false;
|
||||
let isCurrentNode: boolean = false;
|
||||
|
||||
// should mark node as prohibited if identifier of node is referenced somewhere inside other nodes
|
||||
for (const nodeToSearch of nodesToSearch) {
|
||||
estraverse.traverse(nodeToSearch, {
|
||||
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isCurrentNode = true;
|
||||
}
|
||||
|
||||
if (!NodeGuards.isIdentifierNode(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCurrentNode) {
|
||||
identifierNamesSet.push(node.name);
|
||||
} else if (identifierNamesSet.includes(node.name)) {
|
||||
isReferencedIdentifierName = true;
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
estraverse.traverse(hostNode, {
|
||||
enter: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isCurrentNode = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (isCurrentNode || isReferencedIdentifierName) {
|
||||
break;
|
||||
if (!NodeGuards.isIdentifierNode(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCurrentNode) {
|
||||
identifierNamesSet.push(node.name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (identifierNamesSet.includes(node.name)) {
|
||||
isReferencedIdentifierName = true;
|
||||
}
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
},
|
||||
leave: (node: ESTree.Node): void | estraverse.VisitorOption => {
|
||||
if (node === objectExpressionNode) {
|
||||
isCurrentNode = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isReferencedIdentifierName;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,14 @@ export class NodeGuards {
|
||||
return node.type === NodeType.ClassDeclaration && node.id !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public static isConditionalExpressionNode (node: ESTree.Node): node is ESTree.ConditionalExpression {
|
||||
return node.type === NodeType.ConditionalExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
* @returns {boolean}
|
||||
|
||||
+6
-4
@@ -9,10 +9,12 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
`
|
||||
function test() {
|
||||
var foo;
|
||||
return (foo = {props: 1})['state'] = {
|
||||
expanded: foo.props
|
||||
},
|
||||
foo.state.expanded;
|
||||
|
||||
return foo
|
||||
? {
|
||||
bar: foo.foo
|
||||
}
|
||||
: 1;
|
||||
}
|
||||
|
||||
console.log(test());
|
||||
|
||||
+120
-3
@@ -1549,7 +1549,7 @@ describe('ObjectExpressionKeysTransformer', () => {
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore sequence expression object expression if it references same sequence expression identifier', () => {
|
||||
it('shouldn ignore sequence expression object expression if it references other sequence expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
@@ -1557,7 +1557,9 @@ describe('ObjectExpressionKeysTransformer', () => {
|
||||
describe('Variant #2: reference on same sequence expression identifier', () => {
|
||||
const match: string = `` +
|
||||
`var ${variableMatch};` +
|
||||
`return *\\(${variableMatch} *= *{'props' *: *0x1}\\)\\['state'] *= *{'expanded' *: *${variableMatch}\\['props']}, *` +
|
||||
`var ${variableMatch} *= *{};` +
|
||||
`${variableMatch}\\['props'] *= *0x1;` +
|
||||
`return *\\(${variableMatch} *= *${variableMatch}\\)\\['state'] *= *{'expanded' *: *${variableMatch}\\['props']}, *` +
|
||||
`${variableMatch}\\['state']\\['expanded'];` +
|
||||
``;
|
||||
const regExp: RegExp = new RegExp(match);
|
||||
@@ -1576,7 +1578,122 @@ describe('ObjectExpressionKeysTransformer', () => {
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore sequence expression object expression if it references same sequence expression identifier', () => {
|
||||
it('shouldn ignore sequence expression object expression if it references other sequence expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #7: conditional expression identifier reference', () => {
|
||||
describe('Variant #1: conditional expression identifier reference', () => {
|
||||
const match: string = `` +
|
||||
`var ${variableMatch};` +
|
||||
`var ${variableMatch} *= *{};` +
|
||||
`${variableMatch}\\['bar'] *= *0x1;` +
|
||||
`${variableMatch} *\\? *{'bar' *: *${variableMatch}\\['foo']} *: *${variableMatch};` +
|
||||
``;
|
||||
const regExp: RegExp = new RegExp(match);
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/conditional-expression-identifier-reference.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore conditional expression object expression if it references other conditional expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #2: return statement conditional expression identifier reference', () => {
|
||||
const match: string = `` +
|
||||
`var ${variableMatch};` +
|
||||
`var ${variableMatch} *= *{};` +
|
||||
`${variableMatch}\\['bar'] *= *0x1;` +
|
||||
`return ${variableMatch} *\\? *{'bar' *: *${variableMatch}\\['foo']} *: *${variableMatch};` +
|
||||
``;
|
||||
const regExp: RegExp = new RegExp(match);
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/return-statement-conditional-expression-identifier-reference.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore conditional expression object expression if it references other conditional expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #3: assignment expression conditional expression identifier reference', () => {
|
||||
const match: string = `` +
|
||||
`var ${variableMatch};` +
|
||||
`var ${variableMatch};` +
|
||||
`var ${variableMatch} *= *{};` +
|
||||
`${variableMatch}\\['bar'] *= *0x1;` +
|
||||
`${variableMatch} *= *${variableMatch} *\\? *{'bar' *: *${variableMatch}\\['foo']} *: *${variableMatch};` +
|
||||
``;
|
||||
const regExp: RegExp = new RegExp(match);
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/assignment-expression-conditional-expression-identifier-reference.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore conditional expression object expression if it references other conditional expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variant #4: variable declarator conditional expression identifier reference', () => {
|
||||
const match: string = `` +
|
||||
`var ${variableMatch};` +
|
||||
`var ${variableMatch} *= *{};` +
|
||||
`${variableMatch}\\['bar'] *= *0x1;` +
|
||||
`var ${variableMatch} *= *${variableMatch} *\\? *{'bar' *: *${variableMatch}\\['foo']} *: *${variableMatch};` +
|
||||
``;
|
||||
const regExp: RegExp = new RegExp(match);
|
||||
|
||||
let obfuscatedCode: string;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/variable-declarator-conditional-expression-identifier-reference.js');
|
||||
|
||||
obfuscatedCode = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
transformObjectKeys: true
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
});
|
||||
|
||||
it('shouldn ignore conditional expression object expression if it references other conditional expression identifier', () => {
|
||||
assert.match(obfuscatedCode, regExp);
|
||||
});
|
||||
});
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
function test() {
|
||||
var foo;
|
||||
var bar;
|
||||
|
||||
bar = foo
|
||||
? {
|
||||
bar: foo.foo
|
||||
}
|
||||
: {
|
||||
bar: 1
|
||||
};
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
var foo;
|
||||
|
||||
foo
|
||||
? {
|
||||
bar: foo.foo
|
||||
}
|
||||
: {
|
||||
bar: 1
|
||||
};
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
var foo;
|
||||
|
||||
return foo
|
||||
? {
|
||||
bar: foo.foo
|
||||
}
|
||||
: {
|
||||
bar: 1
|
||||
};
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
function test() {
|
||||
var foo;
|
||||
|
||||
var bar = foo
|
||||
? {
|
||||
bar: foo.foo
|
||||
}
|
||||
: {
|
||||
bar: 1
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user