@dacely/toildefender 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/obfuscator.js ADDED
@@ -0,0 +1,534 @@
1
+ /** @module toildefender */
2
+
3
+ "use strict";
4
+
5
+ var fs = require("fs");
6
+ var assert = require("assert");
7
+
8
+ var _ = require("lodash");
9
+ var legacyBabel = require("babel-core");
10
+ var modernBabel = (() => {
11
+ try {
12
+ return require("@babel/core");
13
+ } catch (e) {
14
+ return null;
15
+ }
16
+ })();
17
+ var escodegen = require("escodegen");
18
+ var escope = require("escope");
19
+ var esprima = require("esprima");
20
+
21
+ var traverser = require("./traverser");
22
+ var utils = require("./utils");
23
+
24
+ var Logger = require("./logger");
25
+
26
+ var prDeadCode = require("./processors/deadCode");
27
+ var prModules = require("./processors/modules");
28
+ var prMethods = require("./processors/methods");
29
+ var prVariables = require("./processors/variables");
30
+ var prScopes = require("./processors/scopes");
31
+ var prFlattener = require("./processors/flattener");
32
+ var prNormalizer = require("./processors/normalizer");
33
+ var prPreprocessing = require("./processors/preprocessing");
34
+ var prPostprocessing = require("./processors/postprocessing");
35
+ var prUglifier = require("./processors/uglifier");
36
+ var prIdentifiers = require("./processors/identifiers");
37
+ var prLiterals = require("./processors/literals");
38
+ var prNumericVm = require("./processors/numericVm");
39
+ var prHealth = require("./processors/health");
40
+
41
+ var defaultOptions = {
42
+ babel: true,
43
+ babelTarget: "ie 11",
44
+ features: {
45
+ dead_code: true,
46
+ scope: true,
47
+ control_flow: true,
48
+ identifiers: true,
49
+ numeric_vm: false,
50
+ object_packing: true,
51
+ literals: true,
52
+ mangle: true,
53
+ compress: true
54
+ },
55
+ logLevel: "warn",
56
+ numericVm: {
57
+ enabled: false,
58
+ maxFunctionSize: 120,
59
+ minFunctionSize: 1,
60
+ mode: "balanced",
61
+ perFunctionDialect: true,
62
+ seed: "toildefender-numeric-vm",
63
+ hashMesh: {
64
+ bindToVmState: true,
65
+ chaffRatio: 0.55,
66
+ deriveDialectFromMesh: false,
67
+ enabled: false,
68
+ encodeChaff: true,
69
+ mode: "balanced",
70
+ serverBound: false,
71
+ unlock: "per-function"
72
+ },
73
+ virtualize: "marked"
74
+ },
75
+ protections: {
76
+ virtualMachine: {
77
+ bigintBytecode: true,
78
+ enabled: false,
79
+ encodeConstants: true,
80
+ mode: "balanced",
81
+ perFunctionDialect: true,
82
+ randomizedOpcodes: true,
83
+ virtualize: "marked"
84
+ },
85
+ hashMesh: {
86
+ bindToVmState: true,
87
+ chaffRatio: 0.55,
88
+ deriveDialectFromMesh: false,
89
+ enabled: false,
90
+ encodeChaff: true,
91
+ mode: "balanced",
92
+ serverBound: false,
93
+ unlock: "per-function"
94
+ }
95
+ },
96
+ preprocessorVariables: {}
97
+ };
98
+
99
+ var featureDeps = {
100
+ dead_code: [ "control_flow" ],
101
+ scope: [ "mangle" ],
102
+ control_flow: [ "scope", "mangle" ],
103
+ identifiers: [ "mangle" ],
104
+ numeric_vm: [],
105
+ object_packing: [ "identifiers" ],
106
+ literals: [ "scope", "mangle" ],
107
+ compress: [ "mangle" ]
108
+ };
109
+
110
+ var featureDescs = {
111
+ dead_code: {
112
+ en: "Insert dead code"
113
+ },
114
+ scope: {
115
+ en: "Flatten the scope (method) structure to obfuscate application structure"
116
+ },
117
+ control_flow: {
118
+ en: "Flatten control flow (if, while, for, etc...) structure to obfuscate control flow"
119
+ },
120
+ identifiers: {
121
+ en: "Obfuscate identifiers (variable, object and property names)"
122
+ },
123
+ numeric_vm: {
124
+ en: "Virtualize selected functions into BigInt-packed numeric VM programs"
125
+ },
126
+ object_packing: {
127
+ en: "Pack object literal keys into numeric schemas instead of alternating key/value arrays"
128
+ },
129
+ literals: {
130
+ en: "Obfuscate literals (numbers, strings)"
131
+ },
132
+ mangle: {
133
+ en: "Shorten identifiers (variable names, function names)"
134
+ },
135
+ compress: {
136
+ en: "Remove unneeded whitespace"
137
+ }
138
+ };
139
+
140
+ exports.features = _.fromPairs(
141
+ _.map(defaultOptions.features, (enabled, feature) =>
142
+ [
143
+ feature,
144
+ {
145
+ dependencies: featureDeps[feature] || [],
146
+ descriptions: featureDescs[feature] || {},
147
+ default: enabled
148
+ }
149
+ ]
150
+ )
151
+ );
152
+
153
+ /**
154
+ * Logs informational and diagnostic messages onto an output device or object.
155
+ *
156
+ * @callback logAdapterCallback
157
+ * @param {string} level - Message level.
158
+ * @param {string} data - Message data.
159
+ */
160
+
161
+ /**
162
+ * Obfuscates a project.
163
+ * @param {Object} options - Configuration.
164
+ * @param {string} options.code - Code of entry point file to be obfuscated.
165
+ * @param {Object.<string, string>} options.modulesCode - Code of all of options.code's depedencies.
166
+ * @param {boolean} [options.babel = true] - Whether to run babel with ES2015 preset before obfuscating.
167
+ * @param {Object.<string, boolean>} [options.features = All enabled] - Feature configuration.
168
+ * @param {logAdapterCallback} [options.logAdapter = Console] - Logging adapter.
169
+ * @param {string} [options.logLevel = "warn"] - Minimum level of shown log messages.
170
+ * @param {Object.<string, boolean>} [options.preprocessorVariables] - Preprocessor variables.
171
+ * @example
172
+ * toildefender.do({
173
+ * code: "...",
174
+ * modulesCode: {
175
+ * depA: "...",
176
+ * depB: "..."
177
+ * },
178
+ * features: {
179
+ * scope: true,
180
+ * control_flow: true,
181
+ * identifiers: true,
182
+ * literals: true,
183
+ * mangle: true,
184
+ * compress: true
185
+ * }
186
+ * });
187
+ */
188
+ exports.do = function (options) {
189
+ /**
190
+ * Annotates potentially thrown errors with a label
191
+ */
192
+ function tryTag(label, task) {
193
+ try {
194
+ return task();
195
+ } catch (e) {
196
+ throw new Error(`[${label}]\t${e.stack}`);
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Adapter for Logger
202
+ */
203
+ function createConsoleLoggingAdapter(logLevel) {
204
+ const LEVELS = ["log", "error", "warn", "info", "debug"];
205
+ let allowedLevels = [];
206
+ for (let level of LEVELS) {
207
+ allowedLevels.push(level);
208
+ if (level == logLevel) {
209
+ break;
210
+ }
211
+ }
212
+ return (level, data) => {
213
+ if (_.includes(allowedLevels, level)) {
214
+ var prefix = "[task]" + Array(taskIndent).join("\t");
215
+ console.log(`${prefix}[${level}]\t${data.join("\t")}`);
216
+ }
217
+ };
218
+ }
219
+
220
+ var taskIndent = 1;
221
+ /**
222
+ * Wraps a task, indents its output and measures its duration
223
+ */
224
+ function doTask(label, condition, task) {
225
+ return tryTag(label, () => {
226
+ taskIndent++;
227
+ var prefix = "[task]" + Array(taskIndent).join("\t");
228
+ try {
229
+ if (condition) {
230
+ logger.info(`${prefix}${label} ...`);
231
+
232
+ var start = Date.now();
233
+ task();
234
+ var duration = Date.now() - start;
235
+ logger.info(`${prefix}${label}: ${duration}ms`);
236
+ return {
237
+ otherwise: function() { }
238
+ };
239
+ } else {
240
+ return {
241
+ otherwise: function (task) { task(); }
242
+ };
243
+ }
244
+ } finally {
245
+ taskIndent--;
246
+ }
247
+ });
248
+ }
249
+
250
+ function transformModernSyntax(code, label) {
251
+ if (modernBabel) {
252
+ var result = modernBabel.transformSync(code, {
253
+ babelrc: false,
254
+ comments: false,
255
+ compact: false,
256
+ configFile: false,
257
+ sourceType: "unambiguous",
258
+ presets: [
259
+ [
260
+ require.resolve("@babel/preset-env"),
261
+ {
262
+ modules: "commonjs",
263
+ targets: options.babelTarget,
264
+ useBuiltIns: false
265
+ }
266
+ ]
267
+ ]
268
+ });
269
+ return result && result.code || code;
270
+ }
271
+
272
+ var babelOptions = {
273
+ "plugins": [
274
+ "babel-plugin-transform-es2015-arrow-functions",
275
+ //"babel-plugin-transform-es2015-block-scoped-functions",
276
+ "babel-plugin-transform-es2015-block-scoping",
277
+ "babel-plugin-transform-es2015-classes",
278
+ "babel-plugin-transform-es2015-computed-properties",
279
+ //"babel-plugin-check-es2015-constants",
280
+ "babel-plugin-transform-es2015-destructuring",
281
+ "babel-plugin-transform-es2015-duplicate-keys",
282
+ "babel-plugin-transform-es2015-for-of",
283
+ "babel-plugin-transform-es2015-function-name",
284
+ "babel-plugin-transform-es2015-literals",
285
+ "babel-plugin-transform-es2015-object-super",
286
+ "babel-plugin-transform-es2015-parameters",
287
+ "babel-plugin-transform-es2015-shorthand-properties",
288
+ "babel-plugin-transform-es2015-spread",
289
+ "babel-plugin-transform-es2015-sticky-regex",
290
+ "babel-plugin-transform-es2015-template-literals",
291
+ //"babel-plugin-transform-es2015-typeof-symbol",
292
+ "babel-plugin-transform-es2015-unicode-regex"
293
+ ].map(require.resolve)
294
+ };
295
+ return legacyBabel.transform(code, babelOptions).code;
296
+ }
297
+
298
+ options = _.merge({}, defaultOptions, options); // first argument gets mutated
299
+ if (options.protections.virtualMachine.enabled) {
300
+ options.numericVm = _.merge({}, options.numericVm, options.protections.virtualMachine, {
301
+ enabled: true
302
+ });
303
+ options.features.numeric_vm = true;
304
+ }
305
+ if (options.protections.hashMesh.enabled) {
306
+ options.numericVm = _.merge({}, options.numericVm, options.protections.virtualMachine, {
307
+ enabled: true,
308
+ hashMesh: options.protections.hashMesh
309
+ });
310
+ options.features.numeric_vm = true;
311
+ }
312
+ if (!options.logAdapter) {
313
+ options.logAdapter = createConsoleLoggingAdapter(options.logLevel);
314
+ }
315
+ if (!options.forceFeatures) {
316
+ _.map(featureDeps, (deps, feature) => {
317
+ if (options.features[feature]) {
318
+ deps.forEach(dep => options.features[dep] = true);
319
+ }
320
+ });
321
+ } else {
322
+ options.features = options.forceFeatures;
323
+ }
324
+
325
+ var parseOptions = {};
326
+ var scopeOptions = {
327
+ optimistic: true // required or things in the global scope just get lost
328
+ };
329
+
330
+ var logger = new Logger(options.logAdapter);
331
+
332
+ var start = Date.now();
333
+
334
+ // Preprocess
335
+ doTask("preprocessing", true, () => {
336
+ var preprocessor = new prPreprocessing(logger);
337
+ options.modulesCode = _.mapValues(
338
+ options.modulesCode,
339
+ (code, key) => tryTag(key, () => preprocessor.process(code, options.preprocessorVariables))
340
+ );
341
+ options.code = tryTag("app", () => preprocessor.process(options.code, options.preprocessorVariables));
342
+ });
343
+
344
+ // Apply babel
345
+ doTask("babel", options.babel, () => {
346
+ options.modulesCode = _.mapValues(options.modulesCode, (moduleCode, key) => tryTag(key, () => transformModernSyntax(moduleCode, key)));
347
+ options.code = tryTag("app", () => transformModernSyntax(options.code, "app"));
348
+ });
349
+
350
+ // Parse code
351
+ var ast, modulesAST;
352
+ doTask("parse", true, () => {
353
+ modulesAST = _.mapValues(options.modulesCode, (code, key) => tryTag(key, () => esprima.parse(code, parseOptions)));
354
+ modulesAST.app = tryTag("app", () => esprima.parse(options.code, parseOptions));
355
+ });
356
+
357
+ // Merge depedencies into main modules
358
+ doTask("merge", true, () => {
359
+ var modules = new prModules(logger);
360
+ ast = modules.merge(modulesAST, "app");
361
+ });
362
+
363
+ // Insert dead code
364
+ doTask("dead_code", options.features.dead_code, () => {
365
+ var deadCode = new prDeadCode();
366
+ ast = deadCode.insert(ast, 1.0);
367
+ });
368
+
369
+ // Simplify graph
370
+ doTask("simplify", true, () => {
371
+ var normalizer = new prNormalizer(logger);
372
+ ast = normalizer.simplify(ast);
373
+ });
374
+
375
+ doTask("numeric_vm", options.features.numeric_vm || options.numericVm.enabled, () => {
376
+ var numericVm = new prNumericVm(logger, _.merge({}, options.numericVm, {
377
+ enabled: options.features.numeric_vm || options.numericVm.enabled
378
+ }));
379
+ ast = numericVm.apply(ast);
380
+ });
381
+
382
+ // Move identifiers
383
+ doTask("identifiers", options.features.identifiers, () => {
384
+ var identifiers = new prIdentifiers(logger);
385
+
386
+ ast = identifiers.computeProperties(ast);
387
+ ast = identifiers.arrayizeObjects(ast, {
388
+ objectPacking: options.features.object_packing !== false
389
+ });
390
+ //ast = identifiers.moveIdentifiers(ast, escope.analyze(ast, scopeOptions));
391
+ //^ why is this commented out?
392
+ ast = identifiers.moveLiterals(ast, escope.analyze(ast, scopeOptions));
393
+ });
394
+
395
+ doTask("literals", options.features.literals, () => {
396
+ var literals = new prLiterals(logger);
397
+
398
+ literals.generateStrings(ast);
399
+ });
400
+
401
+ doTask("scope", options.features.scope, () => {
402
+ var scopes = new prScopes(logger);
403
+ var methods = new prMethods(logger);
404
+
405
+ var rng = new utils.UniqueRandom(32768);
406
+
407
+ // Make identifiers unique
408
+ doTask("obfuscate_identifiers", true, () => {
409
+ var variables = new prVariables(logger);
410
+ variables.removeFunctionExpressionIds(ast);
411
+ variables.functionDeclarationToExpression(ast, escope.analyze(ast, scopeOptions));
412
+ variables.obfuscateIdentifiers(ast, escope.analyze(ast, scopeOptions));
413
+ variables.redefineParameters(ast, escope.analyze(ast, scopeOptions));
414
+ });
415
+
416
+ // Move identifiers into scope objects
417
+ doTask("create_scope_objects", true, () => {
418
+ scopes.createScopeObjects(ast, escope.analyze(ast, scopeOptions));
419
+ });
420
+
421
+ // Calculate entry points for all methods
422
+ var methodEntryPoints = {};
423
+ doTask("list_methods", true, () => {
424
+ methods.listMethods(ast).forEach(methodName => {
425
+ methodEntryPoints[methodName] = {
426
+ entry: rng.get()
427
+ };
428
+ });
429
+ });
430
+
431
+ // Extract function declarations and expressions
432
+ var fns;
433
+ doTask("extract_methods", true, () => {
434
+ var scopeManager = escope.analyze(ast, scopeOptions);
435
+ fns = methods.extractMethods(ast);
436
+ fns = fns.map(method => {
437
+ var refers = methods.methodRefersToArguments(method, scopeManager);
438
+ methods.removeFirstArguments(method, refers ? method.params.filter(x => x.name.indexOf("$$scope") == 0).length : 0);
439
+ return methods.replaceArgumentReferences(method, true);
440
+ });
441
+ if (options.features.control_flow) {
442
+ methods.replaceFunctionCalls(ast, methodEntryPoints);
443
+ fns.forEach(method => {
444
+ methods.replaceFunctionCalls(method.body, methodEntryPoints);
445
+ });
446
+ }
447
+ });
448
+
449
+ doTask("add_custom_bind", true, () => {
450
+ methods.addCustomBind(ast);
451
+ });
452
+
453
+ doTask("control_flow", options.features.control_flow, () => {
454
+ // Apply control flow flattening and merge methods
455
+ var flattener = new prFlattener(logger, rng);
456
+ var entry = rng.get(), exit = rng.get();
457
+ flattener.addMethod(ast, entry, exit);
458
+ fns.forEach(method => {
459
+ methods.bumpArgumentsIndices(method, 1);
460
+
461
+ var entry = methodEntryPoints[method.id.name].entry;
462
+ flattener.addMethod(method.body, entry, exit);
463
+ });
464
+
465
+ ast = flattener.getProgram(entry, exit);
466
+
467
+ ast = flattener.unifyPrefixStatements(ast);
468
+ })
469
+ .otherwise(() => {
470
+ if (ast.type == "Program") {
471
+ ast.type = "BlockStatement";
472
+ }
473
+ ast = {
474
+ type: "Program",
475
+ body: fns.concat([ ast ])
476
+ };
477
+ });
478
+ });
479
+
480
+ // Postprocessing
481
+ doTask("postprocessing", true, () => {
482
+ var postprocessing = new prPostprocessing(logger);
483
+ ast = postprocessing.do(ast);
484
+ });
485
+
486
+ doTask("health", options.features.health, () => {
487
+ var health = new prHealth(logger);
488
+ ast = health.check(ast);
489
+ });
490
+
491
+ doTask("mangle", options.features.mangle, () => {
492
+ var uglifier = new prUglifier(logger);
493
+ if (ast.type == "Program") {
494
+ ast.type = "BlockStatement";
495
+ }
496
+ ast = uglifier.uglify({
497
+ type: "Program",
498
+ body: [
499
+ {
500
+ type: "CallExpression",
501
+ arguments: [],
502
+ callee: {
503
+ type: "FunctionExpression",
504
+ params: [],
505
+ body: ast
506
+ }
507
+ }
508
+ ]
509
+ });
510
+ });
511
+
512
+ var codegenOptions = {
513
+ sourceMap: false,
514
+ sourceMapWithCode: false
515
+ };
516
+
517
+ doTask("compress", options.features.compress, () => {
518
+ codegenOptions.format = {
519
+ renumber: true,
520
+ hexadecimal: true,
521
+ quotes: "auto",
522
+ compact: true
523
+ };
524
+ });
525
+
526
+ var result = escodegen.generate(ast, codegenOptions);
527
+
528
+ var duration = Date.now() - start;
529
+
530
+ return {
531
+ code: result.code || result,
532
+ map: result.map && result.map.toString()
533
+ };
534
+ };
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "@dacely/toildefender",
3
+ "version": "0.1.0",
4
+ "description": "Modern JavaScript code protection, bytecode virtualization, and obfuscation for the Toil tech stack.",
5
+ "author": "Dacely",
6
+ "contributors": [
7
+ {
8
+ "name": "Alexander Horn",
9
+ "url": "https://github.com/alexhorn"
10
+ }
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/dacely-cloud/toildefender.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/dacely-cloud/toildefender/issues"
18
+ },
19
+ "homepage": "https://github.com/dacely-cloud/toildefender#readme",
20
+ "keywords": [
21
+ "javascript",
22
+ "obfuscation",
23
+ "bytecode",
24
+ "virtual-machine",
25
+ "code-protection",
26
+ "toiljs",
27
+ "dacely"
28
+ ],
29
+ "main": "toildefender.js",
30
+ "exports": {
31
+ ".": "./toildefender.js"
32
+ },
33
+ "bin": {
34
+ "toildefender": "toildefender.js"
35
+ },
36
+ "scripts": {
37
+ "test": "node --test test/*.test.js",
38
+ "test:firefox": "node test/firefox-smoke.mjs",
39
+ "pack:dry": "npm pack --dry-run"
40
+ },
41
+ "toildefender": {
42
+ "mainFiles": [
43
+ "toildefender.js"
44
+ ]
45
+ },
46
+ "files": [
47
+ "cli.js",
48
+ "defendjs.js",
49
+ "estest.js",
50
+ "esutils.js",
51
+ "logger.js",
52
+ "obfuscator.js",
53
+ "processors",
54
+ "toildefender.js",
55
+ "traverser.js",
56
+ "utils.js",
57
+ "LICENSE",
58
+ "NOTICE.md",
59
+ "README.md",
60
+ "images/toildefender-logo.svg",
61
+ "docs/all-modes-output.demo.js"
62
+ ],
63
+ "dependencies": {
64
+ "@babel/core": "^8.0.1",
65
+ "@babel/preset-env": "^8.0.2",
66
+ "babel-core": "6.10.4",
67
+ "babel-plugin-check-es2015-constants": "^6.3.13",
68
+ "babel-plugin-transform-es2015-arrow-functions": "^6.3.13",
69
+ "babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13",
70
+ "babel-plugin-transform-es2015-block-scoping": "^6.9.0",
71
+ "babel-plugin-transform-es2015-classes": "^6.9.0",
72
+ "babel-plugin-transform-es2015-computed-properties": "^6.3.13",
73
+ "babel-plugin-transform-es2015-destructuring": "^6.9.0",
74
+ "babel-plugin-transform-es2015-duplicate-keys": "^6.6.0",
75
+ "babel-plugin-transform-es2015-for-of": "^6.6.0",
76
+ "babel-plugin-transform-es2015-function-name": "^6.9.0",
77
+ "babel-plugin-transform-es2015-literals": "^6.3.13",
78
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.6.0",
79
+ "babel-plugin-transform-es2015-object-super": "^6.3.13",
80
+ "babel-plugin-transform-es2015-parameters": "^6.9.0",
81
+ "babel-plugin-transform-es2015-shorthand-properties": "^6.3.13",
82
+ "babel-plugin-transform-es2015-spread": "^6.3.13",
83
+ "babel-plugin-transform-es2015-sticky-regex": "^6.3.13",
84
+ "babel-plugin-transform-es2015-template-literals": "^6.6.0",
85
+ "babel-plugin-transform-es2015-typeof-symbol": "^6.6.0",
86
+ "babel-plugin-transform-es2015-unicode-regex": "^6.3.13",
87
+ "babel-plugin-transform-regenerator": "^6.9.0",
88
+ "escodegen": "^2.1.0",
89
+ "escope": "3.6.0",
90
+ "esprima": "^4.0.1",
91
+ "esshorten": "1.1.1",
92
+ "estraverse": "4.2.0",
93
+ "expr-eval": "1.0.0",
94
+ "lodash": "4.13.1",
95
+ "minimist": "1.2.0"
96
+ },
97
+ "engines": {
98
+ "node": ">=24.0.0",
99
+ "npm": ">=10.0.0"
100
+ },
101
+ "publishConfig": {
102
+ "access": "public"
103
+ },
104
+ "license": "AGPL-3.0",
105
+ "devDependencies": {
106
+ "playwright": "^1.61.0"
107
+ }
108
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ var assert = require("assert");
4
+
5
+ var _ = require("lodash");
6
+ var estest = require("../estest");
7
+ var traverser = require("../traverser");
8
+ var utils = require("../utils");
9
+
10
+ const KEYWORDS = ["await","break","case","catch","class","const","continue","debugger","default","delete","do","else","enum","export","extends","finally","for","function","if","implements","import","in","instanceof","interface","let","new","package","private","protected","public","return","static","super","switch","this","throw","try","typeof","var","void","while","with","yield"];
11
+
12
+ module.exports = class DeadCode {
13
+
14
+ constructor (logger) {
15
+ this.logger = logger;
16
+ }
17
+
18
+ /**
19
+ * Insert dead code
20
+ * @param {Node} ast
21
+ * @returns {Node}
22
+ */
23
+ insert (ast, probability) {
24
+ assert.ok(estest.isNode(ast));
25
+
26
+ var rngAlpha = new utils.UniqueRandomAlpha(3);
27
+
28
+ return traverser.traverse(ast, [], (node, stack) => {
29
+ if (node.type == "BlockStatement") {
30
+ for (var i = 0; i < probability; ++i) {
31
+ if (probability - i < Math.random()) {
32
+ continue;
33
+ }
34
+
35
+ var pos = utils.random(0, node.body.length - 1);
36
+ var len = utils.random(1, node.body.length - pos);
37
+
38
+ var varValue = _.sample(KEYWORDS);
39
+
40
+ var spliced = node.body.splice(pos, len);
41
+ node.body.splice(pos, 0,
42
+ {
43
+ type: "IfStatement",
44
+ test: {
45
+ type: "BinaryExpression",
46
+ operator: "==",
47
+ left: { type: "Literal", value: varValue },
48
+ right: { type: "Literal", value: varValue }
49
+ },
50
+ consequent: {
51
+ type: "BlockStatement",
52
+ body: spliced
53
+ }
54
+ }
55
+ );
56
+ }
57
+ }
58
+ return node;
59
+ });
60
+ }
61
+
62
+ };