@dacely/toildefender 0.1.4 → 0.1.5

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.
@@ -213,6 +213,15 @@ function veilmark$numericVmRun(program, base, tokenCount, seed, tag, constants,
213
213
  return value;
214
214
  }
215
215
 
216
+ function readConstant(index) {
217
+ var cell = constants[index];
218
+ if (cell && cell[0] === 0 && typeof cell[1] === "function") {
219
+ cell[1] = cell[1]();
220
+ cell[0] = 1;
221
+ }
222
+ return cell && cell[0] === 1 ? cell[1] : cell;
223
+ }
224
+
216
225
  function read() {
217
226
  if (ip < 0 || ip >= tokenCount) throw new Error("invalid virtual opcode");
218
227
  var value = decodeAt(ip);
@@ -254,7 +263,7 @@ function veilmark$numericVmRun(program, base, tokenCount, seed, tag, constants,
254
263
  if (op === ops[3]) { push(true); continue; }
255
264
  if (op === ops[4]) { push(false); continue; }
256
265
  if (op === ops[5]) { push(readUnsigned()); continue; }
257
- if (op === ops[6]) { push(constants[readUnsigned()]); continue; }
266
+ if (op === ops[6]) { push(readConstant(readUnsigned())); continue; }
258
267
  if (op === ops[7]) { push(frameArgs[readUnsigned()]); continue; }
259
268
  if (op === ops[8]) { push(loadLocal(readUnsigned())); continue; }
260
269
  if (op === ops[9]) { storeLocal(readUnsigned(), pop()); continue; }
@@ -281,7 +290,7 @@ function veilmark$numericVmRun(program, base, tokenCount, seed, tag, constants,
281
290
  if (op === ops[30]) { var jf = readSigned(); if (!pop()) ip += jf; continue; }
282
291
  if (op === ops[31]) { var jt = readSigned(); if (pop()) ip += jt; continue; }
283
292
  if (op === ops[32]) { readUnsigned(); var argc = readUnsigned(); var ca = popArgs(argc); var fn = pop(); push(fn.apply(undefined, ca)); continue; }
284
- if (op === ops[33]) { readUnsigned(); var largc = readUnsigned(); var la = popArgs(largc); var lfn = constants[readUnsigned()]; push(lfn.apply(undefined, la)); continue; }
293
+ if (op === ops[33]) { readUnsigned(); var largc = readUnsigned(); var la = popArgs(largc); var lfn = readConstant(readUnsigned()); push(lfn.apply(undefined, la)); continue; }
285
294
  if (op === ops[34]) { var gpKey = pop(); var gpObj = pop(); push(gpObj[gpKey]); continue; }
286
295
  if (op === ops[35]) { var spValue = pop(); var spKey = pop(); var spObj = pop(); spObj[spKey] = spValue; push(spValue); continue; }
287
296
  if (op === ops[36]) { var ac = readUnsigned(); var arr = new Array(ac); var ai = ac; while (ai > 0) { ai -= 1; arr[ai] = pop(); } push(arr); continue; }
@@ -292,6 +301,8 @@ function veilmark$numericVmRun(program, base, tokenCount, seed, tag, constants,
292
301
  if (op === ops[41]) { push(argsLike); continue; }
293
302
  if (op === ops[42]) { push(typeof pop()); continue; }
294
303
  if (op === ops[43]) { var mc = readUnsigned(); var ma = popArgs(mc); var mk = pop(); var mo = pop(); push(mo[mk].apply(mo, ma)); continue; }
304
+ if (op === ops[44]) { var cgpKey = readConstant(readUnsigned()); var cgpObj = pop(); push(cgpObj[cgpKey]); continue; }
305
+ if (op === ops[45]) { storeLocal(readUnsigned(), pop()); continue; }
295
306
  throw new Error("invalid virtual opcode");
296
307
  }
297
308
  }
@@ -304,7 +315,7 @@ var OP_NAMES = [
304
315
  "STRICT_EQ", "STRICT_NEQ", "LT", "LTE", "GT", "GTE", "JMP", "JMP_FALSE",
305
316
  "JMP_TRUE", "CALL_EXT", "CALL_LOCAL", "GET_PROP", "SET_PROP", "MAKE_ARRAY",
306
317
  "MAKE_OBJECT", "RETURN", "THROW", "PUSH_THIS", "PUSH_ARGUMENTS", "TYPEOF",
307
- "CALL_METHOD"
318
+ "CALL_METHOD", "GET_CONST_PROP", "STORE_LOCAL_POP"
308
319
  ];
309
320
 
310
321
  var BASES = [257, 263, 269, 521, 1031, 4099, 65537];
@@ -318,6 +329,7 @@ function unary(operator, argument) { return { type: "UnaryExpression", operator:
318
329
  function member(object, property) { return { type: "MemberExpression", object: object, property: property, computed: true }; }
319
330
  function arrayExpression(values) { return { type: "ArrayExpression", elements: values }; }
320
331
  function returnStatement(argument) { return { type: "ReturnStatement", argument: argument }; }
332
+ function functionExpression(body) { return { type: "FunctionExpression", id: null, params: [], body: { type: "BlockStatement", body: body }, generator: false, expression: false, async: false }; }
321
333
  function functionName(node) { return node.id && node.id.name ? node.id.name : ""; }
322
334
 
323
335
  function hashSeed(seed) {
@@ -833,6 +845,29 @@ Compiler.prototype.instructionSize = function (instr, positions) {
833
845
  }
834
846
  return size;
835
847
  };
848
+ Compiler.prototype.isInstruction = function (instr, op) {
849
+ return instr && !instr.label && instr.op === op;
850
+ };
851
+ Compiler.prototype.fuseSuperinstructions = function () {
852
+ var out = [];
853
+ for (var i = 0; i < this.instructions.length; i += 1) {
854
+ var one = this.instructions[i];
855
+ var two = this.instructions[i + 1];
856
+ var three = this.instructions[i + 2];
857
+ if (this.isInstruction(one, "PUSH_CONST") && this.isInstruction(two, "GET_PROP")) {
858
+ out.push({ op: "GET_CONST_PROP", args: [one.args[0]] });
859
+ i += 1;
860
+ continue;
861
+ }
862
+ if (this.isInstruction(one, "DUP") && this.isInstruction(two, "STORE_LOCAL") && this.isInstruction(three, "POP")) {
863
+ out.push({ op: "STORE_LOCAL_POP", args: [two.args[0]] });
864
+ i += 2;
865
+ continue;
866
+ }
867
+ out.push(one);
868
+ }
869
+ this.instructions = out;
870
+ };
836
871
  Compiler.prototype.assemble = function () {
837
872
  var positions = new Map();
838
873
  var stable = false;
@@ -896,14 +931,21 @@ Compiler.prototype.constantExpression = function (constant) {
896
931
  if (constant.kind === "undefined") return { type: "UnaryExpression", operator: "void", prefix: true, argument: literal(0) };
897
932
  throw new Error("unsupported constant " + constant.kind);
898
933
  };
934
+ Compiler.prototype.constantCellExpression = function (constant) {
935
+ return arrayExpression([
936
+ literal(0),
937
+ functionExpression([ returnStatement(this.constantExpression(constant)) ])
938
+ ]);
939
+ };
899
940
  Compiler.prototype.finish = function () {
941
+ this.fuseSuperinstructions();
900
942
  var tokens = this.assemble();
901
943
  var encrypted = encryptedStream(tokens, this.dialect.base, this.dialect.seed);
902
944
  var opValues = OP_NAMES.map(name => this.dialect.opcodes[name]);
903
945
  var record = {
904
946
  base: this.dialect.base,
905
947
  blob: packTokens(encrypted.encrypted, this.dialect.base),
906
- constants: this.constants.map(this.constantExpression.bind(this)),
948
+ constants: this.constants.map(this.constantCellExpression.bind(this)),
907
949
  opValues: opValues.map(literal),
908
950
  seed: this.dialect.seed,
909
951
  tag: encrypted.tag,
@@ -9,6 +9,21 @@ var ESUtils = require("../esutils");
9
9
  var traverser = require("../traverser");
10
10
  var utils = require("../utils");
11
11
 
12
+ function isClassMethodFunction(stack) {
13
+ return stack.some(frame => frame.node.type == "MethodDefinition" || frame.node.type == "ClassBody");
14
+ }
15
+
16
+ function isClassMethodScope(scope) {
17
+ var node = scope && scope.block;
18
+ while (node) {
19
+ if (node.type == "MethodDefinition" || node.type == "ClassBody") {
20
+ return true;
21
+ }
22
+ node = node.veilmark$parent;
23
+ }
24
+ return false;
25
+ }
26
+
12
27
  module.exports = class Scopes {
13
28
 
14
29
  constructor (logger) {
@@ -33,6 +48,9 @@ module.exports = class Scopes {
33
48
  var scopes = scopeManager.acquireAll(ast);
34
49
  var rngAlpha = new utils.UniqueRandomAlpha(3);
35
50
  scopeManager.scopes.forEach(scope => {
51
+ if (!this.esutils.canInsertIntoScope(scope) || isClassMethodScope(scope)) {
52
+ return;
53
+ }
36
54
  var scopeVarName = `$$scope$${rngAlpha.get()}`;
37
55
 
38
56
  var counter = 0;
@@ -132,6 +150,9 @@ module.exports = class Scopes {
132
150
  });
133
151
  });
134
152
  } else if (def.type == "FunctionName") {
153
+ if (def.node.type == "FunctionExpression") {
154
+ return;
155
+ }
135
156
  variable.references.forEach(reference => {
136
157
  this.esutils.replaceNode(scope.block, reference.identifier, {
137
158
  type: "CallExpression",
@@ -150,6 +171,10 @@ module.exports = class Scopes {
150
171
  if (scope.block == node) {
151
172
  return node;
152
173
  }
174
+
175
+ if (isClassMethodFunction(stack)) {
176
+ return node;
177
+ }
153
178
 
154
179
  if (node.type.indexOf("Function") == 0) {
155
180
  node.params.unshift({
@@ -9,6 +9,74 @@ var ESUtils = require("../esutils");
9
9
  var traverser = require("../traverser");
10
10
  var utils = require("../utils");
11
11
 
12
+ function isReferenceIdentifier(node, stack) {
13
+ var parentFrame = stack[1];
14
+ if (!parentFrame) {
15
+ return true;
16
+ }
17
+
18
+ var parent = parentFrame.node;
19
+ var key = parentFrame.key;
20
+
21
+ if ((parent.type == "FunctionDeclaration" || parent.type == "FunctionExpression") && (key == "id" || key == "params")) {
22
+ return false;
23
+ }
24
+ if ((parent.type == "ClassDeclaration" || parent.type == "ClassExpression") && key == "id") {
25
+ return false;
26
+ }
27
+ if (parent.type == "VariableDeclarator" && key == "id") {
28
+ return false;
29
+ }
30
+ if (parent.type == "CatchClause" && key == "param") {
31
+ return false;
32
+ }
33
+ if ((parent.type == "MemberExpression" || parent.type == "Property") && key == "property" && parent.computed === false) {
34
+ return false;
35
+ }
36
+ if (parent.type == "Property" && key == "key" && parent.computed === false) {
37
+ return false;
38
+ }
39
+ if ((parent.type == "MethodDefinition" || parent.type == "PropertyDefinition" || parent.type == "FieldDefinition") && key == "key" && parent.computed === false) {
40
+ return false;
41
+ }
42
+ if ((parent.type == "LabeledStatement" || parent.type == "BreakStatement" || parent.type == "ContinueStatement") && key == "label") {
43
+ return false;
44
+ }
45
+
46
+ return true;
47
+ }
48
+
49
+ function functionExpressionUsesOwnName(node) {
50
+ assert.equal(node.type, "FunctionExpression");
51
+
52
+ if (!node.id) {
53
+ return false;
54
+ }
55
+
56
+ var name = node.id.name;
57
+ var used = false;
58
+
59
+ traverser.traverse(node.body, [], (child, stack) => {
60
+ if (child.type == "Identifier" && child.name == name && isReferenceIdentifier(child, stack)) {
61
+ used = true;
62
+ }
63
+ return child;
64
+ });
65
+
66
+ return used;
67
+ }
68
+
69
+ function isClassMethodScope(scope) {
70
+ var node = scope && scope.block;
71
+ while (node) {
72
+ if (node.type == "MethodDefinition" || node.type == "ClassBody") {
73
+ return true;
74
+ }
75
+ node = node.veilmark$parent;
76
+ }
77
+ return false;
78
+ }
79
+
12
80
  module.exports = class Variables {
13
81
 
14
82
  constructor (logger) {
@@ -25,7 +93,7 @@ module.exports = class Variables {
25
93
  */
26
94
  removeFunctionExpressionIds (ast) {
27
95
  return traverser.traverse(ast, [], (node, stack) => {
28
- if (node.type == "FunctionExpression" && node.id) {
96
+ if (node.type == "FunctionExpression" && node.id && !functionExpressionUsesOwnName(node)) {
29
97
  node.id = null;
30
98
  }
31
99
  return node;
@@ -46,6 +114,9 @@ module.exports = class Variables {
46
114
  this.esutils.setParentsRecursive(ast);
47
115
 
48
116
  scopeManager.scopes.forEach(scope => {
117
+ if (!this.esutils.canInsertIntoScope(scope) || isClassMethodScope(scope)) {
118
+ return;
119
+ }
49
120
  scope.variables.forEach(variable => {
50
121
  variable.defs.forEach(def => {
51
122
  if (def.type == "FunctionName") {
@@ -68,7 +139,10 @@ module.exports = class Variables {
68
139
  init: {
69
140
  type: "FunctionExpression",
70
141
  params: def.node.params,
71
- body: def.node.body
142
+ body: def.node.body,
143
+ generator: def.node.generator === true,
144
+ expression: def.node.expression === true,
145
+ async: def.node.async === true
72
146
  }
73
147
  }
74
148
  ]
@@ -90,6 +164,9 @@ module.exports = class Variables {
90
164
  */
91
165
  obfuscateIdentifiers (ast, scopeManager) {
92
166
  scopeManager.scopes.forEach(scope => {
167
+ if (isClassMethodScope(scope)) {
168
+ return;
169
+ }
93
170
  if (scope.isStatic()) {
94
171
  scope.variables.sort((a, b) => {
95
172
  if (a.tainted) {
@@ -104,6 +181,10 @@ module.exports = class Variables {
104
181
  for (let variable of scope.variables) {
105
182
  var name = "$$var$" + utils.hash(variable) + "$" + variable.name;
106
183
 
184
+ if (variable.defs.some(def => def.type == "ClassName")) {
185
+ continue;
186
+ }
187
+
107
188
  if (variable.tainted) {
108
189
  continue;
109
190
  }
@@ -155,6 +236,9 @@ module.exports = class Variables {
155
236
  var rng = new utils.UniqueRandomAlpha(3);
156
237
 
157
238
  scopeManager.scopes.forEach(scope => {
239
+ if (!this.esutils.canInsertIntoScope(scope) || isClassMethodScope(scope)) {
240
+ return;
241
+ }
158
242
  scope.variables.forEach(variable => {
159
243
  variable.defs.forEach(def => {
160
244
  if (def.type == "Parameter") {
package/traverser.js CHANGED
@@ -7,6 +7,12 @@ var estraverse = require("estraverse");
7
7
  var estest = require("./estest");
8
8
  var utils = require("./utils");
9
9
 
10
+ var VISITOR_KEYS = Object.assign({}, estraverse.VisitorKeys, {
11
+ ChainExpression: [ "expression" ],
12
+ PropertyDefinition: [ "key", "value" ],
13
+ FieldDefinition: [ "key", "value" ]
14
+ });
15
+
10
16
  // Depth-first
11
17
  exports.traverse = function (node, stack, processor) {
12
18
  assert.ok(estest.isNode(node));
@@ -57,7 +63,7 @@ exports.visitChildren = function (node, processor) {
57
63
  assert.ok(estest.isNode(node));
58
64
  assert.equal(typeof processor, "function");
59
65
 
60
- var keys = estraverse.VisitorKeys[node.type] || [];
66
+ var keys = VISITOR_KEYS[node.type] || [];
61
67
  keys.forEach(key => {
62
68
  if (Array.isArray(node[key])) {
63
69
  node[key] = node[key].map(x => {
@@ -80,7 +86,7 @@ exports.visitChildrenEx = function (node, processor) {
80
86
  assert.ok(estest.isNode(node));
81
87
  assert.equal(typeof processor, "function");
82
88
 
83
- var keys = estraverse.VisitorKeys[node.type] || [];
89
+ var keys = VISITOR_KEYS[node.type] || [];
84
90
  keys.forEach(key => {
85
91
  if (Array.isArray(node[key])) {
86
92
  let i = node[key].length;