Optimisations for identifers renaming

Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/146
This commit is contained in:
sanex3339
2017-12-13 00:26:23 +03:00
parent 4bde27915f
commit 292647b519
16 changed files with 219 additions and 91 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -26,7 +26,7 @@
"escodegen-wallaby": "1.6.15",
"esprima": "4.0.0",
"estraverse": "4.2.0",
"inversify": "4.5.2",
"inversify": "4.6.0",
"md5": "2.2.1",
"mkdirp": "0.5.1",
"opencollective": "1.0.3",
@@ -34,10 +34,10 @@
"reflect-metadata": "0.1.10",
"source-map-support": "0.5.0",
"string-template": "1.0.0",
"tslib": "1.8.0"
"tslib": "1.8.1"
},
"devDependencies": {
"@types/chai": "4.0.7",
"@types/chai": "4.0.9",
"@types/chance": "0.7.35",
"@types/commander": "2.12.2",
"@types/escodegen": "0.0.6",
@@ -47,10 +47,10 @@
"@types/md5": "2.1.32",
"@types/mkdirp": "0.5.2",
"@types/mocha": "2.2.44",
"@types/node": "8.0.54",
"@types/sinon": "4.0.0",
"@types/node": "8.0.58",
"@types/sinon": "4.1.0",
"@types/string-template": "1.0.2",
"@types/webpack-env": "1.13.2",
"@types/webpack-env": "1.13.3",
"awesome-typescript-loader": "3.4.1",
"babel-cli": "6.26.0",
"babel-loader": "7.1.2",
@@ -61,7 +61,7 @@
"istanbul": "1.1.0-alpha.1",
"mocha": "4.0.1",
"pre-commit": "1.2.2",
"sinon": "4.1.2",
"sinon": "4.1.3",
"threads": "^0.10.0",
"ts-node": "3.3.0",
"tslint": "5.8.0",
+2 -1
View File
@@ -17,6 +17,7 @@ import { DEFAULT_PRESET } from '../options/presets/Default';
import { ArraySanitizer } from './sanitizers/ArraySanitizer';
import { BooleanSanitizer } from './sanitizers/BooleanSanitizer';
import { IdentifierNamesGeneratorSanitizer } from './sanitizers/IdentifierNamesGeneratorSanitizer';
import { ObfuscationTargetSanitizer } from './sanitizers/ObfuscatingTargetSanitizer';
import { SourceMapModeSanitizer } from './sanitizers/SourceMapModeSanitizer';
import { StringArrayEncodingSanitizer } from './sanitizers/StringArrayEncodingSanitizer';
@@ -241,7 +242,7 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
)
.option(
'--identifier-names-generator <string> [hexadecimal, mangled]', 'Sets identifier names generator (Default: hexadecimal)',
BooleanSanitizer
IdentifierNamesGeneratorSanitizer
)
.option(
'--log <boolean>', 'Enables logging of the information to the console',
+1 -1
View File
@@ -6,7 +6,7 @@ import { TCLISanitizer } from '../../types/cli/TCLISanitizer';
*/
export const ArraySanitizer: TCLISanitizer = (value: string): string[] => {
if (/,$/.test(value)) {
throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --optionName 'value1, value2'`);
throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --option-name 'value1, value2'`);
}
return value.split(',').map((string: string) => string.trim());
@@ -0,0 +1,21 @@
import { TCLISanitizer } from '../../types/cli/TCLISanitizer';
import { IdentifierNamesGenerator } from '../../enums/generators/identifier-names-generators/IdentifierNamesGenerator';
/**
* @param {string} value
* @returns {string}
*/
export const IdentifierNamesGeneratorSanitizer: TCLISanitizer = (value: string): string => {
const isCorrectIdentifierNamesGenerator: boolean = Object
.keys(IdentifierNamesGenerator)
.some((key: any): boolean => {
return IdentifierNamesGenerator[key] === value;
});
if (!isCorrectIdentifierNamesGenerator) {
throw new ReferenceError('Invalid value of `--identifier-names-generator` option');
}
return value;
};
+1 -1
View File
@@ -14,7 +14,7 @@ export const SourceMapModeSanitizer: TCLISanitizer = (value: string): string =>
});
if (!isCorrectSourceMapMode) {
throw new ReferenceError('Invalid value of `--sourceMapMode` option');
throw new ReferenceError('Invalid value of `--source-map-mode` option');
}
return value;
@@ -6,6 +6,8 @@ import * as ESTree from 'estree';
import { TIdentifierObfuscatingReplacerFactory } from "../../types/container/node-transformers/TIdentifierObfuscatingReplacerFactory";
import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
import { TReplaceableIdentifiers } from '../../types/node-transformers/TReplaceableIdentifiers';
import { TReplaceableIdentifiersNames } from '../../types/node-transformers/TReplaceableIdentifiersNamesMap';
import { IIdentifierObfuscatingReplacer } from '../../interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer';
import { IOptions } from '../../interfaces/options/IOptions';
@@ -38,7 +40,7 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
/**
* @type {Map<ESTree.Node, ESTree.Identifier[]>}
*/
private readonly replaceableIdentifiers: Map <ESTree.Node, ESTree.Identifier[]> = new Map();
private readonly replaceableIdentifiers: TReplaceableIdentifiers = new Map();
/**
* @param {TIdentifierObfuscatingReplacerFactory} identifierObfuscatingReplacerFactory
@@ -89,7 +91,7 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
// check for cached identifiers for current scope node. If exist - loop through them.
if (this.replaceableIdentifiers.has(blockScopeNode)) {
this.replaceScopeCachedIdentifiers(blockScopeNode, nodeIdentifier);
this.replaceScopeCachedIdentifiers(functionDeclarationNode, blockScopeNode, nodeIdentifier);
} else {
this.replaceScopeIdentifiers(blockScopeNode, nodeIdentifier);
}
@@ -106,18 +108,37 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
}
/**
* @param {FunctionDeclaration} functionDeclarationNode
* @param {TNodeWithBlockStatement} blockScopeNode
* @param {number} nodeIdentifier
*/
private replaceScopeCachedIdentifiers (blockScopeNode: TNodeWithBlockStatement, nodeIdentifier: number): void {
const cachedReplaceableIdentifiers: ESTree.Identifier[] = <ESTree.Identifier[]>this.replaceableIdentifiers.get(blockScopeNode);
private replaceScopeCachedIdentifiers (
functionDeclarationNode: ESTree.FunctionDeclaration,
blockScopeNode: TNodeWithBlockStatement,
nodeIdentifier: number
): void {
const cachedReplaceableIdentifiersNamesMap: TReplaceableIdentifiersNames | undefined = this.replaceableIdentifiers.get(blockScopeNode);
cachedReplaceableIdentifiers.forEach((replaceableIdentifier: ESTree.Identifier) => {
const newReplaceableIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer.
replace(replaceableIdentifier.name, nodeIdentifier);
if (!cachedReplaceableIdentifiersNamesMap) {
return;
}
const cachedReplaceableIdentifiers: ESTree.Identifier[] | undefined = cachedReplaceableIdentifiersNamesMap
.get(functionDeclarationNode.id.name);
if (!cachedReplaceableIdentifiers) {
return;
}
const cachedReplaceableIdentifierLength: number = cachedReplaceableIdentifiers.length;
for (let i: number = 0; i < cachedReplaceableIdentifierLength; i++) {
const replaceableIdentifier: ESTree.Identifier = cachedReplaceableIdentifiers[i];
const newReplaceableIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer
.replace(replaceableIdentifier.name, nodeIdentifier);
replaceableIdentifier.name = newReplaceableIdentifier.name;
});
}
}
/**
@@ -125,7 +146,7 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
* @param {number} nodeIdentifier
*/
private replaceScopeIdentifiers (blockScopeNode: TNodeWithBlockStatement, nodeIdentifier: number): void {
const storedReplaceableIdentifiers: ESTree.Identifier[] = [];
const storedReplaceableIdentifiersNamesMap: TReplaceableIdentifiersNames = new Map();
estraverse.replace(blockScopeNode, {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): any => {
@@ -137,12 +158,15 @@ export class FunctionDeclarationTransformer extends AbstractNodeTransformer {
if (node.name !== newIdentifierName) {
node.name = newIdentifierName;
} else {
const storedReplaceableIdentifiers: ESTree.Identifier[] = storedReplaceableIdentifiersNamesMap.get(node.name) || [];
storedReplaceableIdentifiers.push(node);
storedReplaceableIdentifiersNamesMap.set(node.name, storedReplaceableIdentifiers);
}
}
}
});
this.replaceableIdentifiers.set(blockScopeNode, storedReplaceableIdentifiers);
this.replaceableIdentifiers.set(blockScopeNode, storedReplaceableIdentifiersNamesMap);
}
}
@@ -6,6 +6,8 @@ import * as ESTree from 'estree';
import { TIdentifierObfuscatingReplacerFactory } from '../../types/container/node-transformers/TIdentifierObfuscatingReplacerFactory';
import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';
import { TReplaceableIdentifiers } from '../../types/node-transformers/TReplaceableIdentifiers';
import { TReplaceableIdentifiersNames } from '../../types/node-transformers/TReplaceableIdentifiersNamesMap';
import { IIdentifierObfuscatingReplacer } from '../../interfaces/node-transformers/obfuscating-transformers/obfuscating-replacers/IIdentifierObfuscatingReplacer';
import { IOptions } from '../../interfaces/options/IOptions';
@@ -37,9 +39,9 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
private readonly identifierObfuscatingReplacer: IIdentifierObfuscatingReplacer;
/**
* @type {Map<ESTree.Node, ESTree.Identifier[]>}
* @type {TReplaceableIdentifiers}
*/
private readonly replaceableIdentifiers: Map <ESTree.Node, ESTree.Identifier[]> = new Map();
private readonly replaceableIdentifiers: TReplaceableIdentifiers = new Map();
/**
* @param {TIdentifierObfuscatingReplacerFactory} identifierObfuscatingReplacerFactory
@@ -94,7 +96,7 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
// check for cached identifiers for current scope node. If exist - loop through them.
if (this.replaceableIdentifiers.has(scopeNode)) {
this.replaceScopeCachedIdentifiers(scopeNode, nodeIdentifier);
this.replaceScopeCachedIdentifiers(variableDeclarationNode, scopeNode, nodeIdentifier);
} else {
this.replaceScopeIdentifiers(scopeNode, nodeIdentifier);
}
@@ -107,34 +109,54 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
* @param {number} nodeIdentifier
*/
private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, nodeIdentifier: number): 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.isIdentifierNode(node)) {
this.identifierObfuscatingReplacer.storeNames(node.name, nodeIdentifier);
}
}
});
});
this.traverseDeclarationIdentifiers(variableDeclarationNode, (identifierNode: ESTree.Identifier) => {
this.identifierObfuscatingReplacer.storeNames(identifierNode.name, nodeIdentifier);
});
}
/**
* @param {VariableDeclaration} variableDeclarationNode
* @param {Node} scopeNode
* @param {number} nodeIdentifier
*/
private replaceScopeCachedIdentifiers (scopeNode: ESTree.Node, nodeIdentifier: number): void {
const cachedReplaceableIdentifiers: ESTree.Identifier[] = <ESTree.Identifier[]>this.replaceableIdentifiers.get(scopeNode);
private replaceScopeCachedIdentifiers (
variableDeclarationNode: ESTree.VariableDeclaration,
scopeNode: ESTree.Node,
nodeIdentifier: number
): void {
const cachedReplaceableIdentifiersNamesMap: TReplaceableIdentifiersNames | undefined = this.replaceableIdentifiers.get(scopeNode);
cachedReplaceableIdentifiers.forEach((replaceableIdentifier: ESTree.Identifier) => {
const newReplaceableIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer
.replace(replaceableIdentifier.name, nodeIdentifier);
if (!cachedReplaceableIdentifiersNamesMap) {
return;
}
replaceableIdentifier.name = newReplaceableIdentifier.name;
const identifierNames: string[] = [];
this.traverseDeclarationIdentifiers(variableDeclarationNode, (identifierNode: ESTree.Identifier) => {
identifierNames.push(identifierNode.name);
});
identifierNames.forEach((identifierName: string) => {
const cachedReplaceableIdentifiers: ESTree.Identifier[] | undefined = cachedReplaceableIdentifiersNamesMap.get(identifierName);
if (!cachedReplaceableIdentifiers) {
return;
}
const cachedReplaceableIdentifierLength: number = cachedReplaceableIdentifiers.length;
for (let i: number = 0; i < cachedReplaceableIdentifierLength; i++) {
const replaceableIdentifier: ESTree.Identifier = cachedReplaceableIdentifiers[i];
if (identifierName !== replaceableIdentifier.name) {
continue;
}
const newReplaceableIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer
.replace(replaceableIdentifier.name, nodeIdentifier);
replaceableIdentifier.name = newReplaceableIdentifier.name;
}
});
}
@@ -143,7 +165,7 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
* @param {number} nodeIdentifier
*/
private replaceScopeIdentifiers (blockScopeNode: ESTree.Node, nodeIdentifier: number): void {
const storedReplaceableIdentifiers: ESTree.Identifier[] = [];
const storedReplaceableIdentifiersNamesMap: TReplaceableIdentifiersNames = new Map();
estraverse.replace(blockScopeNode, {
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): any => {
@@ -154,12 +176,39 @@ export class VariableDeclarationTransformer extends AbstractNodeTransformer {
if (node.name !== newIdentifierName) {
node.name = newIdentifierName;
} else {
const storedReplaceableIdentifiers: ESTree.Identifier[] = storedReplaceableIdentifiersNamesMap.get(node.name) || [];
storedReplaceableIdentifiers.push(node);
storedReplaceableIdentifiersNamesMap.set(node.name, storedReplaceableIdentifiers);
}
}
}
});
this.replaceableIdentifiers.set(blockScopeNode, storedReplaceableIdentifiers);
this.replaceableIdentifiers.set(blockScopeNode, storedReplaceableIdentifiersNamesMap);
}
/**
* @param {VariableDeclaration} variableDeclarationNode
* @param {(identifier: Identifier) => void} callback
*/
private traverseDeclarationIdentifiers (
variableDeclarationNode: ESTree.VariableDeclaration,
callback: (identifier: ESTree.Identifier) => void
): 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.isIdentifierNode(node)) {
callback(node);
}
}
});
});
}
}
@@ -66,20 +66,9 @@ export class CommentsTransformer extends AbstractNodeTransformer {
* @returns {Comment[]}
*/
private transformComments (comments: ESTree.Comment[]): ESTree.Comment[] {
let i: number = comments.length;
while (i--) {
const comment: ESTree.Comment = comments[i];
const commentHasPreservedWords: boolean = CommentsTransformer.preservedWords
.some((availableWord: string) => comment.value.includes(availableWord));
if (commentHasPreservedWords) {
continue;
}
comments.splice(i, 1);
}
return comments;
return comments.filter((comment: ESTree.Comment) =>
CommentsTransformer.preservedWords
.some((availableWord: string) => comment.value.includes(availableWord))
);
}
}
@@ -64,7 +64,8 @@ export class ObfuscatingGuardsTransformer extends AbstractNodeTransformer {
* @returns {Node}
*/
public transformNode (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node {
const obfuscationAllowed: boolean = this.obfuscatingGuards.every((nodeGuard: IObfuscatingGuard) => nodeGuard.check(node));
const obfuscationAllowed: boolean = this.obfuscatingGuards
.every((nodeGuard: IObfuscatingGuard) => nodeGuard.check(node));
node.ignoredNode = !obfuscationAllowed;
@@ -4,6 +4,8 @@ import * as ESTree from 'estree';
import { IObfuscatingGuard } from '../../../interfaces/node-transformers/preparing-transformers/obfuscating-guards/IObfuscatingGuard';
import { NodeGuards } from '../../../node/NodeGuards';
@injectable()
export class ConditionalCommentObfuscatingGuard implements IObfuscatingGuard {
/**
@@ -36,7 +38,7 @@ export class ConditionalCommentObfuscatingGuard implements IObfuscatingGuard {
this.obfuscationAllowedForNextNode = null;
}
if (!node.leadingComments && !node.trailingComments) {
if (!NodeGuards.isNodeWithComments(node)) {
return this.obfuscationAllowedForCurrentNode;
}
@@ -0,0 +1,3 @@
import * as ESTree from 'estree';
export type TReplaceableIdentifiers = Map <ESTree.Node, Map<string, ESTree.Identifier[]>>;
@@ -0,0 +1,3 @@
import * as ESTree from 'estree';
export type TReplaceableIdentifiersNames = Map<string, ESTree.Identifier[]>;
+1
View File
@@ -8,6 +8,7 @@ require('source-map-support').install();
import './unit-tests/analyzers/stack-trace-analyzer/StackTraceAnalyzer.spec';
import './unit-tests/cli/sanitizers/ArraySanitizer.spec';
import './unit-tests/cli/sanitizers/BooleanSanitizer.spec';
import './unit-tests/cli/sanitizers/IdentifierNamesGeneratorSanitizer.spec';
import './unit-tests/cli/sanitizers/ObfuscationTargetSanitizer.spec';
import './unit-tests/cli/sanitizers/SourceMapModeSanitizer.spec';
import './unit-tests/cli/sanitizers/StringArrayEncodingSanitizer.spec';
@@ -0,0 +1,36 @@
import { assert } from 'chai';
import { IdentifierNamesGeneratorSanitizer } from '../../../../src/cli/sanitizers/IdentifierNamesGeneratorSanitizer';
describe('IdentifierNamesGeneratorSanitizer', () => {
describe('IdentifierNamesGeneratorSanitizer: TCLISanitizer = (value: string): string', () => {
describe('variant #1: valid identifier names generator', () => {
const inputValue: string = 'mangled';
const expectedValue: string = inputValue;
let value: string;
before(() => {
value = IdentifierNamesGeneratorSanitizer(inputValue);
});
it('should sanitize value', () => {
assert.equal(value, expectedValue);
});
});
describe('variant #2: invalid identifier names generator', () => {
const inputValue: string = 'foo';
let testFunc: () => void;
before(() => {
testFunc = () => IdentifierNamesGeneratorSanitizer(inputValue);
});
it('should throw error', () => {
assert.throw(testFunc, ReferenceError);
});
});
});
});
+26 -28
View File
@@ -2,9 +2,9 @@
# yarn lockfile v1
"@types/chai@4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.7.tgz#cab25b9fcb45903fe6c7fa716dc6394314c13dd8"
"@types/chai@4.0.9":
version "4.0.9"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.9.tgz#9897c0ca23acb117f8b4e3af2c81d85cccdd9bc0"
"@types/chance@0.7.35":
version "0.7.35"
@@ -36,10 +36,6 @@
version "0.0.38"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2"
"@types/events@*":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.1.0.tgz#93b1be91f63c184450385272c47b6496fd028e02"
"@types/md5@2.1.32":
version "2.1.32"
resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.1.32.tgz#93e23437fcd17a7b9ca98d02aa6002e835842fe8"
@@ -60,23 +56,21 @@
version "8.0.53"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8"
"@types/node@8.0.54":
version "8.0.54"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.54.tgz#3fd9357db4af388b79e03845340259440edffde6"
dependencies:
"@types/events" "*"
"@types/node@8.0.58":
version "8.0.58"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.58.tgz#5b3881c0be3a646874803fee3197ea7f1ed6df90"
"@types/sinon@4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362"
"@types/sinon@4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.1.0.tgz#cdfb934d656b2c888e51f53e95900c7bbca11c08"
"@types/string-template@1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@types/string-template/-/string-template-1.0.2.tgz#363b273c9b456705e3111e3571e9248f6474eba4"
"@types/webpack-env@1.13.2":
version "1.13.2"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.2.tgz#c290b99dbef74df21b06671aea36e355bf3b27e1"
"@types/webpack-env@1.13.3":
version "1.13.3"
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.3.tgz#0ecbe70f87341767793774d3683b51aa3246434c"
abbrev@1, abbrev@1.0.x:
version "1.0.9"
@@ -1971,9 +1965,9 @@ invariant@^2.2.2:
dependencies:
loose-envify "^1.0.0"
inversify@4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.5.2.tgz#f2478fd140f398836b2e1fee839f37588f1e6a9b"
inversify@4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/inversify/-/inversify-4.6.0.tgz#49abb2ee8fce3c99e1e94f01f36df77c3bfeba75"
invert-kv@^1.0.0:
version "1.0.0"
@@ -3302,9 +3296,9 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
sinon@4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.2.tgz#65610521d926fb53742dd84cd599f0b89a82f440"
sinon@4.1.3:
version "4.1.3"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.3.tgz#fc599eda47ed9f1a694ce774b94ab44260bd7ac5"
dependencies:
diff "^3.1.0"
formatio "1.2.0"
@@ -3312,7 +3306,7 @@ sinon@4.1.2:
lolex "^2.2.0"
nise "^1.2.0"
supports-color "^4.4.0"
type-detect "^4.0.0"
type-detect "^4.0.5"
slash@^1.0.0:
version "1.0.0"
@@ -3668,9 +3662,9 @@ tsconfig@^6.0.0:
strip-bom "^3.0.0"
strip-json-comments "^2.0.0"
tslib@1.8.0, tslib@^1.0.0, tslib@^1.7.1:
version "1.8.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.0.tgz#dc604ebad64bcbf696d613da6c954aa0e7ea1eb6"
tslib@1.8.1, tslib@^1.0.0, tslib@^1.7.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.8.1.tgz#6946af2d1d651a7b1863b531d6e5afa41aa44eac"
tslint-eslint-rules@4.1.1:
version "4.1.1"
@@ -3740,6 +3734,10 @@ type-detect@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.3.tgz#0e3f2670b44099b0b46c284d136a7ef49c74c2ea"
type-detect@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.5.tgz#d70e5bc81db6de2a381bcaca0c6e0cbdc7635de2"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"