├── .gitmodules
├── .jshintignore
├── examples
├── http
│ ├── .gitignore
│ ├── config.json
│ ├── package.json
│ ├── respond.js
│ └── server.js
├── basic
│ ├── hello.js
│ ├── index.js
│ └── hello-world.js
├── foo-bar.json
├── express
│ ├── .gitignore
│ ├── public
│ │ └── stylesheets
│ │ │ └── style.styl
│ ├── routes
│ │ ├── user.js
│ │ └── index.js
│ ├── package.json
│ ├── views
│ │ └── index.ejs
│ └── app.js
├── comment-at-end
│ └── index.js
├── resolve
│ ├── app.js
│ └── package.json
├── json-everywhere
│ ├── config
│ │ ├── development.json
│ │ └── production.json
│ ├── data
│ │ ├── 01.json
│ │ ├── 02.json
│ │ ├── 04.json
│ │ └── 03.json
│ └── app.js
├── non-included-files
│ ├── lib
│ │ └── foo.js
│ ├── config
│ │ └── bar.js
│ └── app.js
├── multiline-strings
│ └── index.js
└── escaped-strings
│ └── index.js
├── .travis.yml
├── .gitignore
├── .npmignore
├── index.js
├── .jshintrc
├── .editorconfig
├── tasks
└── obfuscator.js
├── test
├── .jshintrc
├── acceptance
│ ├── resolve.js
│ ├── comment-at-end.js
│ ├── multiline-strings.js
│ ├── non-included-files.js
│ ├── escaped-strings.js
│ ├── basic.js
│ ├── express.js
│ ├── http.js
│ └── json-everywhere.js
└── obfuscator.js
├── package.json
├── Makefile
├── bin
└── obfuscator
├── docs.md
├── lib
├── require.js
├── utils.js
└── obfuscator.js
├── History.md
└── Readme.md
/.gitmodules:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 |
2 | lib/require.js
--------------------------------------------------------------------------------
/examples/http/.gitignore:
--------------------------------------------------------------------------------
1 | obfuscated.js
--------------------------------------------------------------------------------
/examples/basic/hello.js:
--------------------------------------------------------------------------------
1 | exports.hello = 'hello';
2 |
--------------------------------------------------------------------------------
/examples/basic/index.js:
--------------------------------------------------------------------------------
1 | exports.world = 'world';
2 |
--------------------------------------------------------------------------------
/examples/foo-bar.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "bar"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/express/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.css
3 | obfuscated.js
--------------------------------------------------------------------------------
/examples/comment-at-end/index.js:
--------------------------------------------------------------------------------
1 | exports.hello = 'hello'
2 | //#im a comment
--------------------------------------------------------------------------------
/examples/resolve/app.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = require.resolve('mocha');
3 |
--------------------------------------------------------------------------------
/examples/json-everywhere/config/development.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": "dev"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/json-everywhere/config/production.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": "prod"
3 | }
4 |
--------------------------------------------------------------------------------
/examples/json-everywhere/data/01.json:
--------------------------------------------------------------------------------
1 | {
2 | "fragment": "The lead character"
3 | }
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 | - "0.12"
5 | - "iojs"
6 |
--------------------------------------------------------------------------------
/examples/json-everywhere/data/02.json:
--------------------------------------------------------------------------------
1 | {
2 | "fragment": "called \"The Bride\""
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib-cov
3 | coverage.html
4 | npm-debug.log
5 | obfuscated.js
6 |
--------------------------------------------------------------------------------
/examples/json-everywhere/data/04.json:
--------------------------------------------------------------------------------
1 | {
2 | "fragment": "lead by her lover \"Bill\"."
3 | }
4 |
--------------------------------------------------------------------------------
/examples/non-included-files/lib/foo.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | return 'foo'
3 | }
4 |
--------------------------------------------------------------------------------
/examples/http/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "foo": "foo? bar.",
3 | "bar": "bar? foo.",
4 | "port": 4300
5 | }
--------------------------------------------------------------------------------
/examples/non-included-files/config/bar.js:
--------------------------------------------------------------------------------
1 | module.exports = function(){
2 | return 'bar'
3 | }
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples/
2 | lib-cov/
3 | test/
4 | .editorconfig
5 | .jshintrc
6 | .travis.yml
7 | coverage.html
8 | Makefile
--------------------------------------------------------------------------------
/examples/json-everywhere/data/03.json:
--------------------------------------------------------------------------------
1 | {
2 | "fragment": "was a member of the Deadly Viper Assassination Squad"
3 | }
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = process.env.OBF_COV
3 | ? require('./lib-cov/obfuscator')
4 | : require('./lib/obfuscator');
5 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "laxbreak": true,
4 | "indent": 2,
5 | "maxlen": 80,
6 | "expr": true,
7 | "supernew": true
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 |
7 | [*.js]
8 | indent_style = space
9 | indent_size = 2
--------------------------------------------------------------------------------
/examples/express/public/stylesheets/style.styl:
--------------------------------------------------------------------------------
1 | body
2 | padding: 50px
3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif
4 | a
5 | color: #00B7FF
--------------------------------------------------------------------------------
/examples/express/routes/user.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * GET users listing.
4 | */
5 |
6 | exports.list = function(req, res){
7 | res.send("respond with a resource");
8 | };
--------------------------------------------------------------------------------
/examples/express/routes/index.js:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * GET home page.
4 | */
5 |
6 | exports.index = function(req, res){
7 | res.render('index', { title: 'Express' });
8 | };
--------------------------------------------------------------------------------
/examples/http/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obfuscator-http-example",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {},
6 | "dependencies": {}
7 | }
8 |
--------------------------------------------------------------------------------
/examples/multiline-strings/index.js:
--------------------------------------------------------------------------------
1 |
2 | var str =
3 | 'hello\
4 | \'world\'\
5 | \"and\"\
6 | stuff';
7 |
8 | exports.fn = function () {
9 | return str;
10 | }
11 |
--------------------------------------------------------------------------------
/examples/resolve/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obfuscator-example-resolve",
3 | "version": "0.0.0",
4 | "private": true,
5 | "dependencies": {
6 | "mocha": "*"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/examples/basic/hello-world.js:
--------------------------------------------------------------------------------
1 | exports.helloWorld = function () {
2 | var hello = require('./hello').hello;
3 | var world = require('./').world;
4 |
5 | return hello + ' ' + world;
6 | };
7 |
--------------------------------------------------------------------------------
/examples/non-included-files/app.js:
--------------------------------------------------------------------------------
1 |
2 | var bar = require('./config/bar')
3 | var foo = require('./lib/foo')
4 |
5 | module.exports = function(){
6 | return [foo(), bar()].join(' ')
7 | }
8 |
--------------------------------------------------------------------------------
/examples/escaped-strings/index.js:
--------------------------------------------------------------------------------
1 |
2 | exports.a = '\n\t\ba\r\n';
3 | exports.b = 'b';
4 | exports.c = '\nc';
5 |
6 | exports.string = function () {
7 | return this.a + this.b + this.c;
8 | };
9 |
--------------------------------------------------------------------------------
/examples/express/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obfuscator-example-express",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {},
6 | "dependencies": {
7 | "express": "3.0.0rc4",
8 | "ejs": "*",
9 | "stylus": "*"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tasks/obfuscator.js:
--------------------------------------------------------------------------------
1 |
2 | module.exports = function () {
3 | var err =
4 | 'Grunt support has been removed from obfusactor.\n'
5 | + 'Please use\n\n'
6 | + ' https://github.com/stephenmathieson/grunt-obfuscator\n';
7 |
8 | console.error(err);
9 | };
10 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "globals": {
3 | "describe": false,
4 | "before": false,
5 | "beforeEach": false,
6 | "after": false,
7 | "afterEach": false,
8 | "it": false
9 | },
10 | "node": true,
11 | "expr": true,
12 | "laxbreak": true
13 | }
14 |
--------------------------------------------------------------------------------
/examples/express/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= title %>
5 |
6 |
7 |
8 | <%= title %>
9 | Welcome to <%= title %>
10 |
11 |
--------------------------------------------------------------------------------
/examples/http/respond.js:
--------------------------------------------------------------------------------
1 |
2 | var url = require('url');
3 |
4 | var config = require('./config.json');
5 |
6 | module.exports = function (req, res) {
7 | var parsed = url.parse(req.url, true);
8 |
9 | res.writeHead(200, { 'content-type': 'text/plain' });
10 | if ('bar' === parsed.query.foo) {
11 | res.write(config.bar);
12 | } else {
13 | res.write(config.foo);
14 | }
15 | res.end();
16 | };
17 |
--------------------------------------------------------------------------------
/examples/json-everywhere/app.js:
--------------------------------------------------------------------------------
1 |
2 | exports = module.exports = app;
3 |
4 | function app(env) {
5 | env = env || 'development';
6 | return require('./config/' + env).env;
7 | }
8 |
9 | exports.killbill =
10 | [
11 | require('./data/01'),
12 | require('./data/02'),
13 | require('./data/03'),
14 | require('./data/04')
15 | ]
16 | .map(function (o) {
17 | return o.fragment;
18 | })
19 | .join(', ');
20 |
--------------------------------------------------------------------------------
/test/acceptance/resolve.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('better-assert');
4 | var fs = require('fs');
5 | var obfuscator = require('../..');
6 |
7 | var dir = path.join(__dirname, '..', '..', 'examples', 'resolve');
8 | var app = path.join(dir, 'app.js');
9 |
10 | var opts = obfuscator.options([ app ], dir, app, true);
11 |
12 | obfuscator.obfuscator(opts, function (err, code) {
13 | if (err) {
14 | throw err;
15 | }
16 |
17 | var file = path.join(dir, 'obfuscated.js');
18 | fs.writeFileSync(file, code);
19 | assert(path.join(dir, 'node_modules', 'mocha', 'index.js') == require(file));
20 | });
21 |
--------------------------------------------------------------------------------
/examples/http/server.js:
--------------------------------------------------------------------------------
1 |
2 | var http = require('http');
3 | var config = require('./config.json');
4 | var respond = require('./respond');
5 |
6 | exports.start = function (cb) {
7 | cb = typeof cb === 'function'
8 | ? cb
9 | : function () {};
10 |
11 | exports._http = http.createServer(function (req, res) {
12 | return respond(req, res);
13 | });
14 |
15 | exports._http.listen(config.port, cb);
16 | };
17 |
18 | exports.port = config.port;
19 |
20 | exports._http = null;
21 |
22 | exports.close = function (cb) {
23 | cb = typeof cb === 'function'
24 | ? cb
25 | : function () {};
26 | exports._http.close(cb);
27 | };
28 |
--------------------------------------------------------------------------------
/test/acceptance/comment-at-end.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('better-assert');
4 | var fs = require('fs');
5 | var obfuscator = require('../..');
6 |
7 | var EXAMPLES = path.join(__dirname, '..', '..', 'examples');
8 | var FILE = path.join(EXAMPLES, 'comment-at-end', 'index.js');
9 | var opts = obfuscator.options([ FILE ], EXAMPLES, FILE, true);
10 |
11 | obfuscator.obfuscator(opts, function (err, code) {
12 | if (err) {
13 | throw err;
14 | }
15 |
16 | var file = path.join(EXAMPLES, 'comment-at-end', 'obfuscated.js');
17 | fs.writeFileSync(file, code);
18 | assert('hello' === require(file).hello);
19 | });
20 |
--------------------------------------------------------------------------------
/test/acceptance/multiline-strings.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | var path = require('path');
4 | var assert = require('better-assert');
5 | var fs = require('fs');
6 | var obfuscator = require('../..');
7 |
8 | var root = path.join(__dirname, '..', '..', 'examples', 'multiline-strings');
9 | var entry = path.join(root, 'index.js');
10 | var files = [entry];
11 |
12 | var opts = new obfuscator.Options(files, root, entry, true);
13 |
14 | obfuscator(opts, function (err, code) {
15 | if (err) throw err;
16 | var file = path.join(root, 'obfuscated.js');
17 | fs.writeFileSync(file, code);
18 |
19 | var module = require(file);
20 | assert('hello \'world\' "and" stuff' == module.fn());
21 | });
22 |
--------------------------------------------------------------------------------
/test/acceptance/non-included-files.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('better-assert');
4 | var fs = require('fs');
5 | var obfuscator = require('../..');
6 |
7 | var root = path.join(__dirname, '..', '..', 'examples', 'non-included-files');
8 |
9 | var opts = obfuscator.options([
10 | path.join(root, 'app.js'),
11 | path.join(root, 'lib/foo.js'),
12 | ],
13 | root,
14 | path.join(root, 'app.js'),
15 | true);
16 |
17 | obfuscator.obfuscator(opts, function (err, code) {
18 | if (err) {
19 | throw err;
20 | }
21 |
22 | var file = path.join(root, 'obfuscated.js');
23 | fs.writeFileSync(file, code);
24 | assert('foo bar' === require(file)());
25 | });
26 |
--------------------------------------------------------------------------------
/test/acceptance/escaped-strings.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | var path = require('path');
4 | var assert = require('better-assert');
5 | var fs = require('fs');
6 | var obfuscator = require('../..');
7 |
8 | var root = path.join(__dirname, '..', '..', 'examples', 'escaped-strings');
9 | var entry = path.join(root, 'index.js');
10 | var files = [entry];
11 |
12 | var opts = new obfuscator.Options(files, root, entry, true);
13 |
14 | obfuscator(opts, function (err, code) {
15 | if (err) throw err;
16 | var file = path.join(root, 'obfuscated.js');
17 | fs.writeFileSync(file, code);
18 |
19 | var module = require(file);
20 |
21 | assert('\n\t\ba\r\n' == module.a);
22 | assert('b' == module.b);
23 | assert('\nc' == module.c);
24 | assert('\n\t\ba\r\nb\nc' == module.string());
25 | });
26 |
--------------------------------------------------------------------------------
/test/acceptance/basic.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('better-assert');
4 | var fs = require('fs');
5 | var obfuscator = require('../..');
6 |
7 | var EXAMPLES = path.join(__dirname, '..', '..', 'examples');
8 |
9 | var opts = obfuscator.options([
10 | path.join(EXAMPLES, 'basic', 'hello.js'),
11 | path.join(EXAMPLES, 'basic', 'index.js'),
12 | path.join(EXAMPLES, 'basic', 'hello-world.js')
13 | ],
14 | EXAMPLES,
15 | path.join(EXAMPLES, 'basic', 'hello-world.js'),
16 | true);
17 |
18 | obfuscator.obfuscator(opts, function (err, code) {
19 | if (err) {
20 | throw err;
21 | }
22 |
23 | var file = path.join(EXAMPLES, 'basic', 'obfuscated.js');
24 | fs.writeFileSync(file, code);
25 | assert('hello world' === require(file).helloWorld());
26 | });
27 |
--------------------------------------------------------------------------------
/test/acceptance/express.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('assert');
4 | var fs = require('fs');
5 | var supertest = require('supertest');
6 | var obfuscator = require('../..');
7 |
8 | var EXAMPLES = path.join(__dirname, '..', '..', 'examples');
9 |
10 | var opts = obfuscator.options([
11 | path.join(EXAMPLES, 'express', 'app.js'),
12 | path.join(EXAMPLES, 'express', 'routes', 'index.js'),
13 | path.join(EXAMPLES, 'express', 'routes', 'user.js')
14 | ],
15 | EXAMPLES,
16 | path.join(EXAMPLES, 'express', 'app.js'),
17 | true);
18 |
19 | obfuscator.obfuscator(opts, function (err, code) {
20 | if (err) {
21 | throw err;
22 | }
23 |
24 | var file = path.join(EXAMPLES, 'express', 'obfuscated.js');
25 |
26 | fs.writeFileSync(file, code);
27 |
28 | var app = require(file).app;
29 | app.listen(3344, function () {
30 | var request = supertest(app);
31 | request.get('/').expect(200, function () {
32 | request.get('/users').expect(200, process.exit);
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/examples/express/app.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Module dependencies.
4 | */
5 |
6 | var express = require('express')
7 | , routes = require('./routes')
8 | , user = require('./routes/user')
9 | , http = require('http')
10 | , path = require('path');
11 |
12 | var app = express();
13 |
14 | app.configure(function(){
15 | app.set('port', process.env.PORT || 3000);
16 | app.set('views', __dirname + '/views');
17 | app.set('view engine', 'ejs');
18 | app.use(express.favicon());
19 | app.use(express.logger('dev'));
20 | app.use(express.bodyParser());
21 | app.use(express.methodOverride());
22 | app.use(express.cookieParser('your secret here'));
23 | app.use(express.session());
24 | app.use(app.router);
25 | app.use(require('stylus').middleware(__dirname + '/public'));
26 | app.use(express.static(path.join(__dirname, 'public')));
27 | });
28 |
29 | app.configure('development', function(){
30 | app.use(express.errorHandler());
31 | });
32 |
33 | app.get('/', routes.index);
34 | app.get('/users', user.list);
35 |
36 | exports.app = app;
37 |
--------------------------------------------------------------------------------
/test/acceptance/http.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('assert');
4 | var fs = require('fs');
5 | var supertest = require('supertest');
6 | var obfuscator = require('../..');
7 |
8 | var EXAMPLES = path.join(__dirname, '..', '..', 'examples');
9 |
10 | var opts = obfuscator.options([
11 | path.join(EXAMPLES, 'http', 'server.js'),
12 | path.join(EXAMPLES, 'http', 'respond.js')
13 | ],
14 | EXAMPLES,
15 | path.join(EXAMPLES, 'http', 'server.js'),
16 | false);
17 |
18 | obfuscator.obfuscator(opts, function (err, code) {
19 | if (err) {
20 | throw err;
21 | }
22 |
23 | var file = path.join(EXAMPLES, 'http', 'obfuscated.js');
24 |
25 | fs.writeFileSync(file, code);
26 |
27 | var app = require(file);
28 | app.start(function () {
29 | var request = supertest('http://localhost:' + app.port);
30 | request.get('/').expect(200, function () {
31 | request.get('/?foo=bar').expect('foo? bar.', function () {
32 | request.get('/?bar=foo').expect('bar? foo.', app.close);
33 | });
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/acceptance/json-everywhere.js:
--------------------------------------------------------------------------------
1 |
2 | var path = require('path');
3 | var assert = require('better-assert');
4 | var fs = require('fs');
5 | var glob = require('glob');
6 | var obfusactor = require('../..');
7 |
8 | var dir = path.join(__dirname, '../../examples/json-everywhere');
9 |
10 | try {
11 | fs.unlinkSync(path.join(dir, 'obfuscated.js'));
12 | } catch (e) {}
13 |
14 | var opts = {
15 | files: glob.sync(path.join(dir, '/**/*.js*')),
16 | root: path.join(__dirname, '../../examples'),
17 | entry: path.join(dir, 'app.js'),
18 | strings: true
19 | };
20 |
21 | obfusactor(opts, function (err, js) {
22 | if (err) throw err;
23 | var file = path.join(dir, 'obfuscated.js');
24 | fs.writeFile(file, js, function (err) {
25 | if (err) throw err;
26 | var app = require(file);
27 |
28 | assert(
29 | 'The lead character, called "The Bride", '
30 | + 'was a member of the Deadly Viper '
31 | + 'Assassination Squad, lead by her '
32 | + 'lover "Bill".' === app.killbill);
33 |
34 | assert('dev' === app('development'));
35 | assert('prod' === app('production'));
36 |
37 | });
38 | });
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "obfuscator",
3 | "version": "0.5.4",
4 | "scripts": {
5 | "test": "make all"
6 | },
7 | "description": "Code protection / obfuscation for node",
8 | "keywords": [
9 | "code protection",
10 | "obfuscator",
11 | "uglify"
12 | ],
13 | "author": "Stephen Mathieson ",
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/stephenmathieson/node-obfuscator.git"
17 | },
18 | "dependencies": {
19 | "uglify-js": "~2.4.0",
20 | "commander": "~2.0.0"
21 | },
22 | "devDependencies": {
23 | "better-assert": "~1.0.0",
24 | "glob": "~3.2.7",
25 | "jshint": "~2.0.1",
26 | "mocha": "^2.3.0",
27 | "should": "~1.2.2",
28 | "supertest": "~0.6.0"
29 | },
30 | "bin": {
31 | "obfuscator": "bin/obfuscator"
32 | },
33 | "main": "index.js",
34 | "bugs": {
35 | "url": "https://github.com/stephenmathieson/node-obfuscator/issues"
36 | },
37 | "homepage": "https://github.com/stephenmathieson/node-obfuscator",
38 | "directories": {
39 | "example": "examples",
40 | "test": "test"
41 | },
42 | "license": "MIT"
43 | }
44 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | BINS = node_modules/.bin
3 | SRC = index.js $(wildcard lib/*.js)
4 | TESTS = $(wildcard test/*.js)
5 | EXAMPLES = $(wildcard examples/*/*.js)
6 | ACCEPTANCE = $(wildcard test/acceptance/*.js)
7 |
8 | MOCHA_REPORTER ?= spec
9 | JSCOVERAGE ?= jscoverage
10 |
11 | all: lint test test-acceptance
12 |
13 | test: node_modules
14 | @$(BINS)/mocha \
15 | -R $(MOCHA_REPORTER) \
16 | -r should
17 |
18 | test-acceptance: examples/resolve/node_modules \
19 | examples/express/node_modules \
20 | $(ACCEPTANCE)
21 | @echo 'everything looks good'
22 |
23 | examples/resolve/node_modules: examples/resolve/package.json
24 | @cd examples/resolve; npm install
25 |
26 | examples/express/node_modules: examples/express/package.json
27 | @cd examples/express; npm install
28 |
29 | $(ACCEPTANCE):
30 | node $@
31 |
32 | test-cov: coverage.html
33 | @open coverage.html
34 |
35 | coverage.html: node_modules lib-cov $(TESTS)
36 | @OBF_COV=1 \
37 | MOCHA_REPORTER=html-cov \
38 | $(MAKE) test > coverage.html
39 |
40 | lib-cov: $(SRC)
41 | @rm -rf $@
42 | @$(JSCOVERAGE) lib $@
43 |
44 | lint: node_modules
45 | @$(BINS)/jshint \
46 | --verbose \
47 | $(SRC) \
48 | $(TESTS) \
49 | $(ACCEPTANCE)
50 |
51 | node_modules: package.json
52 | @npm install
53 |
54 | clean:
55 | rm -rf examples/*/obfuscated.js
56 | rm -rf examples/*/node_modules
57 | rm -rf lib-cov coverage.html
58 |
59 | .PHONY: test $(ACCEPTANCE) clean
60 |
--------------------------------------------------------------------------------
/bin/obfuscator:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /*jslint node:true*/
4 |
5 | 'use strict';
6 |
7 | var program = require('commander'),
8 | fs = require('fs'),
9 | Options = require('..').Options,
10 | obfuscator = require('..').obfuscator;
11 |
12 | program
13 | .version(require('../package.json').version)
14 | .usage('[options] ')
15 | .option('--entry ', 'The application\'s entry point', String)
16 | .option('--root ', 'The application\'s root directory',
17 | String, process.cwd())
18 | .option('--out ', 'The output file', String)
19 | .option('--no-color', 'Disable colored output', Boolean, false)
20 | .option('--strings', 'Obfuscate strings contained in the project')
21 | .parse(process.argv);
22 |
23 | var options = new Options(program.args,
24 | program.root,
25 | program.entry,
26 | program.strings);
27 |
28 | obfuscator(options, function (err, obfuscated) {
29 | if (err) {
30 | throw err;
31 | }
32 |
33 | if (program.out) {
34 | fs.writeFile(program.out, obfuscated, function (err) {
35 | if (err) {
36 | throw err;
37 | }
38 |
39 | var message = 'wrote ';
40 |
41 | if (program['no-color']) {
42 | message += program.out;
43 | } else {
44 | message += '\x1B[36m' + program.out + '\x1B[39m';
45 | }
46 |
47 | console.log(message);
48 | });
49 | } else {
50 | console.log(obfuscated);
51 | }
52 | });
53 |
54 |
--------------------------------------------------------------------------------
/docs.md:
--------------------------------------------------------------------------------
1 | # Obfuscator Documentation
2 |
3 | ### `obfuscator(options, cb)`
4 |
5 | #### Parameters
6 |
7 | * options `Object` The options
8 | * cb `Function` Callback: `function (err, obfuscated)`
9 |
10 | Obfuscate and concatenate a NodeJS _"package"_
11 | because corporate says so.
12 |
13 | ### `obfuscator.options(files, root, entry, [strings])`
14 |
15 | #### Parameters
16 |
17 | * files `Array` The files contained in the package
18 | * root `String` The root of the package
19 | * entry `String` The entry point
20 | * [strings] `Boolean` Shall strings be obfuscated
21 |
22 | #### Returns
23 |
24 | `Object`
25 |
26 | Create an `options` object for the `obfuscator`
27 |
28 | Aliases (for back-compat):
29 |
30 | - `Options`
31 | - `ObfuscatorOptions`
32 |
33 |
34 | Examples:
35 |
36 | ```js
37 | var opts = new obfuscator.Options(
38 | // files
39 | [ './myfile.js', './mydir/thing.js'],
40 | // root
41 | './',
42 | // entry
43 | 'myfile.js',
44 | // strings
45 | true)
46 |
47 | var opts = obfuscator.options({...})
48 | ```
49 |
50 | ### `obfuscator.utils.hex(str)`
51 |
52 | #### Parameters
53 |
54 | * str `String`
55 |
56 | #### Returns
57 |
58 | `String`
59 |
60 | Convert (or _obfuscate_) a string to its escaped
61 | hexidecimal representation. For example,
62 | `hex('a')` will return `'\x63'`.
63 |
64 | ### `obfuscator.utils.strings(js)`
65 |
66 | #### Parameters
67 |
68 | * js `String`
69 |
70 | #### Returns
71 |
72 | `String`
73 |
74 | Mangle simple strings contained in some `js`
75 |
76 | Strings will be _mangled_ by replacing each
77 | contained character with its escaped hexidecimal
78 | representation. For example, "a" will render
79 | to "\x63".
80 |
81 | Strings which contain non-alphanumeric characters
82 | other than `.-_/` will be ignored.
83 |
84 | Strings not wrapped in double quotes will be ignored.
85 |
86 | Example:
87 |
88 | ```js
89 | utils.strings('var foo = "foo"';);
90 | //=> 'var foo = "\x66\x6f\x6f";'
91 | ```
92 |
93 |
--------------------------------------------------------------------------------
/lib/require.js:
--------------------------------------------------------------------------------
1 |
2 | // based on TJ Holowaychuk's commonjs require binding
3 |
4 | function require(p, root) {
5 | // third-party module? use native require
6 | if ('.' != p[0] && '/' != p[0])
7 | return native_require(p);
8 |
9 | root = root || 'root';
10 |
11 | var path = require.resolve(p);
12 |
13 | // if it's a non-registered json file, it
14 | // must be at the root of the project
15 | if (!path && /\.json$/i.test(p))
16 | return native_require('./' + require.basename(p));
17 |
18 | var module = require.cache[path];
19 |
20 | if (!module) {
21 | try {
22 | return native_require(p);
23 | } catch (err) {
24 | throw new Error('failed to require "' + p + '" from ' + root +'\n' + err.message + '\n' + err.stack);
25 | }
26 | }
27 |
28 | if (!module.exports) {
29 | module.exports = {};
30 | module.call(module.exports, module, module.exports,
31 | require.relative(path));
32 | }
33 |
34 | return module.exports;
35 | }
36 |
37 | // same as node's `require`
38 | require.cache = {};
39 |
40 | // node's native `path.basename`
41 | require.basename = native_require('path').basename;
42 |
43 | require.resolve = function (path) {
44 | // GH-12
45 | if ('.' != path[0]) return native_require.resolve(path);
46 |
47 | var pathWithSlash = path.slice(-1) === '/' ? path : path + '/';
48 | var paths = [
49 | path,
50 | path + '.js',
51 | pathWithSlash + 'index.js',
52 | path + '.json',
53 | pathWithSlash + 'index.json'
54 | ];
55 |
56 | for (var i = 0, p; p = paths[i]; i++) {
57 | if (require.cache[p]) return p;
58 | }
59 | };
60 |
61 | require.register = function (path, fn) {
62 | require.cache[path] = fn;
63 | };
64 |
65 | require.relative = function (parent) {
66 | function relative(p) {
67 | if ('.' != p[0]) return require(p);
68 |
69 | var path = parent.split('/');
70 | var segs = p.split('/');
71 | path.pop();
72 |
73 | for (var i = 0, len = segs.length; i < len; i += 1) {
74 | var seg = segs[i];
75 | if ('..' == seg) {
76 | path.pop();
77 | } else if ('.' != seg) {
78 | path.push(seg);
79 | }
80 | }
81 |
82 | return require(path.join('/'), parent);
83 | }
84 |
85 | relative.resolve = require.resolve;
86 | relative.cache = require.cache;
87 | return relative;
88 | };
89 |
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 |
2 | 0.5.4 / 2015-09-14
3 | ==================
4 |
5 | * require: support `require('.\')` (@benweet, #28)
6 |
7 | 0.5.3 / 2015-09-01
8 | ==================
9 |
10 | * register: ensure a trailing newline after each file's contents (#26)
11 | * package: update "mocha" to 2.3.0
12 | * test: add assertions for files with trailing commas
13 |
14 | 0.5.2 / 2015-04-03
15 | ==================
16 |
17 | * travis: Remove 0.8, add 0.12 and iojs
18 |
19 | 0.5.1 / 2015-04-03
20 | ==================
21 |
22 | * require: Also throw the error native_require returns (@JamyDev, #20)
23 |
24 | 0.5.0 / 2014-09-30
25 | ==================
26 |
27 | * require: Fallback to the native `require()`
28 | * .jshintrc: Relax options
29 | * examples: Add a "non-included file" example
30 | * Readme: Add note about stupid usage of `require()`
31 | * docs: Use escaped string
32 |
33 | 0.4.2 / 2014-04-07
34 | ==================
35 |
36 | * pkg: npm init
37 | * pkg: Remove 'preferGlobal'
38 | * Use Uglify's TreeTransformer to mangle strings rather than regular expressions.
39 |
40 | 0.4.1 / 2014-03-31
41 | ==================
42 |
43 | * Fix obfuscation of escaped characters
44 | * license: update copyright year
45 |
46 | 0.4.0 / 2014-02-26
47 | ==================
48 |
49 | * Add `resolve.resolve` support ([GH-12](https://github.com/stephenmathieson/node-obfuscator/issues/12))
50 | * Fix mocked require tests
51 | * Cleanup makefile
52 | * Refactor JSON file support
53 | * Obfuscate all strings
54 | * Remove component-builder example
55 |
56 | 0.3.0 / 2013-12-20
57 | ==================
58 |
59 | - removed grunt support; use [grunt-obfuscator](https://github.com/stephenmathieson/grunt-obfuscator) instead.
60 |
61 | 0.2.2 / 2013-10-22
62 | ==================
63 |
64 | - update uglify-js to ~2.4.0
65 |
66 | 0.2.1 / 2013-09-12
67 | ==================
68 |
69 | - fixed grunt task ([#9](https://github.com/stephenmathieson/node-obfuscator/pull/9))
70 |
71 | 0.2.0 / 2013-08-13
72 | ==================
73 |
74 | - fixed windows pathing bug ([#8](https://github.com/stephenmathieson/node-obfuscator/pull/8))
75 | - `obfuscator` is now a function ([#1](https://github.com/stephenmathieson/node-obfuscator/issues/1))
76 | - added support for custom compression options ([#2](https://github.com/stephenmathieson/node-obfuscator/issues/2))
77 | - updated UglifyJS
78 |
79 | 0.1.0 / 2013-05-17
80 | ==================
81 |
82 | - added `string` option, optionally obfuscating strings contained in your package
83 | - added ability to export from an _"obfuscated"_ package
84 |
85 | 0.0.2 / 2013-02-10
86 | ==================
87 |
88 | - initial release
89 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 |
2 | # maintainer wanted
3 |
4 | if you want this package, open an issue or email me
5 |
6 |
7 |
8 | # Obfuscator
9 |
10 | [](https://travis-ci.org/stephenmathieson/node-obfuscator) [](https://gemnasium.com/stephenmathieson/node-obfuscator)
11 |
12 | Obfuscate your node packages because your boss says so!
13 |
14 | ## Why?
15 |
16 | Because I had this conversation:
17 |
18 | > **me**: hi boss. this application should be written in node, not java. node is good and stuff.
19 |
20 | > **boss**: oh, okay. node sounds great. what about code protection so people don't steal our software?
21 |
22 | > **me**: ...
23 |
24 | > **boss**: you can't use node.
25 |
26 | ... but now:
27 |
28 | > **me**: hi boss. first off, code protection is stupid. secondly, java can be decompiled.
29 |
30 | > **boss**: but decompiling java is a lot of work.
31 |
32 | > **me**: so is [un-obfuscating javascript](http://github.com/stephenmathieson/node-obfuscator)!
33 |
34 | ## Usage
35 |
36 | ### Command Line (installed globally with the `-g` flag)
37 |
38 | ```
39 | $ obfuscator --entry app.js ./app.js ./routes/index.js ./routes/user.js
40 | ```
41 |
42 | ### JavaScript API
43 |
44 | ```javascript
45 | var Options = require('obfuscator').Options;
46 | var obfuscator = require('obfuscator').obfuscator;
47 | var fs = require('fs');
48 | var options = new Options([ '/path/to/file1.js', '/path/to/file2.js' ], '/path/to', 'file1.js', true);
49 |
50 | // custom compression options
51 | // see https://github.com/mishoo/UglifyJS2/#compressor-options
52 | options.compressor = {
53 | conditionals: true,
54 | evaluate: true,
55 | booleans: true,
56 | loops: true,
57 | unused: false,
58 | hoist_funs: false
59 | };
60 |
61 | obfuscator(options, function (err, obfuscated) {
62 | if (err) {
63 | throw err;
64 | }
65 | fs.writeFile('./cool.js', obfuscated, function (err) {
66 | if (err) {
67 | throw err;
68 | }
69 |
70 | console.log('cool.');
71 | });
72 | });
73 | ```
74 |
75 | Also see [acceptance tests](https://github.com/stephenmathieson/node-obfuscator/tree/master/test/acceptance) or the [docs](https://github.com/stephenmathieson/node-obfuscator/tree/master/docs.md).
76 |
77 | ## How it Works
78 |
79 | Think [browserify](https://github.com/substack/node-browserify) only for node, plus [UglifyJs](https://github.com/mishoo/UglifyJS2). Your entire project will be concatenated into a single file. This file will contain a stubbed `require` function, which will handle everything for you. This single file will be run through [UglifyJs](https://github.com/mishoo/UglifyJS2), making it more difficult to read.
80 |
81 | Undoing this process is hopefully as painful as decompiling java bytecode.
82 |
83 | ## Known bugs and limitations
84 |
85 | - everything (including json, subdirectories, etc.) must be in the `root` of your project.
86 | - you're not able to use many of the native module's `require` features; only `require.cache` and `require.resolve` have been exposed.
87 | - you're not able to do silly things with `module.`
88 | - dynamically built `require()`s are not supported; chances are, there's a significantly cleaner way of handling loading your depenencies anyway.
89 |
90 | ## License
91 |
92 | (The MIT License)
93 |
94 | Copyright (c) 2013-2014 Stephen Mathieson <me@stephenmathieson.com>
95 |
96 | Permission is hereby granted, free of charge, to any person obtaining
97 | a copy of this software and associated documentation files (the
98 | 'Software'), to deal in the Software without restriction, including
99 | without limitation the rights to use, copy, modify, merge, publish,
100 | distribute, sublicense, and/or sell copies of the Software, and to
101 | permit persons to whom the Software is furnished to do so, subject to
102 | the following conditions:
103 |
104 | The above copyright notice and this permission notice shall be
105 | included in all copies or substantial portions of the Software.
106 |
107 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
108 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
109 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
110 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
111 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
112 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
113 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
114 |
--------------------------------------------------------------------------------
/lib/utils.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | var uglifyjs = require('uglify-js');
5 | var merge = require('util')._extend;
6 |
7 | /**
8 | * Create an `AST` from the given `js`, invoking `cb(err, ast)`
9 | *
10 | * @api private
11 | * @param {String} js
12 | * @param {Function} cb
13 | */
14 |
15 | exports.ast = function (js, cb) {
16 | try {
17 | var ast = uglifyjs.parse(js);
18 | cb(null, ast);
19 | } catch (err) {
20 | var e = new Error(err.message);
21 | // cheap hack to use actual errors
22 | // rather than the indecipherable JS_Parse_Error garbage
23 | merge(e, err);
24 | // expose the bad js
25 | e.source = js;
26 | cb(e);
27 | }
28 | };
29 |
30 | /**
31 | * Compress the given `ast`, conditionally using `opts`
32 | *
33 | * @api private
34 | * @param {Object} [opts]
35 | * @return {AST}
36 | */
37 |
38 | exports.compress = function (ast, opts) {
39 | opts = opts || exports.compress.defaults;
40 | var compressor = uglifyjs.Compressor(opts);
41 | // for some stupid reason, this is the
42 | // only non-modifier method...
43 | return ast.transform(compressor);
44 | };
45 |
46 | /**
47 | * Default compression options
48 | *
49 | * @api private
50 | * @type {Object}
51 | */
52 |
53 | exports.compress.defaults = {
54 | sequences: true,
55 | properties: true,
56 | dead_code: true,
57 | drop_debugger: true,
58 | unsafe: true,
59 | conditionals: true,
60 | comparisons: true,
61 | evaluate: true,
62 | booleans: true,
63 | loops: true,
64 | unused: true,
65 | hoist_funs: true,
66 | hoist_vars: true,
67 | if_return: true,
68 | join_vars: true,
69 | cascade: true,
70 | warnings: false
71 | };
72 |
73 | /**
74 | * Uglify the given `js` with `opts`
75 | *
76 | * @api private
77 | * @param {String} js
78 | * @param {Object} [opts]
79 | * @param {Function} cb
80 | */
81 |
82 | exports.uglify = function (js, opts, cb) {
83 |
84 | /**
85 | * Handle mangling and compression of the generated `AST`
86 | *
87 | * @api private
88 | * @param {Error} err
89 | * @param {AST} ast
90 | */
91 |
92 | function handleAST(err, ast) {
93 | if (err) return cb(err);
94 |
95 | var stream = new uglifyjs.OutputStream;
96 |
97 | ast.figure_out_scope();
98 | ast.mangle_names();
99 | ast = exports.compress(ast, opts.compressor);
100 |
101 | if (opts.strings) {
102 | ast = mangleStrings(ast);
103 | // disable uglify's string escaping to prevent
104 | // double escaping our hex
105 | stream.print_string = function (str) {
106 | return this.print('"' + str + '"');
107 | };
108 | }
109 |
110 | ast.print(stream);
111 | return cb(null, stream.toString());
112 | }
113 |
114 | if (typeof opts === 'function') {
115 | cb = opts;
116 | opts = {};
117 | }
118 |
119 | // build the AST
120 | exports.ast(js, handleAST);
121 | };
122 |
123 |
124 | /**
125 | * Escape map.
126 | */
127 |
128 | var map = {
129 | '\b': '\\b',
130 | '\f': '\\f',
131 | '\n': '\\n',
132 | '\r': '\\r',
133 | '\t': '\\t',
134 | '\\': '\\\\'
135 | };
136 |
137 | /**
138 | * Convert (or _obfuscate_) a string to its escaped
139 | * hexidecimal representation. For example,
140 | * `hex('a')` will return `'\x63'`.
141 | *
142 | * @api public
143 | * @name obfuscator.utils.hex
144 | * @param {String} str
145 | * @return {String}
146 | */
147 |
148 | exports.hex = function (str) {
149 | var result = '';
150 |
151 | for (var i = 0, l = str.length; i < l; i++) {
152 | var char = str[i];
153 |
154 | if (map[char]) {
155 | result += map[char];
156 | } else if ('\\' == char) {
157 | result += '\\' + str[++i];
158 | } else {
159 | result += '\\x' + str.charCodeAt(i).toString(16);
160 | }
161 | }
162 | return result;
163 | };
164 |
165 |
166 | /**
167 | * Mangle strings contained in the given `ast`.
168 | *
169 | * @api private
170 | * @param {AST} ast
171 | * @return {AST} mangled ast
172 | */
173 |
174 | function mangleStrings(ast) {
175 | var transformer = new uglifyjs.TreeTransformer(null, mangleString);
176 | return ast.transform(transformer);
177 | }
178 |
179 | /**
180 | * Mangle the given `node`, assuming it's an `AST_String`.
181 | *
182 | * @api private
183 | * @param {AST_Node} node
184 | * @return {AST_Node}
185 | */
186 |
187 | function mangleString(node) {
188 | if (!(node instanceof uglifyjs.AST_String)) {
189 | return;
190 | }
191 |
192 | var str = node.getValue();
193 | var hex = exports.hex(str);
194 | var obj = merge({}, node);
195 | obj.value = hex;
196 | return new uglifyjs.AST_String(obj);
197 | }
198 |
--------------------------------------------------------------------------------
/lib/obfuscator.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | var fs = require('fs');
5 | var path = require('path');
6 | var uglify = require('uglify-js');
7 | var utils = require('./utils');
8 |
9 | var rJSON = /\.json$/;
10 | var __require = fs.readFileSync(path.join(__dirname, './require.js'), 'utf-8');
11 |
12 | module.exports = obfuscator;
13 |
14 |
15 | /**
16 | * Obfuscate and concatenate a NodeJS _"package"_
17 | * because corporate says so.
18 | *
19 | * @api public
20 | * @param {Object} options The options
21 | * @param {Function} cb Callback: `function (err, obfuscated)`
22 | */
23 |
24 | function obfuscator(options, cb) {
25 | if (!options.files || !options.root || !options.entry) {
26 | return cb(new TypeError('Invalid options'));
27 | }
28 |
29 | obfuscator.concatenate(options, function (err, code) {
30 | if (err) {
31 | return cb(err);
32 | }
33 |
34 | utils.uglify(code, options, cb);
35 | });
36 | }
37 |
38 | // back-compat alias
39 | obfuscator.obfuscator = obfuscator;
40 |
41 | /**
42 | * Expose the current version
43 | *
44 | * @api private
45 | * @type {String}
46 | */
47 |
48 | obfuscator.version = require('../package').version;
49 |
50 | /**
51 | * Expose utils
52 | *
53 | * @api private
54 | * @type {Object}
55 | */
56 |
57 | obfuscator.utils = utils;
58 |
59 | /**
60 | * Create an `options` object for the `obfuscator`
61 | *
62 | * Aliases (for back-compat):
63 | *
64 | * - `Options`
65 | * - `ObfuscatorOptions`
66 | *
67 | *
68 | * Examples:
69 | *
70 | * ```js
71 | * var opts = new obfuscator.Options(
72 | * // files
73 | * [ './myfile.js', './mydir/thing.js'],
74 | * // root
75 | * './',
76 | * // entry
77 | * 'myfile.js',
78 | * // strings
79 | * true)
80 | *
81 | * var opts = obfuscator.options({...})
82 | * ```
83 | *
84 | * @api public
85 | * @param {Array} files The files contained in the package
86 | * @param {String} root The root of the package
87 | * @param {String} entry The entry point
88 | * @param {Boolean} [strings] Shall strings be obfuscated
89 | * @return {Object}
90 | */
91 |
92 | obfuscator.options = function (files, root, entry, strings) {
93 | // TODO support globbling
94 | if (!Array.isArray(files) || !files.length) {
95 | throw new TypeError('Invalid files array');
96 | }
97 |
98 | if (typeof root !== 'string' || !root) {
99 | throw new TypeError('Invalid root directory');
100 | }
101 |
102 | if (typeof entry !== 'string' || !entry) {
103 | throw new TypeError('Invalid entry point');
104 | }
105 |
106 | return {
107 | files: files,
108 | root: root,
109 | entry: dotslash(entry),
110 | strings: strings
111 | };
112 | };
113 |
114 | // alias
115 | obfuscator.Options =
116 | obfuscator.ObfuscatorOptions = obfuscator.options;
117 |
118 | /**
119 | * Register a `file` in location to `root`
120 | *
121 | * @api private
122 | * @param {String} root
123 | * @param {String} file
124 | * @param {Function} cb
125 | */
126 |
127 | obfuscator.register = function (root, file, cb) {
128 | var filename = dotslash(path.relative(root, file));
129 |
130 | fs.readFile(file, 'utf-8', function (err, data) {
131 | if (err) {
132 | return cb(err);
133 | }
134 |
135 | var code =
136 | 'require.register("' + filename + '",'
137 | + 'function (module, exports, require) {'
138 | + (rJSON.test(file)
139 | // just export json
140 | ? 'module.exports = ' + data + ';'
141 | // add the file as is
142 | : data) + '\n'
143 | + '});';
144 | return cb(null, code);
145 | });
146 | };
147 |
148 | /**
149 | * Concatenate a list of files for pre-obfuscation
150 | *
151 | * @api private
152 | * @param {Object} options
153 | * @param {Function} [cb]
154 | */
155 |
156 | obfuscator.concatenate = function (options, cb) {
157 | var entry = dotslash(path.relative(options.root, options.entry));
158 | var index = -1;
159 | var built = [];
160 |
161 | function complete() {
162 | // export the exported stuff from entry
163 | built.push('this_module.exports = require("' + entry + '");');
164 | // end iffe
165 | built.push('}(require, module));');
166 | // done :)
167 | cb(null, built.join('\n'));
168 | }
169 |
170 | // begin iffe with access to node's native
171 | // require and the current module
172 | built.push('(function (native_require, this_module) {');
173 | // add the `require` shim
174 | built.push(__require);
175 |
176 | (function next() {
177 | index++;
178 | var file = options.files[index];
179 |
180 | // no remaining files?
181 | if (!file) {
182 | return complete();
183 | }
184 |
185 | // register the file
186 | obfuscator.register(options.root, file, function (err, data) {
187 | if (err) {
188 | return cb(err);
189 | }
190 |
191 | built.push(data);
192 | next();
193 | });
194 | }());
195 | };
196 |
197 | /**
198 | * Force a filepath to start with _./_
199 | *
200 | * @api private
201 | * @param {String} filepath
202 | * @return {String}
203 | */
204 |
205 | function dotslash(filepath) {
206 | filepath = filepath.replace(/\\/g, '/');
207 | switch (filepath[0]) {
208 | case '.':
209 | case '/':
210 | return filepath;
211 | default:
212 | return './' + filepath;
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/test/obfuscator.js:
--------------------------------------------------------------------------------
1 |
2 | 'use strict';
3 |
4 | var read = require('fs').readFileSync,
5 | path = require('path'),
6 | obfuscator = require('..'),
7 | utils = obfuscator.utils,
8 | uglifyjs = require('uglify-js'),
9 | assert = require('better-assert');
10 |
11 | var FIXTURES = path.join(__dirname, '..', 'examples');
12 |
13 | function fixture(file) {
14 | return path.join(FIXTURES, file);
15 | }
16 |
17 | fixture.read = function (file) {
18 | return read(fixture(file));
19 | };
20 |
21 | describe('obfuscator', function () {
22 | it('should be a function', function () {
23 | obfuscator.should.be.a.function;
24 | });
25 |
26 | describe('.obfuscator()', function () {
27 | it('should be a function', function () {
28 | obfuscator.obfuscator.should.be.a.function;
29 | });
30 |
31 | it('should mirror obfuscator', function () {
32 | obfuscator.obfuscator.should.be.equal(obfuscator);
33 | });
34 |
35 | it('should error when given invalid options', function (done) {
36 | obfuscator.obfuscator({}, function (err) {
37 | if (!err) {
38 | throw new Error('should have errored');
39 | }
40 | done();
41 | });
42 | });
43 |
44 | it('should obfuscate a package', function (done) {
45 | var opts = obfuscator.options([
46 | fixture('basic/hello.js'),
47 | fixture('basic/index.js'),
48 | fixture('basic/hello-world.js')
49 | ],
50 | FIXTURES,
51 | fixture('basic/hello-world'));
52 |
53 | obfuscator.obfuscator(opts, function (err, code) {
54 | if (err) {
55 | return done(err);
56 | }
57 |
58 | code.should.be.a.string;
59 | done();
60 | });
61 | });
62 |
63 | it('should not obfuscate strings by default', function (done) {
64 | var opts = obfuscator.options([
65 | fixture('basic/hello.js'),
66 | fixture('basic/index.js'),
67 | fixture('basic/hello-world.js')
68 | ],
69 | FIXTURES,
70 | fixture('basic/hello-world'));
71 |
72 | obfuscator.obfuscator(opts, function (err, code) {
73 | if (err) {
74 | return done(err);
75 | }
76 |
77 | code.should.include('basic/hello');
78 | code.should.include('basic/index');
79 | code.should.include('basic/hello-world');
80 | done();
81 | });
82 | });
83 |
84 | it('should obfuscate strings when strings=true', function (done) {
85 | var opts = obfuscator.options([
86 | fixture('basic/hello.js'),
87 | fixture('basic/index.js'),
88 | fixture('basic/hello-world.js')
89 | ],
90 | FIXTURES,
91 | fixture('basic/hello-world'),
92 | true);
93 |
94 | obfuscator.obfuscator(opts, function (err, code) {
95 | if (err) {
96 | return done(err);
97 | }
98 |
99 | code.should.not.include('basic/hello');
100 | code.should.not.include('basic/index');
101 | code.should.not.include('basic/hello-world');
102 | done();
103 | });
104 | });
105 |
106 | it('should support files with trailing comments', function(done){
107 | var file = fixture('comment-at-end/index.js');
108 | var opts = obfuscator.options([ file ], FIXTURES, file);
109 | obfuscator.obfuscator(opts, done);
110 | });
111 | });
112 |
113 | describe('.options()', function () {
114 | it('should be a function', function () {
115 | obfuscator.Options.should.be.a.function;
116 | });
117 |
118 | it('should be aliased as Options', function () {
119 | obfuscator.options.should.be.eql(obfuscator.Options);
120 | });
121 |
122 | it('should be aliased as ObfuscatorOptions', function () {
123 | obfuscator.options.should.be.eql(obfuscator.ObfuscatorOptions);
124 | });
125 |
126 | it('should throw without files', function () {
127 | (function () {
128 | obfuscator.options(null, 'foo', 'bar');
129 | }).should.throw(/files/);
130 | });
131 |
132 | it('should throw with a bad root', function () {
133 | (function () {
134 | obfuscator.options([ 'file.js' ], null, 'bar');
135 | }).should.throw(/root/);
136 | });
137 |
138 | it('should throw with a bad entry', function () {
139 | (function () {
140 | obfuscator.options([ 'file.js' ], 'foo', null);
141 | }).should.throw(/entry/);
142 | });
143 |
144 | it('should force ./ on the entrypoint', function () {
145 | var opts = obfuscator.options(['file.js'], 'foo', 'bar');
146 | opts.entry.should.be.equal('./bar');
147 | });
148 |
149 | it('should work when invoked with "new"', function () {
150 | var opts = new obfuscator.Options(['file.js'], 'foo', 'bar');
151 | opts.files.should.be.eql(['file.js']);
152 | opts.root.should.be.equal('foo');
153 | opts.entry.should.be.equal('./bar');
154 | });
155 |
156 | it('should work when invoked without "new"', function () {
157 | var opts = obfuscator.options(['file.js'], 'foo', 'bar');
158 | opts.files.should.be.eql(['file.js']);
159 | opts.root.should.be.equal('foo');
160 | opts.entry.should.be.equal('./bar');
161 | });
162 | });
163 |
164 | describe('.register()', function () {
165 | it('should be a function', function () {
166 | obfuscator.register.should.be.a.function;
167 | });
168 |
169 | it('should handle ENOENT errors', function (done) {
170 | obfuscator.register('fakeroot', 'fakefile', function (err) {
171 | err.code.should.be.equal('ENOENT');
172 | done();
173 | });
174 | });
175 |
176 | it('should wrap the file contents with require stuff', function (done) {
177 | obfuscator.register(FIXTURES, fixture('basic/hello.js'), function (err, js) {
178 | if (err) {
179 | done(err);
180 | }
181 |
182 | js.should.include(fixture.read('basic/hello.js'));
183 | js.should.match(/^require\.register/);
184 | js.should.match(/\}\)\;$/);
185 | done();
186 | });
187 | });
188 |
189 | it('should register the file relative to the root', function (done) {
190 | obfuscator.register(FIXTURES, fixture('basic/hello.js'), function (err, js) {
191 | if (err) {
192 | done(err);
193 | }
194 |
195 | js.should.include('require.register("./basic/hello.js",');
196 | done();
197 | });
198 | });
199 |
200 | it('should handle JSON exporting', function (done) {
201 | obfuscator.register(FIXTURES, fixture('foo-bar.json'), function (err, js) {
202 | if (err) {
203 | done(err);
204 | }
205 |
206 | js.should.include('module.exports = ' + fixture.read('foo-bar.json'));
207 | done();
208 | });
209 | });
210 | });
211 |
212 | describe('.concatenate()', function () {
213 | it('should be a function', function () {
214 | obfuscator.concatenate.should.be.a.function;
215 | });
216 |
217 | describe('given no files', function () {
218 | var code;
219 |
220 | beforeEach(function (done) {
221 | obfuscator.concatenate({
222 | files: [],
223 | root: path.join(FIXTURES, 'basic'),
224 | entry: fixture('basic/hello-world.js')
225 | }, function (err, _code) {
226 | if (err) {
227 | done(err);
228 | }
229 | code = _code;
230 | done();
231 | });
232 | });
233 |
234 | it('should start the iffe', function () {
235 | code.should.match(/^\(function \(native_require, this_module\) \{/);
236 | });
237 |
238 | it('should contain the require mock', function () {
239 | code.should.include('function require(');
240 | });
241 |
242 | it('should export the entry point\'s exports', function () {
243 | code.should.include('this_module.exports = require("./hello-world.js");');
244 | });
245 |
246 | it('should end the iffe', function () {
247 | code.should.match(/\}\(require, module\)\)\;$/);
248 | });
249 | });
250 |
251 | describe('given files', function () {
252 | var code;
253 |
254 | beforeEach(function (done) {
255 | obfuscator.concatenate({
256 | files: [
257 | fixture('basic/hello.js'),
258 | fixture('basic/index.js'),
259 | fixture('basic/hello-world.js')
260 | ],
261 | root: path.join(FIXTURES, 'basic'),
262 | entry: fixture('basic/hello-world.js')
263 | }, function (err, _code) {
264 | if (err) {
265 | done(err);
266 | }
267 | code = _code;
268 | done();
269 | });
270 | });
271 |
272 | it('should wrap each file', function () {
273 | code.should.include(fixture.read('basic/hello.js'));
274 | code.should.include(fixture.read('basic/index.js'));
275 | code.should.include(fixture.read('basic/hello-world.js'));
276 | });
277 |
278 | it('should start the iffe', function () {
279 | code.should.match(/^\(function \(native_require, this_module\) \{/);
280 | });
281 |
282 | it('should contain the require mock', function () {
283 | code.should.include('function require(');
284 | });
285 |
286 | it('should export the entry point\'s exports', function () {
287 | code.should.include('this_module.exports = require("./hello-world.js");');
288 | });
289 |
290 | it('should end the iffe', function () {
291 | code.should.match(/\}\(require, module\)\)\;$/);
292 | });
293 | });
294 |
295 | describe('given a missing file', function () {
296 | it('should pass the error', function (done) {
297 | obfuscator.concatenate({
298 | files: [ 'hello' ],
299 | root: '/dev/null',
300 | entry: 'yeah'
301 | }, function (err) {
302 | err.code.should.be.equal('ENOENT');
303 | done();
304 | });
305 | });
306 | });
307 |
308 | describe('given missing files', function () {
309 | it('should pass the error', function (done) {
310 | obfuscator.concatenate({
311 | files: [ 'hello', 'world', 'yeah', 'things', '1', '2', '3', '4' ],
312 | root: '/dev/null',
313 | entry: 'stuff'
314 | }, function (err) {
315 | err.code.should.be.equal('ENOENT');
316 | done();
317 | });
318 | });
319 | });
320 |
321 | });
322 |
323 | describe('.utils', function () {
324 | describe('.uglify', function () {
325 | it('should pass an error given bad js', function (done) {
326 | utils.uglify('a b c d e', function (err) {
327 | assert(err instanceof Error);
328 | done();
329 | });
330 | });
331 |
332 | it('should allow for custom compression options', function (done) {
333 | var opts = {
334 | compressor: {
335 | join_vars: false
336 | }
337 | },
338 | code = [
339 | '(function () {',
340 | ' var a = "a"',
341 | ' var b = "b"',
342 | ' alert(a + b)',
343 | '}())'
344 | ].join('\n');
345 |
346 | utils.uglify(code, opts, function (err, js) {
347 | if (err) {
348 | return done(err);
349 | }
350 |
351 | js.match(/var/g).length.should.be.equal(2);
352 | done();
353 | });
354 | });
355 |
356 | it('should have default options', function () {
357 | utils.compress.defaults.should.be.an.object;
358 | utils.compress.defaults.join_vars.should.be.true;
359 | });
360 | });
361 |
362 | describe('.ast()', function () {
363 | var bad = 'blah blah blah',
364 | good = 'var hello = "hello"';
365 |
366 | it('should not throw given unparseable js', function (done) {
367 | utils.ast(bad, function () {
368 | done();
369 | });
370 | });
371 |
372 | it('should pass an Error given unparseable js', function (done) {
373 | utils.ast(bad, function (err) {
374 | assert(err instanceof Error);
375 | done();
376 | });
377 | });
378 |
379 | it('should keep JS_Parse_Error properties', function (done) {
380 | utils.ast(bad, function (err) {
381 | assert(err.line);
382 | assert(err.col);
383 | assert(err.message);
384 | done();
385 | });
386 | });
387 |
388 | it('should provide a walkable AST given parseable js', function (done) {
389 | utils.ast(good, function (err, ast) {
390 | assert(ast.walk);
391 |
392 | function walker() {
393 | if (walker.done) {
394 | return;
395 | }
396 | walker.done = true;
397 | done();
398 | }
399 |
400 | ast.walk(new uglifyjs.TreeWalker(walker));
401 | });
402 | });
403 | });
404 |
405 | describe('.hex()', function () {
406 | it('should encode strings to their hex representations', function () {
407 | utils.hex('foo').should.be.equal('\\x66\\x6f\\x6f');
408 | utils.hex('bar').should.be.equal('\\x62\\x61\\x72');
409 | utils.hex('baz').should.be.equal('\\x62\\x61\\x7a');
410 | utils.hex('qaz').should.be.equal('\\x71\\x61\\x7a');
411 | utils.hex('dat').should.be.equal('\\x64\\x61\\x74');
412 | utils.hex('\b').should.be.equal('\\b');
413 | utils.hex('\f').should.be.equal('\\f');
414 | utils.hex('\n').should.be.equal('\\n');
415 | utils.hex('\r').should.be.equal('\\r');
416 | utils.hex('\t').should.be.equal('\\t');
417 | utils.hex('\\').should.be.equal('\\\\');
418 | utils.hex('\\\t').should.be.equal('\\\\\\t');
419 | });
420 | });
421 | });
422 | });
423 |
--------------------------------------------------------------------------------