فهرست منبع

Correct obfuscation of object rest and spread properties

sanex3339 7 سال پیش
والد
کامیت
afe33db8fb
19فایلهای تغییر یافته به همراه242 افزوده شده و 58 حذف شده
  1. 4 0
      CHANGELOG.md
  2. 0 0
      dist/index.js
  3. 6 6
      package.json
  4. 1 1
      src/JavaScriptObfuscator.ts
  5. 21 8
      src/analyzers/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts
  6. 5 1
      src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts
  7. 1 1
      src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts
  8. 5 5
      src/node-transformers/obfuscating-transformers/FunctionTransformer.ts
  9. 4 4
      src/node-transformers/obfuscating-transformers/VariableDeclarationTransformer.ts
  10. 4 5
      test/dev/dev.ts
  11. 59 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/ObjectExpressionTransformer.spec.ts
  12. 8 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/fixtures/object-rest.js
  13. 5 0
      test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/fixtures/object-spread.js
  14. 54 0
      test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/FunctionTransformer.spec.ts
  15. 3 0
      test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/object-rest-parameter.js
  16. 3 0
      test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/rest-parameter.js
  17. 27 0
      test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec.ts
  18. 8 0
      test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/fixtures/object-rest.js
  19. 24 27
      yarn.lock

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 ===
+v0.16.0
+---
+* Correct obfuscation of object rest and spread properties
+
 v0.15.0
 ---
 * **Internal change:** switched AST parser from `esprima` on `espree`

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/index.js


+ 6 - 6
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.15.0",
+  "version": "0.16.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -25,7 +25,7 @@
     "class-validator": "0.8.5",
     "commander": "2.15.1",
     "escodegen-wallaby": "1.6.18",
-    "espree": "3.5.4",
+    "espree": "4.0.0-alpha.0",
     "estraverse": "4.2.0",
     "inversify": "4.11.1",
     "js-string-escape": "1.0.1",
@@ -53,17 +53,17 @@
     "@types/mkdirp": "0.5.2",
     "@types/mocha": "5.0.0",
     "@types/multimatch": "2.1.2",
-    "@types/node": "9.6.2",
+    "@types/node": "9.6.5",
     "@types/rimraf": "2.0.2",
     "@types/sinon": "4.3.1",
     "@types/string-template": "1.0.2",
-    "@types/webpack-env": "1.13.5",
-    "awesome-typescript-loader": "5.0.0-0",
+    "@types/webpack-env": "1.13.6",
+    "awesome-typescript-loader": "5.0.0",
     "babel-loader": "8.0.0-beta.2",
     "chai": "4.1.2",
     "coveralls": "3.0.0",
     "istanbul": "1.1.0-alpha.1",
-    "mocha": "5.0.5",
+    "mocha": "5.1.0",
     "pre-commit": "1.2.2",
     "rimraf": "2.6.2",
     "sinon": "4.5.0",

+ 1 - 1
src/JavaScriptObfuscator.ts

@@ -33,7 +33,7 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
         ecmaFeatures: {
             experimentalObjectRestSpread: true
         },
-        ecmaVersion: 8,
+        ecmaVersion: 9,
         loc: true,
         range: true
     };

+ 21 - 8
src/analyzers/stack-trace-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts

@@ -13,6 +13,26 @@ import { NodeUtils } from '../../../node/NodeUtils';
 
 @injectable()
 export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
+    /**
+     * @param {Property} propertyNode
+     * @param {string | number} nextItemInCallsChain
+     * @returns {boolean}
+     */
+    private static isValidTargetPropertyNode (propertyNode: ESTree.Property, nextItemInCallsChain: string | number): boolean {
+        if (!propertyNode.key) {
+            return false;
+        }
+
+        const isTargetPropertyNodeWithIdentifierKey: boolean =
+            NodeGuards.isIdentifierNode(propertyNode.key) && propertyNode.key.name === nextItemInCallsChain;
+        const isTargetPropertyNodeWithLiteralKey: boolean =
+            NodeGuards.isLiteralNode(propertyNode.key) &&
+            Boolean(propertyNode.key.value) &&
+            propertyNode.key.value === nextItemInCallsChain;
+
+        return isTargetPropertyNodeWithIdentifierKey || isTargetPropertyNodeWithLiteralKey;
+    }
+
     /**
      * @param {NodeGuards[]} blockScopeBody
      * @param {MemberExpression} callee
@@ -135,14 +155,7 @@ export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtra
         }
 
         for (const propertyNode of objectExpressionProperties) {
-            const isTargetPropertyNodeWithIdentifierKey: boolean =
-                NodeGuards.isIdentifierNode(propertyNode.key) && propertyNode.key.name === nextItemInCallsChain;
-            const isTargetPropertyNodeWithLiteralKey: boolean =
-                NodeGuards.isLiteralNode(propertyNode.key) &&
-                Boolean(propertyNode.key.value) &&
-                propertyNode.key.value === nextItemInCallsChain;
-
-            if (!isTargetPropertyNodeWithIdentifierKey && !isTargetPropertyNodeWithLiteralKey) {
+            if (!ObjectExpressionCalleeDataExtractor.isValidTargetPropertyNode(propertyNode, nextItemInCallsChain)) {
                 continue;
             }
 

+ 5 - 1
src/node-transformers/converting-transformers/ObjectExpressionKeysTransformer.ts

@@ -68,6 +68,10 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
      * @returns {string | null}
      */
     private static getPropertyNodeKeyName (propertyNode: ESTree.Property): string | null {
+        if (!propertyNode.key) {
+            return null;
+        }
+
         const propertyKeyNode: ESTree.Expression = propertyNode.key;
 
         if (NodeGuards.isLiteralNode(propertyKeyNode) && typeof propertyKeyNode.value === 'string') {
@@ -189,7 +193,7 @@ export class ObjectExpressionKeysTransformer extends AbstractNodeTransformer {
              * Stage 2: creating new expression statement node with member expression based on removed property
              */
             const shouldCreateLiteralNode: boolean = !property.computed
-                || (property.computed && NodeGuards.isLiteralNode(property.key));
+                || (property.computed && !!property.key && NodeGuards.isLiteralNode(property.key));
             const memberExpressionProperty: ESTree.Expression = shouldCreateLiteralNode
                 ? NodeFactory.literalNode(propertyKeyName)
                 : NodeFactory.identifierNode(propertyKeyName);

+ 1 - 1
src/node-transformers/converting-transformers/ObjectExpressionTransformer.ts

@@ -61,7 +61,7 @@ export class ObjectExpressionTransformer extends AbstractNodeTransformer {
     public transformNode (objectExpressionNode: ESTree.ObjectExpression, parentNode: ESTree.Node): ESTree.Node {
         objectExpressionNode.properties
             .forEach((property: ESTree.Property) => {
-                if (property.computed) {
+                if (property.computed || !property.key) {
                     return;
                 }
 

+ 5 - 5
src/node-transformers/obfuscating-transformers/FunctionTransformer.ts

@@ -98,12 +98,12 @@ export class FunctionTransformer extends AbstractNodeTransformer {
     private storeFunctionParams (functionNode: ESTree.Function, nodeIdentifier: number): void {
         functionNode.params
             .forEach((paramsNode: ESTree.Node) => {
-                if (NodeGuards.isObjectPatternNode(paramsNode)) {
-                    return estraverse.VisitorOption.Skip;
-                }
-
                 estraverse.traverse(paramsNode, {
                     enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+                        if (NodeGuards.isPropertyNode(paramsNode)) {
+                            return estraverse.VisitorOption.Skip;
+                        }
+
                         if (NodeGuards.isAssignmentPatternNode(node) && NodeGuards.isIdentifierNode(node.left)) {
                             this.identifierObfuscatingReplacer.storeLocalName(node.left.name, nodeIdentifier);
 
@@ -127,7 +127,7 @@ export class FunctionTransformer extends AbstractNodeTransformer {
         ignoredIdentifierNamesSet: Set<string>
     ): void {
         properties.forEach((property: ESTree.Property) => {
-            if (!NodeGuards.isIdentifierNode(property.key)) {
+            if (!property.key || !NodeGuards.isIdentifierNode(property.key)) {
                 return;
             }
 

+ 4 - 4
src/node-transformers/obfuscating-transformers/VariableDeclarationTransformer.ts

@@ -226,12 +226,12 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
     ): void {
         variableDeclarationNode.declarations
             .forEach((declarationNode: ESTree.VariableDeclarator) => {
-                if (NodeGuards.isObjectPatternNode(declarationNode.id)) {
-                    return estraverse.VisitorOption.Skip;
-                }
-
                 estraverse.traverse(declarationNode.id, {
                     enter: (node: ESTree.Node) => {
+                        if (NodeGuards.isPropertyNode(node)) {
+                            return estraverse.VisitorOption.Skip;
+                        }
+
                         if (NodeGuards.isIdentifierNode(node)) {
                             callback(node);
                         }

+ 4 - 5
test/dev/dev.ts

@@ -6,15 +6,14 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
 
     let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
         `
-        (function () {
-            let a = 0;
-            let b = 0;
-            var test = {a, b};
-        })();
+        function func ({foo, ...rest}) {
+            return foo + rest;
+        }
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
             compact: false,
+            transformObjectKeys: true
         }
     ).getObfuscatedCode();
 

+ 59 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/ObjectExpressionTransformer.spec.ts

@@ -74,4 +74,63 @@ describe('ObjectExpressionTransformer', () => {
             assert.match(obfuscatedCode, regExp);
         });
     });
+
+    describe('object rest', () => {
+        const objectRegExp: RegExp = /var *_0x[a-f0-9]{4,6} *= *\{'foo': *0x1, *'bar': *0x2, *'baz': *0x3\};/;
+        const objectRestRegExp: RegExp = /var *\{foo, *\.\.\.*_0x[a-f0-9]{4,6}\} *= *_0x[a-f0-9]{4,6};/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/object-rest.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('Match #1: should transform object name', () => {
+            assert.match(obfuscatedCode, objectRegExp);
+        });
+
+        it('Match #2: should transform object rest construction', () => {
+            assert.match(obfuscatedCode, objectRestRegExp);
+        });
+    });
+
+    describe('object spread', () => {
+        const object1RegExp: RegExp = /var *_0x[a-f0-9]{4,6} *= *\{'foo': *0x1\};/;
+        const object2RegExp: RegExp = /var *_0x[a-f0-9]{4,6} *= *\{'bar': *0x2\};/;
+        const objectSpreadRegExp: RegExp = /var *_0x[a-f0-9]{4,6} *= *\{\.\.\._0x[a-f0-9]{4,6}, *\.\.\._0x[a-f0-9]{4,6}\};/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/object-spread.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('Match #1: should transform object name', () => {
+            assert.match(obfuscatedCode, object1RegExp);
+        });
+
+        it('Match #2: should transform object name', () => {
+            assert.match(obfuscatedCode, object2RegExp);
+        });
+
+        it('Match #3: should transform object spread construction', () => {
+            assert.match(obfuscatedCode, objectSpreadRegExp);
+        });
+    });
 });

+ 8 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/fixtures/object-rest.js

@@ -0,0 +1,8 @@
+(function() {
+    var obj = {
+        foo: 1,
+        bar: 2,
+        baz: 3
+    };
+    var {foo, ...rest} = obj;
+})();

+ 5 - 0
test/functional-tests/node-transformers/converting-transformers/object-expression-transformer/fixtures/object-spread.js

@@ -0,0 +1,5 @@
+(function() {
+    var obj1 = {foo: 1};
+    var obj2 = {bar: 2};
+    var newObj = {...obj1, ...obj2};
+})();

+ 54 - 0
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/FunctionTransformer.spec.ts

@@ -284,4 +284,58 @@ describe('FunctionTransformer', () => {
             assert.equal(arrayPatternIdentifierName2, functionBodyIdentifierName2);
         });
     });
+
+    describe('rest parameters', () => {
+        const functionRegExp: RegExp = /function *func *\(_0x[a-f0-9]{4,6}, *..._0x[a-f0-9]{4,6}\) *\{/;
+        const returnRegExp: RegExp = /return *_0x[a-f0-9]{4,6} *\+ *_0x[a-f0-9]{4,6};/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/rest-parameter.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('Match #1: should transform function rest parameter', () => {
+            assert.match(obfuscatedCode, functionRegExp);
+        });
+
+        it('Match #2: should transform identifiers inside function body', () => {
+            assert.match(obfuscatedCode, returnRegExp);
+        });
+    });
+
+    describe('object rest parameter', () => {
+        const functionRegExp: RegExp = /function *func *\(\{foo, *..._0x[a-f0-9]{4,6}\}\) *\{/;
+        const returnRegExp: RegExp = /return *foo *\+ *_0x[a-f0-9]{4,6};/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/object-rest-parameter.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('Match #1: should transform function rest parameter', () => {
+            assert.match(obfuscatedCode, functionRegExp);
+        });
+
+        it('Match #2: should transform identifiers inside function body', () => {
+            assert.match(obfuscatedCode, returnRegExp);
+        });
+    });
 });

+ 3 - 0
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/object-rest-parameter.js

@@ -0,0 +1,3 @@
+function func ({foo, ...rest}) {
+    return foo + rest;
+}

+ 3 - 0
test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/fixtures/rest-parameter.js

@@ -0,0 +1,3 @@
+function func (foo, ...rest) {
+    return foo + rest;
+}

+ 27 - 0
test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/VariableDeclarationTransformer.spec.ts

@@ -571,4 +571,31 @@ describe('VariableDeclarationTransformer', () => {
             assert.match(obfuscatedCode, defaultExportRegExp);
         });
     });
+
+    describe('Variant #16: object rest', () => {
+        const objectRegExp: RegExp = /var *_0x[a-f0-9]{4,6} *= *\{'foo': *0x1, *'bar': *0x2, *'baz': *0x3\};/;
+        const objectRestRegExp: RegExp = /var *\{foo, *\.\.\.*_0x[a-f0-9]{4,6}\} *= *_0x[a-f0-9]{4,6};/;
+
+        let obfuscatedCode: string;
+
+        before(() => {
+            const code: string = readFileAsString(__dirname + '/fixtures/object-rest.js');
+            const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET
+                }
+            );
+
+            obfuscatedCode = obfuscationResult.getObfuscatedCode();
+        });
+
+        it('Match #1: should transform object name', () => {
+            assert.match(obfuscatedCode, objectRegExp);
+        });
+
+        it('Match #2: should transform object rest construction', () => {
+            assert.match(obfuscatedCode, objectRestRegExp);
+        });
+    });
 });

+ 8 - 0
test/functional-tests/node-transformers/obfuscating-transformers/variable-declaration-transformer/fixtures/object-rest.js

@@ -0,0 +1,8 @@
+(function() {
+    var obj = {
+        foo: 1,
+        bar: 2,
+        baz: 3
+    };
+    var {foo, ...rest} = obj;
+})();

+ 24 - 27
yarn.lock

@@ -599,9 +599,9 @@
   version "9.6.0"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.0.tgz#d3480ee666df9784b1001a1872a2f6ccefb6c2d7"
 
-"@types/[email protected].2":
-  version "9.6.2"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.2.tgz#e49ac1adb458835e95ca6487bc20f916b37aff23"
+"@types/[email protected].5":
+  version "9.6.5"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.5.tgz#ee700810fdf49ac1c399fc5980b7559b3e5a381d"
 
 "@types/[email protected]":
   version "2.0.2"
@@ -618,9 +618,9 @@
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@types/string-template/-/string-template-1.0.2.tgz#363b273c9b456705e3111e3571e9248f6474eba4"
 
-"@types/[email protected].5":
-  version "1.13.5"
-  resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.5.tgz#ca854e9fbdbcdf45d7376882875f28e2c60593f8"
+"@types/[email protected].6":
+  version "1.13.6"
+  resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.6.tgz#128d1685a7c34d31ed17010fc87d6a12c1de6976"
 
 abbrev@1:
   version "1.1.1"
@@ -636,17 +636,13 @@ acorn-dynamic-import@^3.0.0:
   dependencies:
     acorn "^5.0.0"
 
-acorn-jsx@^3.0.0:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
+acorn-jsx@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e"
   dependencies:
-    acorn "^3.0.4"
-
-acorn@^3.0.4:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
+    acorn "^5.0.3"
 
-acorn@^5.0.0, acorn@^5.5.0:
+acorn@^5.0.0, acorn@^5.0.3, acorn@^5.5.1:
   version "5.5.3"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
 
@@ -869,9 +865,9 @@ atob@^2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
 
[email protected]-0:
-  version "5.0.0-0"
-  resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-5.0.0-0.tgz#1330750fe603d54a5f0e25429185baed0e48f9f2"
[email protected]:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-5.0.0.tgz#130c304ae52a60933f15d93f7629003b483fa8b1"
   dependencies:
     chalk "^2.3.1"
     enhanced-resolve "^4.0.0"
@@ -2429,12 +2425,12 @@ eslint-scope@^3.7.1:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
-espree@3.5.4:
-  version "3.5.4"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7"
+espree@4.0.0-alpha.0:
+  version "4.0.0-alpha.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-4.0.0-alpha.0.tgz#c12e621c27cdd74655aaed07011664dba9b8c22d"
   dependencies:
-    acorn "^5.5.0"
-    acorn-jsx "^3.0.0"
+    acorn "^5.5.1"
+    acorn-jsx "^4.1.1"
 
 esprima@^2.7.1:
   version "2.7.3"
@@ -4017,7 +4013,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
 
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+[email protected], minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
   dependencies:
@@ -4067,9 +4063,9 @@ [email protected], [email protected], "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
   dependencies:
     minimist "0.0.8"
 
-mocha@5.0.5:
-  version "5.0.5"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.0.5.tgz#e228e3386b9387a4710007a641f127b00be44b52"
+mocha@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.1.0.tgz#5ff11cc39c0bb65330ac6c41f9086634e3e3f686"
   dependencies:
     browser-stdout "1.3.1"
     commander "2.11.0"
@@ -4079,6 +4075,7 @@ [email protected]:
     glob "7.1.2"
     growl "1.10.3"
     he "1.1.1"
+    minimatch "3.0.4"
     mkdirp "0.5.1"
     supports-color "4.4.0"
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است