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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: JavaScript Obfuscator CI

on:
push:
branches: [master]
branches: [master, release-**]
pull_request:
branches: [master]
branches: [master, release-**]
schedule:
- cron: '0 1 * * *'

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
Change Log

v4.2.2
---
* Add JavaScript Obfuscator PRO advertisement message

v4.2.1
---
* Downgrade `multimatch` version to avoid esm errors
Expand Down
3 changes: 1 addition & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

**JavaScript Obfuscator** is a powerful, enterprise-grade code obfuscation tool for JavaScript and Node.js applications. It transforms readable JavaScript code into a protected, difficult-to-understand format while maintaining full functionality. The project is widely used for protecting intellectual property and preventing reverse engineering.

- **Version**: 4.1.1
- **Version**: 5.0.0
- **Author**: Timofey Kachalov (@sanex3339)
- **License**: BSD-2-Clause
- **Repository**: https://github.com/javascript-obfuscator/javascript-obfuscator
Expand Down Expand Up @@ -1423,7 +1423,6 @@ Use [grunt-contrib-obfuscator](https://github.com/javascript-obfuscator/grunt-co

- **GitHub Issues**: Bug reports and feature requests
- **GitHub Discussions**: Questions and general discussion
- **OpenCollective**: Financial support and sponsorship
- **GitHub Sponsors**: Direct sponsorship

## License
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

#### You can support this project by donating:
* (Github) https://github.com/sponsors/sanex3339
* (OpenCollective) https://opencollective.com/javascript-obfuscator

Huge thanks to all supporters!

Expand Down
13 changes: 3 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "4.2.1",
"version": "4.2.2",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
Expand Down Expand Up @@ -29,6 +29,7 @@
"chance": "1.1.13",
"class-validator": "0.14.3",
"commander": "12.1.0",
"conf": "15.0.2",
"eslint-scope": "8.4.0",
"eslint-visitor-keys": "4.2.1",
"fast-deep-equal": "3.1.3",
Expand All @@ -37,7 +38,6 @@
"md5": "2.3.0",
"mkdirp": "3.0.1",
"multimatch": "5.0.0",
"opencollective-postinstall": "2.0.3",
"process": "0.11.10",
"reflect-metadata": "0.2.2",
"source-map-support": "0.5.21",
Expand Down Expand Up @@ -124,7 +124,6 @@
"prettier:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"format": "yarn run prettier && yarn run eslint --fix",
"git:addFiles": "git add .",
"postinstall": "opencollective-postinstall",
"precommit": "yarn run eslint",
"prepublishOnly": "yarn run build && yarn run build:typings",
"prepare": "husky install"
Expand All @@ -137,11 +136,5 @@
"Dmitry Zamotkin (https://github.com/zamotkin)"
],
"license": "BSD-2-Clause",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/javascript-obfuscator"
},
"collective": {
"url": "https://opencollective.com/javascript-obfuscator"
}
"packageManager": "yarn@1.22.21+sha512.ca75da26c00327d26267ce33536e5790f18ebd53266796fbb664d2a4a5116308042dd8ee7003b276a20eace7d3c5561c3577bdd71bcb67071187af124779620a"
}
6 changes: 6 additions & 0 deletions src/JavaScriptObfuscator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ecmaVersion } from './constants/EcmaVersion';
import { ASTParserFacade } from './ASTParserFacade';
import { NodeGuards } from './node/NodeGuards';
import { Utils } from './utils/Utils';
import { AdvertisementUtils } from './utils/AdvertisementUtils';

@injectable()
export class JavaScriptObfuscator implements IJavaScriptObfuscator {
Expand Down Expand Up @@ -157,6 +158,11 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
* @returns {IObfuscationResult}
*/
public obfuscate(sourceCode: string): IObfuscationResult {
if (AdvertisementUtils.shouldShowAdvertisement()) {
this.logger.advertise(LoggingMessage.JavaScriptObfuscatorProAdFirstPart);
this.logger.advertise(LoggingMessage.JavaScriptObfuscatorProAdSecondPart);
}

if (typeof sourceCode !== 'string') {
sourceCode = '';
}
Expand Down
5 changes: 5 additions & 0 deletions src/constants/ProAdvertiseMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const proAdvertiseMessageFirstPart = `🛡️ JavaScript Obfuscator Pro is now available — with powerful Virtual Machine-based obfuscation
(bytecode virtualization, anti-decompilation, unique opcode and VM structure each compilation, and more).`;

export const proAdvertiseMessageSecondPart =
'👉️ Get your API key at https://obfuscator.io and start using Virtual Machine obfuscation with javascript-obfuscator package.';
6 changes: 5 additions & 1 deletion src/enums/logger/LoggingMessage.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { proAdvertiseMessageFirstPart, proAdvertiseMessageSecondPart } from '../../constants/ProAdvertiseMessage';

export enum LoggingMessage {
EmptySourceCode = 'Empty source code. Obfuscation canceled...',
ObfuscationCompleted = 'Obfuscation completed. Total time: %s sec.',
ObfuscationStarted = 'Obfuscation started...',
RandomGeneratorSeed = 'Random generator seed: %s...',
CodeTransformationStage = 'Code transformation stage: %s...',
NodeTransformationStage = 'AST transformation stage: %s...',
Version = 'Version: %s'
Version = 'Version: %s',
JavaScriptObfuscatorProAdFirstPart = proAdvertiseMessageFirstPart,
JavaScriptObfuscatorProAdSecondPart = proAdvertiseMessageSecondPart
}
6 changes: 6 additions & 0 deletions src/interfaces/logger/ILogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ export interface ILogger {
* @param {string | number} value
*/
warn(loggingMessage: LoggingMessage, value?: string | number): void;

/**
* @param {LoggingMessage} loggingMessage
* @param {string | number} value
*/
advertise(loggingMessage: LoggingMessage): void;
}
7 changes: 7 additions & 0 deletions src/logger/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,11 @@ export class Logger implements ILogger {

Logger.log(Logger.colorWarn, LoggingPrefix.Base, loggingMessage, value);
}

/**
* @param {LoggingMessage} loggingMessage
*/
public advertise(loggingMessage: LoggingMessage): void {
Logger.log(Logger.colorInfo, LoggingPrefix.Base, loggingMessage);
}
}
228 changes: 228 additions & 0 deletions src/utils/AdvertisementUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/**
* Utility class for managing PRO advertisement display
* - Limits display to first N times
* - Skips display in CI environments
* - Only works in Node.js (returns false in browser)
*/
export class AdvertisementUtils {
/**
* Maximum number of times to show the advertisement
*/
private static readonly maxDisplayCount: number = 5;

/**
* Storage key for the display count
*/
private static readonly storageKey: string = 'adDisplayCount';

/**
* Storage key for the timestamp of first display
*/
private static readonly timestampKey: string = 'adFirstDisplayTime';

/**
* Reset period in milliseconds (3 days)
*/
private static readonly resetPeriodMs: number = 3 * 24 * 60 * 60 * 1000;

/**
* Common CI environment variables to detect
*/
private static readonly ciEnvVars: string[] = [
'CI',
'CONTINUOUS_INTEGRATION',
'GITHUB_ACTIONS',
'GITLAB_CI',
'TRAVIS',
'CIRCLECI',
'JENKINS_URL',
'HUDSON_URL',
'TEAMCITY_VERSION',
'BUILDKITE',
'TF_BUILD', // Azure Pipelines
'BITBUCKET_BUILD_NUMBER',
'CODEBUILD_BUILD_ID', // AWS CodeBuild
'DRONE',
'HEROKU_TEST_RUN_ID',
'NETLIFY',
'VERCEL',
'NOW_BUILDER', // Vercel (legacy)
'RENDER',
'CODESANDBOX_SSE',
'STACKBLITZ'
];

/**
* Cached conf instance
*/
private static config: any = null;

/**
* Check if running in a CI environment
*/
public static isCI(): boolean {
if (!this.isNodeEnvironment()) {
return false;
}

return this.ciEnvVars.some((envVar) => {
const value = process.env[envVar];

return value !== undefined && value !== '' && value !== '0' && value !== 'false';
});
}

/**
* Check if advertisement should be displayed
* Returns true if:
* - Running in Node.js (not browser)
* - Not in CI environment
* - Display count is less than maxDisplayCount
*
* Also increments the display count if returning true
*
* In browser environments, always returns false
*/
public static shouldShowAdvertisement(): boolean {
// Don't show in browser - only Node.js CLI
if (!this.isNodeEnvironment()) {
return false;
}

// Don't show in CI environments
if (this.isCI()) {
return false;
}

if (!process.stdout.isTTY) {
return false;
}

// Initialize config if needed
const config = this.getConfig();

if (!config) {
return false;
}

// Check if reset period has passed (3 days)
const firstDisplayTime = this.getFirstDisplayTime(config);
const now = Date.now();

if (firstDisplayTime && now - firstDisplayTime >= this.resetPeriodMs) {
// Reset counter after 3 days
this.resetDisplayData(config);
}

// Check display count
const count = this.getDisplayCount(config);

if (count >= this.maxDisplayCount) {
return false;
}

// Set first display time if not set
if (!firstDisplayTime || now - firstDisplayTime >= this.resetPeriodMs) {
this.setFirstDisplayTime(config, now);
}

// Increment count for next time
this.setDisplayCount(config, count + 1);

return true;
}

/**
* Check if running in Node.js environment
*/
private static isNodeEnvironment(): boolean {
return typeof process !== 'undefined' && process.versions?.node !== undefined;
}

/**
* Get or create conf instance
*/
private static getConfig(): any {
if (this.config) {
return this.config;
}

if (typeof window === 'undefined') {
try {
// Dynamic import to avoid bundling in browser
// eslint-disable-next-line no-eval
const Conf = eval('require')('conf').default;

this.config = new Conf({
projectName: 'javascript-obfuscator'
});

return this.config;
} catch {
return null;
}
}

return null;
}

/**
* Get current display count from config
*/
private static getDisplayCount(config: any): number {
try {
const count = config.get(this.storageKey, 0);

return typeof count === 'number' ? count : 0;
} catch {
return 0;
}
}

/**
* Set display count in config
*/
private static setDisplayCount(config: any, count: number): void {
try {
config.set(this.storageKey, count);
} catch {
// Ignore errors
}
}

/**
* Get first display timestamp from config
*/
private static getFirstDisplayTime(config: any): number | null {
try {
const timestamp = config.get(this.timestampKey, null);

return typeof timestamp === 'number' ? timestamp : null;
} catch {
return null;
}
}

/**
* Set first display timestamp in config
*/
private static setFirstDisplayTime(config: any, timestamp: number): void {
try {
config.set(this.timestampKey, timestamp);
} catch {
// Ignore errors
}
}

/**
* Reset display data (count and timestamp)
*/
private static resetDisplayData(config: any): void {
try {
config.delete(this.storageKey);
config.delete(this.timestampKey);
} catch {
// Ignore errors
}
}
}
1 change: 1 addition & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import './unit-tests/storages/identifier-names-cache/PropertyIdentifierNamesCach
import './unit-tests/storages/string-array-transformers/literal-nodes-cache/LiteralNodesCacheStorage.spec';
import './unit-tests/storages/string-array-transformers/string-array/StringArrayStorage.spec';
import './unit-tests/storages/string-array-transformers/visited-lexical-scope-nodes-stack/VisitedLexicalScopeNodesStackStorage.spec';
import './unit-tests/utils/AdvertisementUtils.spec';
import './unit-tests/utils/ArrayUtils.spec';
import './unit-tests/utils/CryptUtils.spec';
import './unit-tests/utils/CryptUtilsStringArray.spec';
Expand Down
Loading
Loading