transformObjectKeys refactoring and bug fixes

This commit is contained in:
sanex3339
2020-01-26 18:56:31 +03:00
parent 7d8d409347
commit 856709ab12
14 changed files with 215 additions and 147 deletions
+1
View File
@@ -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
+3 -3
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "0.24.0-rc.3",
"version": "0.24.0-rc.4",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
+1
View File
@@ -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;
}
+8
View File
@@ -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
View File
@@ -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());
@@ -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);
});
});
@@ -0,0 +1,12 @@
function test() {
var foo;
var bar;
bar = foo
? {
bar: foo.foo
}
: {
bar: 1
};
}
@@ -0,0 +1,11 @@
function test() {
var foo;
foo
? {
bar: foo.foo
}
: {
bar: 1
};
}
@@ -0,0 +1,11 @@
function test() {
var foo;
return foo
? {
bar: foo.foo
}
: {
bar: 1
};
}
@@ -0,0 +1,11 @@
function test() {
var foo;
var bar = foo
? {
bar: foo.foo
}
: {
bar: 1
};
}