Custom linter to check code quality (#2753)

This commit is contained in:
Scott Nonnenberg 2018-09-20 15:24:52 -07:00 committed by GitHub
parent 366401f77a
commit ecb126e74c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 7467 additions and 66 deletions

View file

@ -16,8 +16,10 @@ ts/**/*.js
ts/protobuf/*.d.ts
ts/protobuf/*.js
stylesheets/manifest.css
ts/util/lint/exceptions.json
# Third-party files
node_modules/**
components/**
js/Mp3LameEncoder.min.js
js/WebAudioRecorderMp3.js

View file

@ -9,6 +9,7 @@ install:
script:
- yarn generate
- yarn lint
- yarn lint-deps
- yarn test-node
- yarn nsp check
- yarn prepare-beta-build

View file

@ -7,6 +7,7 @@ const spectron = require('spectron');
const asar = require('asar');
const fs = require('fs');
const assert = require('assert');
const sass = require('node-sass');
/* eslint-disable more/no-then, no-console */
@ -98,6 +99,7 @@ module.exports = grunt => {
},
sass: {
options: {
implementation: sass,
sourceMap: true,
importer: importOnce,
},

View file

@ -14,6 +14,7 @@ install:
build_script:
- yarn generate
- yarn lint-windows
- yarn lint-deps
- yarn test-node
- yarn nsp check
- node build\grunt.js

View file

@ -32,6 +32,7 @@
"eslint": "eslint .",
"lint": "yarn format --list-different && yarn lint-windows",
"lint-windows": "yarn eslint && yarn tslint",
"lint-deps": "node ts/util/lint/linter.js",
"tslint": "tslint --format stylish --project .",
"format": "prettier --write \"*.{css,js,json,md,scss,ts,tsx}\" \"./**/*.{css,js,json,md,scss,ts,tsx}\"",
"transpile": "tsc",
@ -73,9 +74,11 @@
"moment": "^2.21.0",
"mustache": "^2.3.0",
"node-fetch": "https://github.com/scottnonnenberg-signal/node-fetch.git#3e5f51e08c647ee5f20c43b15cf2d352d61c36b4",
"node-gyp": "^3.8.0",
"node-sass": "^4.9.3",
"os-locale": "^2.1.0",
"pify": "^3.0.0",
"protobufjs": "^6.8.6",
"protobufjs": "~6.8.6",
"proxy-agent": "^2.1.0",
"react": "^16.2.0",
"react-contextmenu": "^2.9.2",
@ -126,7 +129,7 @@
"grunt-contrib-watch": "^1.0.0",
"grunt-exec": "^3.0.0",
"grunt-gitinfo": "^0.1.7",
"grunt-sass": "^2.0.0",
"grunt-sass": "^3.0.1",
"mocha": "^4.1.0",
"mocha-testcheck": "^1.0.0-rc.0",
"node-sass-import-once": "^1.2.0",

View file

@ -0,0 +1,29 @@
// tslint:disable no-console
import { join } from 'path';
import { fromPairs, groupBy, map } from 'lodash';
import { ExceptionType } from './types';
import { loadJSON } from './util';
const exceptionsPath = join(__dirname, 'exceptions.json');
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const byRule = groupBy(exceptions, 'rule');
const byRuleThenByCategory = fromPairs(
map(byRule, (list, ruleName) => {
const byCategory = groupBy(list, 'reasonCategory');
return [
ruleName,
fromPairs(
map(byCategory, (innerList, categoryName) => {
return [categoryName, innerList.length];
})
),
];
})
);
console.log(JSON.stringify(byRuleThenByCategory, null, ' '));

6715
ts/util/lint/exceptions.json Normal file

File diff suppressed because it is too large Load diff

265
ts/util/lint/linter.ts Normal file
View file

@ -0,0 +1,265 @@
// tslint:disable no-console
import { readFileSync } from 'fs';
import { join, relative } from 'path';
// @ts-ignore
import glob from 'glob';
import { forEach, some, values } from 'lodash';
import { ExceptionType, REASONS, RuleType } from './types';
import { ENCODING, loadJSON } from './util';
const ALL_REASONS = REASONS.join('|');
const now = new Date();
function getExceptionKey(exception: any) {
return `${exception.rule}-${exception.path}-${exception.lineNumber}`;
}
function createLookup(list: Array<any>) {
const lookup = Object.create(null);
forEach(list, exception => {
const key = getExceptionKey(exception);
if (lookup[key]) {
throw new Error(`Duplicate exception found for key ${key}`);
}
lookup[key] = exception;
});
return lookup;
}
const rulesPath = join(__dirname, 'rules.json');
const exceptionsPath = join(__dirname, 'exceptions.json');
const basePath = join(__dirname, '../../..');
const searchPattern = join(basePath, '**/*.{js,ts,tsx}');
const rules: Array<RuleType> = loadJSON(rulesPath);
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const exceptionsLookup = createLookup(exceptions);
let scannedCount = 0;
const allSourceFiles = glob.sync(searchPattern, { nodir: true });
const results: Array<ExceptionType> = [];
const excludedFiles = [
// Generated files
'^js/components.js',
'^js/libtextsecure.js',
'^js/util_worker.js',
'^libtextsecure/components.js',
'^libtextsecure/test/test.js',
'^test/test.js',
// From libsignal-protocol-javascript project
'^js/libsignal-protocol-worker.js',
'^libtextsecure/libsignal-protocol.js',
// Copied from dependency
'^js/Mp3LameEncoder.min.js',
// Test files
'^libtextsecure/test/*',
'^test/*',
// Modules used only in test/development scenarios
'^node_modules/@types/*',
'^node_modules/ajv/*',
'^node_modules/amdefine/*',
'^node_modules/anymatch/*',
'^node_modules/asn1\\.js/*',
'^node_modules/autoprefixer/*',
'^node_modules/babel*',
'^node_modules/bluebird/*',
'^node_modules/body-parser/*',
'^node_modules/bower/*',
'^node_modules/buble/*',
'^node_modules/chai/*',
'^node_modules/cli-table2/*',
'^node_modules/codemirror/*',
'^node_modules/coffee-script/*',
'^node_modules/compression/*',
'^node_modules/degenerator/*',
'^node_modules/detect-port-alt/*',
'^node_modules/electron-builder/*',
'^node_modules/electron-icon-maker/*',
'^node_modules/electron-osx-sign/*',
'^node_modules/electron-publish/*',
'^node_modules/escodegen/*',
'^node_modules/eslint*',
'^node_modules/esprima/*',
'^node_modules/express/*',
'^node_modules/extract-zip/*',
'^node_modules/finalhandler/*',
'^node_modules/fsevents/*',
'^node_modules/globule/*',
'^node_modules/grunt*',
'^node_modules/handle-thing/*',
'^node_modules/har-validator/*',
'^node_modules/highlight\\.js/*',
'^node_modules/hpack\\.js/*',
'^node_modules/http-proxy-middlewar/*',
'^node_modules/icss-utils/*',
'^node_modules/intl-tel-input/examples/*',
'^node_modules/istanbul*',
'^node_modules/jimp/*',
'^node_modules/jquery/*',
'^node_modules/jss/*',
'^node_modules/jss-global/*',
'^node_modules/livereload-js/*',
'^node_modules/lolex/*',
'^node_modules/magic-string/*',
'^node_modules/mocha/*',
'^node_modules/minimatch/*',
'^node_modules/nise/*',
'^node_modules/node-sass-import-once/*',
'^node_modules/node-sass/*',
'^node_modules/nsp/*',
'^node_modules/nyc/*',
'^node_modules/phantomjs-prebuilt/*',
'^node_modules/postcss*',
'^node_modules/preserve/*',
'^node_modules/prettier/*',
'^node_modules/protobufjs/cli/*',
'^node_modules/ramda/*',
'^node_modules/react-docgen/*',
'^node_modules/react-error-overlay/*',
'^node_modules/react-styleguidist/*',
'^node_modules/recast/*',
'^node_modules/reduce-css-calc/*',
'^node_modules/resolve/*',
'^node_modules/sass-graph/*',
'^node_modules/scss-tokenizer/*',
'^node_modules/send/*',
'^node_modules/serve-index/*',
'^node_modules/sinon/*',
'^node_modules/snapdragon-util/*',
'^node_modules/snapdragon/*',
'^node_modules/sockjs-client/*',
'^node_modules/spectron/*',
'^node_modules/style-loader/*',
'^node_modules/svgo/*',
'^node_modules/testcheck/*',
'^node_modules/text-encoding/*',
'^node_modules/tinycolor2/*',
'^node_modules/to-ast/*',
'^node_modules/trough/*',
'^node_modules/ts-loader/*',
'^node_modules/tslint*',
'^node_modules/tweetnacl/*',
'^node_modules/typescript/*',
'^node_modules/uglify-es/*',
'^node_modules/uglify-js/*',
'^node_modules/use/*',
'^node_modules/vary/*',
'^node_modules/vm-browserify/*',
'^node_modules/webdriverio/*',
'^node_modules/webpack*',
'^node_modules/xmldom/*',
'^node_modules/xml-parse-from-string/*',
];
function setupRules(allRules: Array<RuleType>) {
forEach(allRules, (rule, index) => {
if (!rule.name) {
throw new Error(`Rule at index ${index} is missing a name`);
}
if (!rule.expression) {
throw new Error(`Rule '${rule.name}' is missing an expression`);
}
rule.regex = new RegExp(rule.expression, 'g');
});
}
setupRules(rules);
forEach(allSourceFiles, file => {
const relativePath = relative(basePath, file).replace(/\\/g, '/');
if (
some(excludedFiles, excluded => {
const regex = new RegExp(excluded);
return regex.test(relativePath);
})
) {
return;
}
scannedCount += 1;
const fileContents = readFileSync(file, ENCODING);
const lines = fileContents.split('\n');
forEach(rules, (rule: RuleType) => {
const excludedModules = rule.excludedModules || [];
if (some(excludedModules, module => relativePath.startsWith(module))) {
return;
}
forEach(lines, (rawLine, lineIndex) => {
const line = rawLine.replace(/\r/g, '');
if (!rule.regex.test(line)) {
return;
}
const path = relativePath;
const lineNumber = lineIndex + 1;
const exceptionKey = getExceptionKey({
rule: rule.name,
path: relativePath,
lineNumber,
});
const exception = exceptionsLookup[exceptionKey];
if (exception && (!exception.line || exception.line === line)) {
delete exceptionsLookup[exceptionKey];
return;
}
results.push({
rule: rule.name,
path,
line: line.length < 300 ? line : undefined,
lineNumber,
reasonCategory: ALL_REASONS,
updated: now.toJSON(),
reasonDetail: '<optional>',
});
});
});
});
const unusedExceptions = values(exceptionsLookup);
console.log(
`${scannedCount} files scanned.`,
`${results.length} questionable lines,`,
`${unusedExceptions.length} unused exceptions,`,
`${exceptions.length} total exceptions.`
);
if (results.length === 0 && unusedExceptions.length === 0) {
process.exit();
}
console.log();
console.log('Questionable lines:');
console.log(JSON.stringify(results, null, ' '));
if (unusedExceptions.length) {
console.log();
console.log('Unused exceptions!');
console.log(JSON.stringify(unusedExceptions, null, ' '));
}
process.exit(1);

173
ts/util/lint/rules.json Normal file
View file

@ -0,0 +1,173 @@
[
{
"name": "eval",
"expression": "\\beval\\(",
"reason": "Arbitrary code execution"
},
{
"name": "DOM-innerHTML",
"expression": "\\binnerHTML\\b",
"reason": "Potential XSS"
},
{
"name": "DOM-outerHTML",
"expression": "\\bouterHTML\\b",
"reason": "Potential XSS"
},
{
"name": "DOM-document.write(",
"expression": "\\bdocument.write(ln)?\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-$(",
"expression": "\\$\\(",
"reason": "Potential XSS",
"excludedModules": ["node_modules/prelude-ls"]
},
{
"name": "jQuery-html(",
"expression": "\\bhtml\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-append(",
"expression": "\\bappend\\(",
"reason": "Potential XSS",
"excludedModules": [
"components/bytebuffer",
"components/protobuf",
"node_modules/google-libphonenumber",
"node_modules/handlebars"
]
},
{
"name": "jQuery-appendTo(",
"expression": "\\bappendTo\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-insertAfter(",
"expression": "\\binsertAfter\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-insertBefore(",
"expression": "\\binsertBefore\\(",
"reason": "Potential XSS",
"excludedModules": ["node_modules/react-dom"]
},
{
"name": "jQuery-prepend(",
"expression": "\\bprepend\\(",
"reason": "Potential XSS",
"excludedModules": ["components/bytebuffer", "node_modules/handlebars"]
},
{
"name": "jQuery-prependTo(",
"expression": "\\bprependTo\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-wrap(",
"expression": "\\bwrap\\(",
"reason": "Potential XSS",
"excludedModules": [
"components/bytebuffer",
"components/protobuf",
"node_modules/handlebars",
"node_modules/lodash"
]
},
{
"name": "jQuery-wrapInner(",
"expression": "\\bwrapInner\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-wrapAll(",
"expression": "\\bwrapAll\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-before(",
"expression": "\\bbefore\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-after(",
"expression": "\\bafter\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-globalEval(",
"expression": "\\bglobalEval\\(",
"reason": "Arbitrary code execution"
},
{
"name": "jQuery-getScript(",
"expression": "\\bgetScript\\(",
"reason": "Arbitrary code execution"
},
{
"name": "jQuery-load(",
"expression": "\\bload\\(",
"reason": "Arbitrary code execution"
},
{
"name": "React-ref",
"expression": "\\bref(\\s)*=\\b",
"reason": "Potential XSS",
"excludedModules": [
"node_modules/react-dom",
"node_modules/tslint-microsoft-contrib",
"node_modules/react-error-overlay",
"node_modules/react-styleguidist"
]
},
{
"name": "React-createRef",
"expression": "\\bcreateRef\\(",
"reason": "Potential XSS",
"excludedModules": [
"node_modules/react-dom",
"node_modules/tslint-microsoft-contrib",
"node_modules/react-error-overlay",
"node_modules/react-styleguidist"
]
},
{
"name": "React-findDOMNode",
"expression": "\\bfindDOMNode\\(",
"reason": "Potential XSS",
"excludedModules": [
"node_modules/react-dom",
"node_modules/tslint-microsoft-contrib",
"node_modules/react-error-overlay",
"node_modules/react-styleguidist"
]
},
{
"name": "React-dangerouslySetInnerHTML",
"expression": "\\bdangerouslySetInnerHTML\\b",
"reason": "Potential XSS",
"excludedModules": [
"node_modules/react-dom",
"node_modules/tslint-microsoft-contrib",
"node_modules/react-error-overlay",
"node_modules/react-styleguidist"
]
},
{
"name": "fbjs-createNodesFromMarkup",
"expression": "\\bcreateNodesFromMarkup\\b",
"reason": "Potential XSS, pipes input to innerHTML",
"excludedModules": ["node_modules/react-dom", "node_modules/fbjs"]
},
{
"name": "thenify-multiArgs",
"expression": "\\bmultiArgs\\b",
"reason": "Potential arbitrary code execution, piped to eval",
"excludedModules": ["node_modules/thenify"]
}
]

View file

@ -0,0 +1,14 @@
// tslint:disable no-console
import { join } from 'path';
import { writeFileSync } from 'fs';
import { ExceptionType } from './types';
import { loadJSON, sortExceptions } from './util';
const exceptionsPath = join(__dirname, 'exceptions.json');
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const sorted = sortExceptions(exceptions);
writeFileSync(exceptionsPath, JSON.stringify(sorted, null, ' '));

63
ts/util/lint/types.ts Normal file
View file

@ -0,0 +1,63 @@
// Tool requirements:
// - Feed it a set of regular expressions with descriptions as to what the risks are
// - Feed it also a set of exceptions
// - It would tell us if there were any new matches that didn't already have exceptions
//
// Rules:
// {
// "name": "rule-name",
// "expression": "^regex-as-string$",
// "reason": "Reason that this expression is dangerous"
// }
//
// Categories of reasons - low to high risk:
// "falseMatch"
// "testCode"
// "exampleCode"
// "otherUtilityCode"
// "regexMatchedSafeCode"
// "notExercisedByOurApp"
// "ruleNeeded"
// "usageTrusted"
//
// Exceptions:
// [{
// "rule": "rule-name",
// "path": "path/to/filename.js",
// "lineNumber": 45,
// "reasonCategory": "<category from list above>",
// "updated": "2018-09-08T00:21:13.180Z",
// "reasonDetail": "<Optional additional information about why this is okay>"
// }]
//
// When the tool finds issues it outputs them in exception format to make it easy to add
// to the exceptions.json file
export const REASONS = [
'falseMatch',
'testCode',
'exampleCode',
'otherUtilityCode',
'regexMatchedSafeCode',
'notExercisedByOurApp',
'ruleNeeded',
'usageTrusted',
];
export type RuleType = {
name: string;
expression: string | null;
reason: string;
regex: RegExp;
excludedModules: Array<string> | null;
};
export type ExceptionType = {
rule: string;
path: string;
line?: string;
lineNumber: number;
reasonCategory: string;
updated: string;
reasonDetail: string;
};

24
ts/util/lint/util.ts Normal file
View file

@ -0,0 +1,24 @@
// tslint:disable no-console
import { readFileSync } from 'fs';
import { orderBy } from 'lodash';
import { ExceptionType } from './types';
export const ENCODING = 'utf8';
export function loadJSON(target: string) {
try {
const contents = readFileSync(target, ENCODING);
return JSON.parse(contents);
} catch (error) {
console.log(`Error loading JSON from ${target}: ${error.stack}`);
throw error;
}
}
export function sortExceptions(exceptions: Array<ExceptionType>) {
return orderBy(exceptions, ['path', 'lineNumber', 'rule']);
}

237
yarn.lock
View file

@ -114,9 +114,9 @@
version "4.14.106"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd"
"@types/long@^3.0.32":
version "3.0.32"
resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69"
"@types/long@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef"
"@types/mocha@^5.0.0":
version "5.0.0"
@ -126,14 +126,14 @@
version "9.6.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-9.6.1.tgz#e2d374ef15b315b48e7efc308fa1a7cd51faa06c"
"@types/node@^10.1.0":
version "10.10.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.10.1.tgz#d5c96ca246a418404914d180b7fdd625ad18eca6"
"@types/node@^8.0.24":
version "8.9.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.9.4.tgz#dfd327582a06c114eb6e0441fa3d6fab35edad48"
"@types/node@^8.9.4":
version "8.10.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.12.tgz#dcb66f6de39074a296534bd1a256a3c6a1c8f5b5"
"@types/qs@^6.5.1":
version "6.5.1"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.5.1.tgz#a38f69c62528d56ba7bd1f91335a8004988d72f7"
@ -645,6 +645,10 @@ aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
aws4@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
babel-code-frame@6.26.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@ -1529,7 +1533,7 @@ colors@~0.6.0-1:
version "0.6.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc"
combined-stream@1.0.6:
combined-stream@1.0.6, combined-stream@~1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
@ -2334,13 +2338,6 @@ duplexify@^3.4.2, duplexify@^3.5.3:
readable-stream "^2.0.0"
stream-shift "^1.0.0"
each-async@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/each-async/-/each-async-1.1.1.tgz#dee5229bdf0ab6ba2012a395e1b869abf8813473"
dependencies:
onetime "^1.0.0"
set-immediate-shim "^1.0.0"
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
@ -2974,6 +2971,10 @@ extend@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4"
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
external-editor@^2.0.4:
version "2.1.0"
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
@ -3242,7 +3243,7 @@ forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
form-data@^2.3.2:
form-data@^2.3.2, form-data@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
@ -3787,13 +3788,9 @@ grunt-legacy-util@~1.0.0:
underscore.string "~3.2.3"
which "~1.2.1"
grunt-sass@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/grunt-sass/-/grunt-sass-2.0.0.tgz#9074cf9d7b4592e20f7788caa727b8f9aa06b60a"
dependencies:
each-async "^1.0.0"
node-sass "^4.0.0"
object-assign "^4.0.1"
grunt-sass@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/grunt-sass/-/grunt-sass-3.0.1.tgz#2760207d7b78db84429d9fa77d22289a6fc903a0"
grunt@^1.0.1:
version "1.0.1"
@ -3858,6 +3855,13 @@ har-validator@~5.0.3:
ajv "^5.1.0"
har-schema "^2.0.0"
har-validator@~5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
dependencies:
ajv "^5.3.0"
har-schema "^2.0.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@ -4741,8 +4745,8 @@ jquery@^3.3.1:
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
js-base64@^2.1.8:
version "2.1.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
version "2.4.9"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
js-base64@^2.1.9:
version "2.4.3"
@ -5097,8 +5101,8 @@ lodash.memoize@^4.1.2:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
lodash.uniq@^4.5.0:
version "4.5.0"
@ -5367,6 +5371,10 @@ mime-db@~1.30.0:
version "1.30.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
mime-db@~1.36.0:
version "1.36.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397"
mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.7:
version "2.1.15"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
@ -5385,6 +5393,12 @@ mime-types@~2.1.18:
dependencies:
mime-db "~1.33.0"
mime-types@~2.1.19:
version "2.1.20"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19"
dependencies:
mime-db "~1.36.0"
mime@1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
@ -5591,7 +5605,7 @@ mz@^2.3.1:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nan@^2.0.0, nan@^2.3.2, nan@^2.3.3:
nan@^2.0.0, nan@^2.3.3:
version "2.6.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
@ -5677,19 +5691,18 @@ node-forge@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300"
node-gyp@^3.3.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.1.tgz#19561067ff185464aded478212681f47fd578cbc"
node-gyp@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
dependencies:
fstream "^1.0.0"
glob "^7.0.3"
graceful-fs "^4.1.2"
minimatch "^3.0.2"
mkdirp "^0.5.0"
nopt "2 || 3"
npmlog "0 || 1 || 2 || 3 || 4"
osenv "0"
request "2"
request "^2.87.0"
rimraf "2"
semver "~5.3.0"
tar "^2.0.0"
@ -5760,9 +5773,9 @@ node-sass-import-once@^1.2.0:
dependencies:
js-yaml "^3.2.7"
node-sass@^4.0.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.2.tgz#4012fa2bd129b1d6365117e88d9da0500d99da64"
node-sass@^4.9.3:
version "4.9.3"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.3.tgz#f407cf3d66f78308bb1e346b24fa428703196224"
dependencies:
async-foreach "^0.1.3"
chalk "^1.1.1"
@ -5776,12 +5789,13 @@ node-sass@^4.0.0:
lodash.mergewith "^4.6.0"
meow "^3.7.0"
mkdirp "^0.5.1"
nan "^2.3.2"
node-gyp "^3.3.1"
nan "^2.10.0"
node-gyp "^3.8.0"
npmlog "^4.0.0"
request "^2.79.0"
sass-graph "^2.1.1"
request "2.87.0"
sass-graph "^2.2.4"
stdout-stream "^1.4.0"
"true-case-path" "^1.0.2"
nodesecurity-npm-utils@^6.0.0:
version "6.0.0"
@ -5863,7 +5877,7 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0:
"npmlog@0 || 1 || 2 || 3 || 4":
version "4.0.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f"
dependencies:
@ -5872,7 +5886,7 @@ npm-run-path@^2.0.0:
gauge "~2.7.1"
set-blocking "~2.0.0"
npmlog@^4.0.2:
npmlog@^4.0.0, npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
dependencies:
@ -5951,6 +5965,10 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@ -6010,10 +6028,6 @@ once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
dependencies:
wrappy "1"
onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
onetime@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
@ -6787,9 +6801,9 @@ prop-types@^15.5.10, prop-types@^15.6.0, prop-types@^15.6.1:
loose-envify "^1.3.1"
object-assign "^4.1.1"
protobufjs@^6.8.6:
version "6.8.6"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.6.tgz#ce3cf4fff9625b62966c455fc4c15e4331a11ca2"
protobufjs@~6.8.6:
version "6.8.8"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c"
dependencies:
"@protobufjs/aspromise" "^1.1.2"
"@protobufjs/base64" "^1.1.2"
@ -6801,8 +6815,8 @@ protobufjs@^6.8.6:
"@protobufjs/path" "^1.1.2"
"@protobufjs/pool" "^1.1.0"
"@protobufjs/utf8" "^1.1.0"
"@types/long" "^3.0.32"
"@types/node" "^8.9.4"
"@types/long" "^4.0.0"
"@types/node" "^10.1.0"
long "^4.0.0"
proxy-addr@~2.0.3:
@ -6833,6 +6847,10 @@ pseudomap@^1.0.1, pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
psl@^1.1.24:
version "1.1.29"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
public-encrypt@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
@ -6898,6 +6916,10 @@ qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
query-string@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
@ -7454,7 +7476,7 @@ request-progress@^2.0.1:
dependencies:
throttleit "^1.0.0"
request@2, request@2.81.0, request@^2.45.0, request@^2.65.0, request@^2.79.0:
request@2.81.0, request@^2.45.0, request@^2.65.0, request@^2.79.0:
version "2.81.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
dependencies:
@ -7481,6 +7503,31 @@ request@2, request@2.81.0, request@^2.45.0, request@^2.65.0, request@^2.79.0:
tunnel-agent "^0.6.0"
uuid "^3.0.0"
request@2.87.0:
version "2.87.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.6.0"
caseless "~0.12.0"
combined-stream "~1.0.5"
extend "~3.0.1"
forever-agent "~0.6.1"
form-data "~2.3.1"
har-validator "~5.0.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.17"
oauth-sign "~0.8.2"
performance-now "^2.1.0"
qs "~6.5.1"
safe-buffer "^5.1.1"
tough-cookie "~2.3.3"
tunnel-agent "^0.6.0"
uuid "^3.1.0"
request@^2.81.0, request@~2.83.0:
version "2.83.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
@ -7508,6 +7555,31 @@ request@^2.81.0, request@~2.83.0:
tunnel-agent "^0.6.0"
uuid "^3.1.0"
request@^2.87.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.0"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.4.3"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@ -7680,14 +7752,14 @@ sanitize-filename@^1.6.1:
dependencies:
truncate-utf8-bytes "^1.0.0"
sass-graph@^2.1.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.2.tgz#f4d6c95b546ea2a09d14176d0fc1a07ee2b48354"
sass-graph@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49"
dependencies:
glob "^7.0.0"
lodash "^4.0.0"
scss-tokenizer "^0.2.1"
yargs "^6.6.0"
scss-tokenizer "^0.2.3"
yargs "^7.0.0"
sax@>=0.6.0:
version "1.2.1"
@ -7704,9 +7776,9 @@ schema-utils@^0.4.2, schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
scss-tokenizer@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.1.tgz#07c0cc577bb7ab4d08fd900185adbf4bc844141d"
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
dependencies:
js-base64 "^2.1.8"
source-map "^0.4.2"
@ -7790,7 +7862,7 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
set-immediate-shim@^1.0.0, set-immediate-shim@^1.0.1:
set-immediate-shim@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
@ -8181,8 +8253,8 @@ statuses@1:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
stdout-stream@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b"
version "1.4.1"
resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
dependencies:
readable-stream "^2.0.1"
@ -8644,6 +8716,13 @@ tough-cookie@~2.3.3:
dependencies:
punycode "^1.4.1"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
dependencies:
psl "^1.1.24"
punycode "^1.4.1"
"traverse@>=0.3.0 <0.4":
version "0.3.9"
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
@ -8668,6 +8747,12 @@ trough@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.1.tgz#a9fd8b0394b0ae8fff82e0633a0a36ccad5b5f86"
"true-case-path@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
dependencies:
glob "^7.1.2"
truncate-utf8-bytes@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
@ -9468,6 +9553,12 @@ yargs-parser@^4.2.0:
dependencies:
camelcase "^3.0.0"
yargs-parser@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
dependencies:
camelcase "^3.0.0"
yargs-parser@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
@ -9486,7 +9577,7 @@ yargs-parser@^9.0.2:
dependencies:
camelcase "^4.1.0"
yargs@6.6.0, yargs@^6.5.0, yargs@^6.6.0:
yargs@6.6.0, yargs@^6.5.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
dependencies:
@ -9538,6 +9629,24 @@ yargs@^11.0.0:
y18n "^3.2.1"
yargs-parser "^9.0.2"
yargs@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
dependencies:
camelcase "^3.0.0"
cliui "^3.2.0"
decamelize "^1.1.1"
get-caller-file "^1.0.1"
os-locale "^1.4.0"
read-pkg-up "^1.0.1"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^1.0.2"
which-module "^1.0.0"
y18n "^3.2.1"
yargs-parser "^5.0.0"
yargs@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c"