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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ v5.4.0
---
* Add support for `import attributes`. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1256
* Fixed `reservedNames` not preserving class method and property names when `stringArray` or `deadCodeInjection` is enabled. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1279
* Fixed infinite loop / stack overflow when `reservedNames` patterns match all generated identifier names. Now throws a descriptive error instead. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1382
* Fixed `transformObjectKeys` changing evaluation order when object expression is inside a sequence expression with preceding side effects (e.g. `return aux(ys), { min }`). Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1246
* 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!
* Replaced `conf` dependency with custom implementation using `env-paths` and native `fs`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import { NodeGuards } from '../../node/NodeGuards';

@injectable()
export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNamesGenerator {
/**
* @type {number}
*/
private static readonly maxGenerationAttempts: number = 10000;

/**
* @type {IOptions}
*/
Expand Down Expand Up @@ -132,6 +137,19 @@ export abstract class AbstractIdentifierNamesGenerator implements IIdentifierNam
return !this.allLexicalScopePreservedNames.has(name);
}

/**
* @param {number} attempts
*/
protected checkGenerationAttempts(attempts: number): void {
if (attempts > AbstractIdentifierNamesGenerator.maxGenerationAttempts) {
throw new Error(
'Unable to generate a valid identifier name. ' +
'This is likely caused by `reservedNames` patterns that match all generated names. ' +
'Please check your `reservedNames` option.'
);
}
}

/**
* @param {string} name
* @returns {boolean}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,31 @@ export class HexadecimalIdentifierNamesGenerator extends AbstractIdentifierNames
/**
* @param {number} nameLength
* @param {(name: string) => boolean} validationFn
* @param {number} attempts
* @returns {string}
*/
private generateNextName(nameLength: number | undefined, validationFn: (name: string) => boolean): string {
private generateNextName(
nameLength: number | undefined,
validationFn: (name: string) => boolean
): string {
const rangeMinInteger: number = 10000;
const rangeMaxInteger: number = 99_999_999;
const randomInteger: number = this.randomGenerator.getRandomInteger(rangeMinInteger, rangeMaxInteger);
const hexadecimalNumber: string = NumberUtils.toHex(randomInteger);
const prefixLength: number = Utils.hexadecimalPrefix.length;
const baseNameLength: number =
(nameLength ?? HexadecimalIdentifierNamesGenerator.baseIdentifierNameLength) + prefixLength;
const baseIdentifierName: string = hexadecimalNumber.slice(0, baseNameLength);
const identifierName: string = `_${baseIdentifierName}`;

if (!validationFn(identifierName)) {
return this.generateNextName(nameLength, validationFn);
let identifierName: string = '';
let isValid: boolean = false;

for (let attempts: number = 0; !isValid; attempts++) {
this.checkGenerationAttempts(attempts);

const randomInteger: number = this.randomGenerator.getRandomInteger(rangeMinInteger, rangeMaxInteger);
const hexadecimalNumber: string = NumberUtils.toHex(randomInteger);
const baseIdentifierName: string = hexadecimalNumber.slice(0, baseNameLength);

identifierName = `_${baseIdentifierName}`;
isValid = validationFn(identifierName);
}

this.preserveName(identifierName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,25 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
};

let identifierName: string = previousMangledName;
let isValidIdentifierName: boolean;
let isValidIdentifierName: boolean = false;
let reservedNameAttempts: number = 0;

do {
identifierName = generateNewMangledName(identifierName);

if (
this.preservedNamesSet.has(identifierName) ||
MangledIdentifierNamesGenerator.reservedNamesSet.has(identifierName)
) {
continue;
}

isValidIdentifierName = validationFunction?.(identifierName) ?? this.isValidIdentifierName(identifierName);

if (!isValidIdentifierName) {
this.checkGenerationAttempts(reservedNameAttempts);
reservedNameAttempts++;
}
} while (!isValidIdentifierName);

return identifierName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,4 +368,23 @@ describe('MangledIdentifierNamesGenerator', () => {
});
});
});

describe('`reservedNames` that match all generated names', () => {
let testFunc: () => void;

before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-names-match-all.js');

testFunc = () =>
JavaScriptObfuscator.obfuscate(code, {
...NO_ADDITIONAL_NODES_PRESET,
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
reservedNames: ['^(?!renameMeOnly$)']
});
});

it('should throw an error when all generated names match reservedNames', () => {
assert.throws(testFunc, 'Unable to generate a valid identifier name');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(function(){function pleasedontrename (){}; function renameMeOnly(){}})
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,26 @@ describe('HexadecimalIdentifierNamesGenerator', () => {
assert.notEqual(hexadecimalIdentifierName1, hexadecimalIdentifierName2);
});
});

describe('`reservedNames` that match all generated names', () => {
let testFunc: () => void;

before(() => {
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();

inversifyContainerFacade.load('', '', {
reservedNames: ['^(?!renameMeOnly$)']
});
const identifierNamesGenerator = inversifyContainerFacade.getNamed<IIdentifierNamesGenerator>(
ServiceIdentifiers.IIdentifierNamesGenerator,
IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator
);

testFunc = () => identifierNamesGenerator.generateNext();
});

it('should throw an error when all generated names match reservedNames', () => {
assert.throws(testFunc, 'Unable to generate a valid identifier name');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,26 @@ describe('MangledShuffledIdentifierNamesGenerator', () => {
});
});
});

describe('`reservedNames` that match all generated names', () => {
let testFunc: () => void;

beforeEach(() => {
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();

inversifyContainerFacade.load('', '', {
reservedNames: ['^(?!renameMeOnly$)']
});
const identifierNamesGenerator = inversifyContainerFacade.getNamed<IIdentifierNamesGenerator>(
ServiceIdentifiers.IIdentifierNamesGenerator,
IdentifierNamesGenerator.MangledShuffledIdentifierNamesGenerator
);

testFunc = () => identifierNamesGenerator.generateNext();
});

it('should throw an error when all generated names match reservedNames', () => {
assert.throws(testFunc, 'Unable to generate a valid identifier name');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,26 @@ describe('MangledIdentifierNamesGenerator', () => {
});
});
});

describe('`reservedNames` that match all generated names', () => {
let testFunc: () => void;

beforeEach(() => {
const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();

inversifyContainerFacade.load('', '', {
reservedNames: ['^(?!renameMeOnly$)']
});
const identifierNamesGenerator = inversifyContainerFacade.getNamed<IIdentifierNamesGenerator>(
ServiceIdentifiers.IIdentifierNamesGenerator,
IdentifierNamesGenerator.MangledIdentifierNamesGenerator
);

testFunc = () => identifierNamesGenerator.generateNext();
});

it('should throw an error when all generated names match reservedNames', () => {
assert.throws(testFunc, 'Unable to generate a valid identifier name');
});
});
});
Loading