.extension` on VS Code MarketPlace
10 |
11 | The `vsce package` will also run npm scripts before compiling the .vsix file: `check` (eslint + prettier) and `webpack` (dist files) if either fail, `vsce` doesn't report what failed so you will need to re-run these checks manually. Otherwise it will generate a (zip) `.vsix` file that can be manually installed using `code --install-extension vscode-vuln-cost-1.0.0.vsix`
12 |
13 | To release, ensure the correct type is used in the `vsce publish` - see [Auto-incrementing the extension version](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#autoincrementing-the-extension-version) for details.
14 |
15 | ## Developing
16 |
17 | Using VS Code you need to build and watch for changes using `npm run dev`, then launch the extension in the debugger from VS Code (open command palette, "Debug: select and start debugging", "Launch Extension").
18 |
19 | Useful development links
20 |
21 | - [Colour definitions](https://code.visualstudio.com/api/references/theme-color)
22 | - [Render options for the decorations](https://code.visualstudio.com/api/references/vscode-api#ThemableDecorationAttachmentRenderOptions)
23 |
--------------------------------------------------------------------------------
/test/report.test.js:
--------------------------------------------------------------------------------
1 | import report, { summary } from "../src/report"
2 |
3 | describe('summary', () => {
4 | it('should return a well formatted string', () => {
5 | expect(summary({vulns: {
6 | vulnerabilities: [
7 | {
8 | severity: 'high',
9 | },
10 | {
11 | severity: 'medium',
12 | },
13 | {
14 | severity: 'low',
15 | }
16 | ]
17 | }})).toBe('1 high, 1 medium, 1 low');
18 | })
19 |
20 | it.each([['critical'], ['high'], ['medium'], ['low']])(
21 | 'supports %s severity',
22 | severity => {
23 | expect(
24 | summary({
25 | vulns: {
26 | vulnerabilities: [
27 | {
28 | severity,
29 | },
30 | ],
31 | },
32 | })
33 | ).toBe(`1 ${severity}`);
34 | }
35 | );
36 | })
37 |
38 | describe('report', () => {
39 | it('should return a well formatted report', () => {
40 |
41 | expect(
42 | report('goof@1.0.1', {
43 | vulnerabilities: [
44 | {
45 | id: 'UNIQ-VULN-ID',
46 | title: 'my-first-pkg',
47 | severity: 'low',
48 | upgradePath: [],
49 | from: ['loaded-via-pkg'],
50 | },
51 | ],
52 | })
53 | ).toMatchSnapshot();
54 | })
55 |
56 | it.each([['critical'], ['high'], ['medium'], ['low']])(
57 | 'supports %s severity',
58 | severity => {
59 | expect(
60 | summary({
61 | vulns: {
62 | vulnerabilities: [
63 | {
64 | severity,
65 | },
66 | ],
67 | },
68 | })
69 | ).toContain(`${severity}`);
70 | }
71 | );
72 | })
73 |
--------------------------------------------------------------------------------
/.snyk:
--------------------------------------------------------------------------------
1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
2 | version: v1.14.1
3 | ignore: {}
4 | # patches apply the minimum changes required to fix a vulnerability
5 | patch:
6 | SNYK-JS-LODASH-567746:
7 | - '@babel/traverse > lodash':
8 | patched: '2020-05-01T05:50:53.898Z'
9 | - '@babel/traverse > @babel/generator > lodash':
10 | patched: '2020-05-01T05:50:53.898Z'
11 | - '@babel/traverse > @babel/helper-split-export-declaration > @babel/types > lodash':
12 | patched: '2020-05-01T05:50:53.898Z'
13 | - '@babel/traverse > @babel/helper-function-name > @babel/template > @babel/types > lodash':
14 | patched: '2020-05-01T05:50:53.898Z'
15 | - '@babel/types > lodash':
16 | patched: '2020-05-07T09:02:29.251Z'
17 | - '@babel/traverse > lodash':
18 | patched: '2020-05-07T09:02:29.251Z'
19 | - '@babel/traverse > @babel/types > lodash':
20 | patched: '2020-05-07T09:02:29.251Z'
21 | - '@babel/traverse > @babel/generator > lodash':
22 | patched: '2020-05-07T09:02:29.251Z'
23 | - '@babel/traverse > @babel/generator > @babel/types > lodash':
24 | patched: '2020-05-07T09:02:29.251Z'
25 | - '@babel/traverse > @babel/helper-function-name > @babel/types > lodash':
26 | patched: '2020-05-07T09:02:29.251Z'
27 | - '@babel/traverse > @babel/helper-split-export-declaration > @babel/types > lodash':
28 | patched: '2020-05-07T09:02:29.251Z'
29 | - '@babel/traverse > @babel/helper-function-name > @babel/helper-get-function-arity > @babel/types > lodash':
30 | patched: '2020-05-07T09:02:29.251Z'
31 | - '@babel/traverse > @babel/helper-function-name > @babel/template > @babel/types > lodash':
32 | patched: '2020-05-07T09:02:29.251Z'
33 |
--------------------------------------------------------------------------------
/test/fixtures/st/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "async-cache": {
6 | "version": "0.1.5",
7 | "resolved": "https://registry.npmjs.org/async-cache/-/async-cache-0.1.5.tgz",
8 | "integrity": "sha1-t805bSlaqMUoKbvjDsM7YkJgBto=",
9 | "requires": {
10 | "lru-cache": "~2.3"
11 | }
12 | },
13 | "fd": {
14 | "version": "0.0.3",
15 | "resolved": "https://registry.npmjs.org/fd/-/fd-0.0.3.tgz",
16 | "integrity": "sha512-iAHrIslQb3U68OcMSP0kkNWabp7sSN6d2TBSb2JO3gcLJVDd4owr/hKM4SFJovFOUeeXeItjYgouEDTMWiVAnA=="
17 | },
18 | "graceful-fs": {
19 | "version": "1.2.3",
20 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz",
21 | "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=",
22 | "optional": true
23 | },
24 | "lru-cache": {
25 | "version": "2.3.1",
26 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.3.1.tgz",
27 | "integrity": "sha1-s632s9hW6VTiw5DmzvIggSRaU9Y="
28 | },
29 | "mime": {
30 | "version": "1.2.11",
31 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz",
32 | "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA="
33 | },
34 | "negotiator": {
35 | "version": "0.2.8",
36 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.2.8.tgz",
37 | "integrity": "sha1-rf0gejh1xNNwlXKcLnwoPFui7nI="
38 | },
39 | "st": {
40 | "version": "0.2.4",
41 | "resolved": "https://registry.npmjs.org/st/-/st-0.2.4.tgz",
42 | "integrity": "sha1-lzGPVUhf/L5whuIrQNYXWJI8/6A=",
43 | "requires": {
44 | "async-cache": "~0.1.2",
45 | "fd": "~0.0.2",
46 | "graceful-fs": "~1.2",
47 | "mime": "~1.2.7",
48 | "negotiator": "~0.2.5"
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/diagnostics.js:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 | import { isAuthed } from './getImports/snykAPI';
3 | import { summary } from './report';
4 |
5 | export const KEY_MENTION = 'snyk_vulns';
6 |
7 | /**
8 | * Analyzes the text document for problems.
9 | * This demo diagnostic problem provider finds all mentions of 'emoji'.
10 | * @param doc text document to analyze
11 | * @param diagnosticCollection diagnostic collection
12 | */
13 | export function refreshDiagnostics(doc, diagnosticCollection, packages) {
14 | let diagnostics = [];
15 |
16 | for (let i = 0; i < packages.length; i++) {
17 | const pkg = packages[i];
18 | if (pkg.vulns && pkg.vulns.count > 0) {
19 | diagnostics.push(createDiagnostic(doc, pkg));
20 | }
21 | }
22 |
23 | diagnosticCollection.set(doc.uri, diagnostics);
24 | }
25 |
26 | export function getMessage(pkg) {
27 | // SUPER IMPORTANT: this string is used by the Action Provider as there's no way
28 | // to pass around meta data, so the package name (and version) that represents
29 | // a key to the cache is lifted from this string.
30 | return `⛔ ${pkg.vulns.packageName} has ${pkg.vulns.count} vulns`;
31 | }
32 |
33 | export function getPackageFromMessage(message) {
34 | return message.replace(/^⛔\s+/g, '').split(' ')[0];
35 | }
36 |
37 | function createDiagnostic(doc, pkg) {
38 | // create range that represents, where in the document the word is
39 | let range = new vscode.Range(
40 | pkg.loc.start.line - 1,
41 | pkg.loc.start.column,
42 | pkg.loc.end.line - 1,
43 | pkg.loc.end.column
44 | );
45 |
46 | let message = getMessage(pkg);
47 |
48 | if (!isAuthed()) {
49 | message += '\nConnect your project to Snyk to find and fix vulnerabilities';
50 | } else {
51 | message = message.replace(/\b\d+ vulns$/, summary(pkg) + ' vulns');
52 | }
53 |
54 | let diagnostic = new vscode.Diagnostic(
55 | range,
56 | message,
57 | vscode.DiagnosticSeverity.Warning
58 | );
59 |
60 | diagnostic.code = KEY_MENTION;
61 | return diagnostic;
62 | }
63 |
--------------------------------------------------------------------------------
/src/getImports/testVuln.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import logger from '../logger';
3 | import statistics from '../statistics';
4 | import utm from '../utm';
5 | import { getToken, isAuthed } from './snykAPI';
6 |
7 | function testNoAuth(key) {
8 | return axios
9 | .get(`https://snyk.io/test/npm/${key}?${utm}&type=json`)
10 | .then(({ data }) => {
11 | if (typeof data === 'string') {
12 | // bug on snyk's side, returning a string for 404
13 | logger.log(`bad return on ${key}`);
14 | throw new Error('bad return from snyk api (unauthed)');
15 | }
16 |
17 | return {
18 | ok: data.totalVulns === 0,
19 | packageName: data.resultTitle,
20 | count: data.totalVulns,
21 | };
22 | });
23 | }
24 |
25 | function testWithAuth({ name, version }) {
26 | const encodedName = encodeURIComponent(name);
27 | const url = `https://snyk.io/api/v1/test/npm/${encodedName}/${version}?${utm}`;
28 |
29 | return axios
30 | .get(url, {
31 | headers: {
32 | 'x-is-ci': false,
33 | authorization: `token ${getToken()}`,
34 | },
35 | })
36 | .then(res => {
37 | const vulnerabilities = res.data.issues.vulnerabilities || [];
38 | const uniqBasedOnId = new Set();
39 | vulnerabilities.forEach(v => uniqBasedOnId.add(v.id));
40 | const fixable = vulnerabilities.some(({ isUpgradable }) => isUpgradable);
41 |
42 | return {
43 | vulnerabilities,
44 | packageName: `${name}@${version}`,
45 | count: uniqBasedOnId.size,
46 | fixable,
47 | };
48 | })
49 | .catch(e => {
50 | logger.log(`${url} failed with ${e.message}`);
51 | throw e;
52 | });
53 | }
54 |
55 | export default function test(pkg) {
56 | logger.log(`testing ${pkg.name}@${pkg.version}`);
57 | statistics.sendTest(`${pkg.name}@${pkg.version} - authed: ${isAuthed()}`);
58 | if (isAuthed()) {
59 | return testWithAuth(pkg);
60 | } else {
61 | return testNoAuth(`${pkg.name}/${pkg.version}`);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Reporting Security Issues
2 |
3 | We at Snyk value the security community and believe that responsible disclosure of security vulnerabilities in open source packages helps us ensure the security and privacy of the users.
4 |
5 | If you believe you have found a security vulnerability on Snyk, we encourage you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem. Before reporting though, please review our responsible disclosure policy, and those things that should not be reported.
6 |
7 | Submit your report to security@snyk.io (one issue per report) and respond to the report with any updates. Please do not contact employees directly or through other channels about a report.
8 |
9 | Report security bugs in third-party modules to the person or team maintaining the module. You can also report a vulnerability through our [Snyk Vulnerability Disclosure](https://docs.google.com/a/snyk.io/forms/d/e/1FAIpQLSemwgWZ0JgK1ZULKhy9DZCQ5KulbLEldvmokAuRtt-_nrqNlA/viewform) program.
10 |
11 | ### Responsible Disclosure Policy
12 |
13 | We ask that:
14 |
15 | - You give us reasonable time to investigate and mitigate an issue you report before making public any information about the report or sharing such information with others.
16 | - You do not interact with an individual account (which includes modifying or accessing data from the account) if the account owner has not consented to such actions.
17 | - You make a good faith effort to avoid privacy violations and disruptions to others, including (but not limited to) destruction of data and interruption or degradation of our services.
18 | - You do not exploit a security issue you discover for any reason. (This includes demonstrating additional risk, such as attempted compromise of sensitive company data or probing for additional issues).
19 | - You do not violate any other applicable laws or regulations.
20 |
21 | Find out more about our [security policy](https://snyk.io/docs/security) and [Bug Bounty program](https://snyk.io/docs/security#snyk-s-vulnerability-disclosure-program)
22 |
--------------------------------------------------------------------------------
/test/getImports/htmlParser.test.js:
--------------------------------------------------------------------------------
1 | import { getPackages } from '../../src/getImports/htmlParser';
2 |
3 | const testScenarios = [
4 | {
5 | name: 'jQuery',
6 | url: 'https://code.jquery.com/jquery-3.3.1.min.js',
7 | package: 'jquery',
8 | version: '3.3.1',
9 | },
10 | {
11 | name: 'ASP net CDN',
12 | url: 'https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.2.1.min.js',
13 | package: 'jquery',
14 | version: '3.2.1',
15 | },
16 | {
17 | name: 'Stackpath',
18 | url:
19 | 'https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js',
20 | package: 'bootstrap',
21 | version: '4.4.1',
22 | },
23 | {
24 | name: 'MaxCDN',
25 | url: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js',
26 | package: 'bootstrap',
27 | version: '3.3.7',
28 | },
29 | {
30 | name: 'yandex',
31 | url: 'https://yastatic.net/jquery/3.3.1/jquery.min.js',
32 | package: 'jquery',
33 | version: '3.3.1',
34 | },
35 | {
36 | name: 'unpkg.com',
37 | url: 'https://unpkg.com/jquery@3.3.1/dist/jquery.js',
38 | package: 'jquery',
39 | version: '3.3.1',
40 | },
41 | {
42 | name: 'jsDelivr – npm',
43 | url: 'https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js',
44 | package: 'jquery',
45 | version: '3.2.1',
46 | },
47 | {
48 | name: 'jsDelivr - npm shorthand',
49 | url: 'https://cdn.jsdelivr.net/npm/jquery@3.2',
50 | package: 'jquery',
51 | version: '3.2',
52 | },
53 | {
54 | name: 'jsDelivr - github',
55 | url: 'https://cdn.jsdelivr.net/gh/jquery/jquery@3.2.1/dist/jquery.min.js',
56 | package: 'jquery',
57 | version: '3.2.1',
58 | },
59 | {
60 | name: 'CDNjs',
61 | url: 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js',
62 | package: 'jquery',
63 | version: '3.4.1',
64 | }
65 | ];
66 |
67 | testScenarios.map(scenario => {
68 | test(`Extract ${scenario.name} package and version`, () => {
69 | const packages = getPackages(
70 | 'foo',
71 | ``
72 | );
73 | expect(packages[0].name).toBe(scenario.package);
74 | expect(packages[0].version).toBe(scenario.version);
75 | });
76 | });
77 |
--------------------------------------------------------------------------------
/src/getImports/index.js:
--------------------------------------------------------------------------------
1 | import {
2 | getPackages,
3 | TYPESCRIPT as TYPESCRIPT_LANG,
4 | JAVASCRIPT as JAVASCRIPT_LANG,
5 | HTML as HTML_LANG,
6 | PJSON as PJSON_LANG,
7 | } from './parser';
8 | import nativePackages from './native';
9 | import {
10 | getPackageInfo,
11 | clearPackageCache as _clearPackageCache,
12 | } from './packageInfo';
13 | import { EventEmitter } from 'events';
14 | import finder from 'find-package-json';
15 | import validate from 'validate-npm-package-name';
16 |
17 | export const TYPESCRIPT = TYPESCRIPT_LANG;
18 | export const JAVASCRIPT = JAVASCRIPT_LANG;
19 | export const HTML = HTML_LANG;
20 | export const PJSON = PJSON_LANG;
21 | export const clearPackageCache = _clearPackageCache; // this is weird…
22 |
23 | export function getImports(fileName, text, language) {
24 | const emitter = new EventEmitter();
25 | setTimeout(async () => {
26 | try {
27 | emitter.emit('package', finder(fileName).next().filename);
28 | } catch (e) {
29 | // noop
30 | }
31 | try {
32 | const imports = getPackages(fileName, text, language).filter(info => {
33 | if (nativePackages.includes(info.name.toLowerCase())) {
34 | return false;
35 | }
36 |
37 | if (info.name.startsWith('.')) {
38 | return false;
39 | }
40 |
41 | if (info.name.startsWith('~')) {
42 | return false;
43 | }
44 |
45 | if (info.name.trim() == '') {
46 | return false;
47 | }
48 |
49 | if (info.name.includes('/') && !info.name.startsWith('@')) {
50 | // mutating…
51 | info.name = info.name.split('/').shift();
52 | }
53 |
54 | const valid = validate(info.name);
55 |
56 | if (valid.errors) {
57 | // invalid package name, so isn't real, so we'll bail
58 | return false;
59 | }
60 |
61 | return true;
62 | });
63 | emitter.emit('start', imports);
64 | const promises = imports
65 | .map(packageInfo => getPackageInfo(packageInfo))
66 | .map(promise =>
67 | promise.then(packageInfo => {
68 | emitter.emit('calculated', packageInfo);
69 | return packageInfo;
70 | })
71 | );
72 | const packages = await Promise.all(promises);
73 | emitter.emit('done', packages);
74 | } catch (e) {
75 | console.log(e);
76 | emitter.emit('error', e);
77 | }
78 | }, 0);
79 | return emitter;
80 | }
81 |
--------------------------------------------------------------------------------
/src/statistics.js:
--------------------------------------------------------------------------------
1 | import { v4 as uuidv4 } from 'uuid';
2 | import logger from './logger';
3 | import axios from 'axios';
4 | import * as vscode from 'vscode';
5 |
6 | const STATS_API_ROOT = 'https://us-central1-snyk-vulncost.cloudfunctions.net/app/';
7 | const meta = 'vulncost-analytics';
8 | const FIRST_STARTUP = 'FIRST_STARTUP';
9 | const STARTUP = 'STARTUP';
10 | const TEST = 'TEST';
11 |
12 | const extensionInfo = vscode.extensions.getExtension('snyk-security.vscode-vuln-cost').packageJSON
13 |
14 | const instance = axios.create({
15 | baseURL: STATS_API_ROOT,
16 | timeout: 5000
17 | });
18 |
19 | class Statistics {
20 |
21 | init(context) {
22 | this.context = context;
23 |
24 | this.userid = context.globalState.get('userid');
25 | logger.log('userid found: ' + this.userid);
26 |
27 | if (!this.userid) {
28 | this.userid = uuidv4();
29 | logger.log('created userid: ' + this.userid);
30 | context.globalState.update('userid', this.userid)
31 | }
32 | }
33 |
34 | ping() {
35 | logger.log('try to ping');
36 | instance.get('ping')
37 | .then(response => { logger.log('pinged statistics server: ' + response.data); })
38 | .catch(error => { logger.log(error); });
39 | }
40 |
41 | sendTest(comment) {
42 | //max send once every 2 minutes
43 | const now = new Date().getTime();
44 | if (!this.lastTestSend || now - this.lastTestSend >= 120000) {
45 | this.send(TEST, comment);
46 | this.lastTestSend = now;
47 | }
48 | }
49 |
50 | sendStartup(comment) {
51 | this.firstStartup = this.context.globalState.get('firstStartup');
52 | if (!this.firstStartup) {
53 | this.send(FIRST_STARTUP, comment);
54 | this.context.globalState.update('firstStartup', new Date().getTime());
55 | } else {
56 | this.send(STARTUP, comment);
57 | }
58 | }
59 |
60 | send(action, comment) {
61 | const configuration = vscode.workspace.getConfiguration('vulnCost');
62 |
63 | if (configuration.sendStatistics) {
64 | logger.log(`sending stats action:[${action}], comment:[${comment}]`);
65 |
66 | const message = {
67 | "meta": meta,
68 | "time": new Date().getTime(),
69 | "item": {
70 | "user": this.userid,
71 | "app": extensionInfo.name + ' ' + extensionInfo.version,
72 | "action": action,
73 | "comment": comment
74 | }
75 | };
76 |
77 | instance.post('api/log', message)
78 | .then(response => {
79 | logger.log(`response: ${response.status} on message ${message.action} - ${message.comment}`);
80 | })
81 | .catch(error => {
82 | logger.log(`error: ${error} on message ${message.action} - ${message.comment}`);
83 | });
84 | }
85 | }
86 | }
87 |
88 | export default new Statistics();
89 |
90 |
--------------------------------------------------------------------------------
/src/SnykAction.js:
--------------------------------------------------------------------------------
1 | import * as vscode from 'vscode';
2 | import { isAuthed } from './getImports/snykAPI';
3 | import { getPackageFromCache } from './getImports/packageInfo';
4 | import { KEY_MENTION, getPackageFromMessage } from './diagnostics';
5 |
6 | function createSimpleAction({
7 | isPreferred = true,
8 | diagnostic,
9 | actionTitle,
10 | command,
11 | args = [],
12 | }) {
13 | const action = new vscode.CodeAction(
14 | actionTitle,
15 | vscode.CodeActionKind.QuickFix
16 | );
17 |
18 | action.command = {
19 | command,
20 | title: actionTitle,
21 | arguments: args,
22 | };
23 | action.diagnostics = [diagnostic];
24 | action.isPreferred = isPreferred;
25 | return action;
26 | }
27 |
28 | function createAuthAction(args) {
29 | return createSimpleAction({ command: 'vulnCost.signIn', ...args });
30 | }
31 |
32 | function createShowOutputAction(args) {
33 | return createSimpleAction({ command: 'vulnCost.showOutput', ...args });
34 | }
35 |
36 | function createOpenVulnPageAction(args) {
37 | return createSimpleAction({ command: 'vulnCost.openVulnPage', ...args });
38 | }
39 |
40 | /**
41 | * Provides code actions corresponding to diagnostic problems.
42 | */
43 | export class SnykVulnInfo {
44 | provideCodeActions(document, range, context) {
45 | // for each diagnostic entry that has the matching `code`, create a code action command
46 | return context.diagnostics
47 | .filter(diagnostic => diagnostic.code === KEY_MENTION)
48 | .map(diagnostic => {
49 | const pkg = getPackageFromMessage(diagnostic.message);
50 | const vulns = getPackageFromCache(pkg);
51 | const count = vulns.count;
52 | const res = [];
53 |
54 | if (isAuthed()) {
55 | res.push(
56 | createShowOutputAction({
57 | diagnostic,
58 | actionTitle: `Fix vuln${count === 1 ? '' : 's'}`,
59 | title: 'Show vulnerability details',
60 | args: [vulns],
61 | isPreferred: true,
62 | })
63 | );
64 | } else {
65 | res.push(
66 | createAuthAction({
67 | diagnostic,
68 | actionTitle: `Fix vuln${count === 1 ? '' : 's'}`,
69 | title: 'Connect to Snyk',
70 | isPreferred: true,
71 | })
72 | );
73 | }
74 |
75 | res.push(
76 | createOpenVulnPageAction({
77 | diagnostic,
78 | actionTitle: 'Learn about this package',
79 | title: 'Learn about this package',
80 | args: [pkg],
81 | isPreferred: true,
82 | })
83 | );
84 |
85 | // if (vulns.fixable && isAuthed) {
86 | // res.push(
87 | // createPackageUpgradeAction({
88 | // ...vulns,
89 | // diagnostic,
90 | // isPreferred: true,
91 | // })
92 | // );
93 | // }
94 |
95 | return res;
96 | })
97 | .flat();
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/report.js:
--------------------------------------------------------------------------------
1 | const sort = ['critical', 'high', 'medium', 'low'];
2 | const sortBy = (a, b) => {
3 | return sort.indexOf(a.severity) - sort.indexOf(b.severity);
4 | };
5 | const ucFirst = _ => `${_[0].toUpperCase()}${_.slice(1)}`;
6 |
7 | /**
8 | *
9 | * @param {object} pkg
10 | * @returns {string} User formatted summary
11 | */
12 | export const summary = pkg => {
13 | const message = pkg.vulns.vulnerabilities
14 | .reduce(
15 | (acc, curr) => {
16 | acc[sort.indexOf(curr.severity)]++;
17 | return acc;
18 | },
19 | [0, 0, 0, 0] // relies on the order of critical, high, medium, low (based on `sort`)
20 | )
21 | .map((_, i) => `${_} ${sort[i]}`) // remaps to include the severit type
22 | .filter(_ => _[0] !== '0') // drop those starting with zero
23 | .join(', ');
24 |
25 | return message;
26 | };
27 |
28 | /**
29 | *
30 | * @param {string} tested Tested module in format package@version
31 | * @param {data} data Vulnerability data via test/api
32 | * @returns {string} User formatted report
33 | */
34 | export default function report(tested, data) {
35 | const dedupe = [];
36 |
37 | if (!data.vulnerabilities) {
38 | return '';
39 | }
40 |
41 | const res = data.vulnerabilities
42 | .map(({ severity, upgradePath, title, from, id }) => {
43 | return {
44 | id,
45 | severity,
46 | upgradePath,
47 | remediation: upgradePath[0],
48 | title,
49 | from: from.pop(),
50 | direct: upgradePath.length === 1,
51 | };
52 | })
53 | .filter(({ id }) => {
54 | if (!dedupe.includes(id)) {
55 | dedupe.push(id);
56 | return true;
57 | }
58 | });
59 |
60 | const direct = res
61 | .filter(_ => _.direct)
62 | .sort()
63 | .sort(sortBy);
64 | const indirect = res.filter(_ => _.direct === false).sort(sortBy);
65 |
66 | const remediation = res
67 | .map(_ => _.remediation)
68 | .sort()
69 | .pop();
70 |
71 | const lines = ['', `=== ${tested} ===`, ''];
72 |
73 | // NOTE: agreed that UTM doesn't go on visable links (in the output panel)
74 | if (direct.length) {
75 | lines.push(
76 | 'Direct:',
77 | direct
78 | .map(
79 | _ =>
80 | `${ucFirst(_.severity)} ${_.title}\n- https://snyk.io/vuln/${_.id}`
81 | )
82 | .join('\n'),
83 | ''
84 | );
85 | }
86 |
87 | if (indirect.length) {
88 | lines.push(
89 | 'Indirect:',
90 | indirect
91 | .map(
92 | _ =>
93 | `${ucFirst(_.severity)} ${_.title} in ${
94 | _.from
95 | }\n- https://snyk.io/vuln/${_.id}`
96 | )
97 | .join('\n'),
98 | ''
99 | );
100 | }
101 |
102 | if (remediation) {
103 | lines.push('Possible remediation:', remediation);
104 | } else {
105 | lines.push('No remediation available.');
106 | }
107 |
108 | return lines.join('\n');
109 | }
110 |
111 | // report('webpack@4.39.0', require('../../test/fixtures/webpack.json')) // ?
112 |
--------------------------------------------------------------------------------
/src/decorator.js:
--------------------------------------------------------------------------------
1 | import { workspace, window, Range, Position, ThemeColor } from 'vscode';
2 | import logger from './logger';
3 |
4 | const decorations = {};
5 | let shown = {};
6 |
7 | export function flushDecorations(fileName, packages, displayCheck = false) {
8 | // logger.log(`Flushing decorations`);
9 | decorations[fileName] = {};
10 | packages.forEach(packageInfo => {
11 | if (packageInfo.vulns === undefined) {
12 | const configuration = workspace.getConfiguration('vulnCost');
13 | if (configuration.showDecoration) {
14 | decorate('Scanning for vulns...', packageInfo);
15 | }
16 | } else {
17 | calculated(packageInfo, displayCheck);
18 | }
19 | });
20 | refreshDecorations(fileName);
21 | }
22 |
23 | export function calculated(packageInfo, displayCheck = false) {
24 | const decorationMessage = getDecorationMessage(packageInfo);
25 |
26 | if (displayCheck && decorationMessage.length === 0 && (shown[packageInfo.string+packageInfo.fileName] === undefined || !shown[packageInfo.string+packageInfo.fileName])) {
27 | shown[packageInfo.string+packageInfo.fileName] = true;
28 | decorate("✔️", packageInfo);
29 | setTimeout(() => {decorate(decorationMessage, packageInfo);}, 1000);
30 | } else {
31 | decorate(decorationMessage, packageInfo);
32 | }
33 | }
34 |
35 | export function clearShown() {
36 | shown = {};
37 | }
38 |
39 |
40 | function getDecorationMessage(packageInfo) {
41 | logger.log(
42 | `getDecorationMessage - has object? ${!!packageInfo}, has prop? ${!!packageInfo.vulns}, for ${
43 | packageInfo.name
44 | }`
45 | );
46 | if (!packageInfo.vulns || !packageInfo.vulns.count) {
47 | return '';
48 | }
49 |
50 | const { count } = packageInfo.vulns;
51 | let decorationMessage = `${count} vuln${count === 1 ? '' : 's'} (click to fix)`;
52 |
53 | return decorationMessage;
54 | }
55 |
56 | function decorate(text, packageInfo) {
57 | const { fileName, line } = packageInfo;
58 |
59 | const hasVuln = text.includes('vuln');
60 |
61 | let color = new ThemeColor(hasVuln ? 'errorForeground' : 'foreground');
62 |
63 | let fontWeight = hasVuln ? 'bold' : 'normal';
64 |
65 | decorations[fileName][line] = {
66 | renderOptions: { after: { contentText: text, color, fontWeight } },
67 | range: new Range(
68 | new Position(line - 1, 1024),
69 | new Position(line - 1, 1024)
70 | ),
71 | };
72 | refreshDecorations(fileName);
73 | }
74 |
75 | const decorationType = window.createTextEditorDecorationType({
76 | after: { margin: '0 0 0 1rem' },
77 | });
78 | let decorationsDebounce;
79 | function refreshDecorations(fileName, delay = 10) {
80 | clearTimeout(decorationsDebounce);
81 | decorationsDebounce = setTimeout(
82 | () =>
83 | getEditors(fileName).forEach(editor => {
84 | editor.setDecorations(
85 | decorationType,
86 | Object.keys(decorations[fileName]).map(x => decorations[fileName][x])
87 | );
88 | }),
89 | delay
90 | );
91 | }
92 |
93 | function getEditors(fileName) {
94 | return window.visibleTextEditors.filter(
95 | editor => editor.document.fileName === fileName
96 | );
97 | }
98 |
99 | export function clearDecorations() {
100 | window.visibleTextEditors.forEach(textEditor => {
101 | return textEditor.setDecorations(decorationType, []);
102 | });
103 | }
104 |
--------------------------------------------------------------------------------
/src/getImports/packageInfo.js:
--------------------------------------------------------------------------------
1 | import { dirname, join } from 'path';
2 | import finder from 'find-package-json';
3 | import test from './testVuln';
4 | import logger from '../logger';
5 |
6 | import { DebounceError, debouncePromise } from './debouncePromise';
7 | import report from '../report';
8 | import axios from 'axios';
9 |
10 | let cache = {};
11 | let vulnCache = {};
12 | const projectCache = {};
13 |
14 | export async function getPackageKey(pkg) {
15 | if (pkg.version && pkg.name) {
16 | return { name: pkg.name, version: pkg.version };
17 | }
18 |
19 | let dir = projectCache[pkg.fileName];
20 |
21 | if (!dir) {
22 | const f = finder(pkg.fileName);
23 | dir = dirname(f.next().filename);
24 | projectCache[pkg.fileName] = dir;
25 | }
26 |
27 | const name = pkg.name;
28 |
29 | const f = finder(join(dir, 'node_modules', name));
30 | let packageInfo = f.next().value;
31 |
32 | // if the package doesn't start with the package name we were looking for
33 | // then it means it's not locally installed, so let's get the version from the
34 | // npm registry
35 | if (!packageInfo.name || !packageInfo.name.toLowerCase().startsWith(name)) {
36 | try {
37 | const res = await axios.get(`https://registry.npmjs.org/${name}`);
38 | const version = res.data['dist-tags']['latest'];
39 |
40 | return { name, version };
41 | } catch (err) {
42 | return {
43 | name,
44 | version: 'latest',
45 | };
46 | }
47 | }
48 |
49 | return { name: packageInfo.name, version: packageInfo.version };
50 | }
51 |
52 | function keyed(packageInfo) {
53 | return `${packageInfo.name}@${packageInfo.version}`;
54 | }
55 |
56 | export function clearPackageCache() {
57 | cache = {};
58 | vulnCache = {};
59 | }
60 |
61 | export function getPackageFromCache(key) {
62 | return vulnCache[key];
63 | }
64 |
65 | export async function getPackageInfo(pkg) {
66 | try {
67 | if (pkg.string) {
68 | cache[pkg.string] = cache[pkg.string] || (await getPackageKey(pkg));
69 | } else {
70 | cache[pkg.string] = await getPackageKey(pkg);
71 | }
72 | } catch (e) {
73 | logger.log(e.message);
74 | return pkg;
75 | }
76 |
77 | const key = keyed(cache[pkg.string]);
78 | logger.log('query ' + key);
79 |
80 | if (vulnCache[key] === undefined || vulnCache[key] instanceof Promise) {
81 | try {
82 | vulnCache[key] = vulnCache[key] || lookupVulns(key, cache[pkg.string]);
83 | vulnCache[key] = await vulnCache[key];
84 | logger.log('vuln test complete for ' + key);
85 | const reportSummary = report(key, vulnCache[key]);
86 | vulnCache[key].reportSummary = reportSummary;
87 | if (!vulnCache[key].ok) logger.print(reportSummary);
88 | } catch (e) {
89 | logger.log(`try on vuln test failed: ${e.message}`);
90 | if (e === DebounceError) {
91 | delete vulnCache[key];
92 | throw e;
93 | } else {
94 | vulnCache[key] = {};
95 | return { ...pkg, vulns: vulnCache[key], error: e };
96 | }
97 | }
98 | }
99 | return { ...pkg, vulns: vulnCache[key] };
100 | }
101 |
102 | export default function lookupVulns(key, pkg) {
103 | return debouncePromise(
104 | key,
105 | (resolve, reject) => {
106 | test(pkg)
107 | .then(resolve)
108 | .catch(reject);
109 | },
110 | 2000
111 | );
112 | }
113 |
--------------------------------------------------------------------------------
/GETTING-STARTED.MD:
--------------------------------------------------------------------------------
1 | # Adding the Extension
2 | To get started with the Vuln Cost Security Scanner extension for Visual Studio Code (VSCode), you must first install the *Vuln Cost - Security Scanner* by `snyk-security.vscode-vuln-cost` from the **Extensions Marketplace**. You may need to restart VSCode to see warnings.
3 |
4 | You can tell the extension is successfully installed by opening your `package.json` or equivalent. You should see **Scanning for vulns…** appear next to all of your dependencies. If a dependency version doesn’t match known vulnerable versions in Snyk’s database, the message will disappear. If the version matches a known-vulnerable one, the text will change to show the number of vulnerabilities in a message like `2 vulns (click to fix)`. You can’t actually click to fix until you [authorize your account](#authorizing-your-account), though, and the text saying `(click to fix)` is not clickable.
5 |
6 | # Viewing Vulnerability Details
7 | Click within the name of a dependency that lists a number of vulns, and a blue extension icon will appear in the left margin of VSCode. When you click the extension icon, a dropdown appears with two options: **Fix Vulns** or **Learn about this package**.
8 |
9 | If you select **Fix Vulns**, VSCode asks if you would like to go to the external webpage. If you haven’t authorized Snyk CLI since installing the extension, you will need to [authorize your account](#authorizing-your-account) to actually see fix options. If you don't have an account yet, you can create a free account to authorize. If you have already authorized Snyk CLI, the **Output** panel lists direct and indirect vulnerabilities known to affect your package version, as well as a version to upgrade to for possible remediation.
10 |
11 | If you select **Learn about this package**, VSCode asks if you would like to go to the external webpage, and agreeing takes you to the Snyk Advisor page for that package.
12 |
13 | # Authorizing Your Account
14 | The first time you use the Vuln Cost Security Scanner on a new device, you will need to authorize it by logging into your Snyk account before seeing remediation advice.
15 | 1. First, find a package with known vulnerabilities in your `package.json` or equivalent.
16 | 2. Click in the name of that package, then click the blue extension icon that appears in the left margin.
17 | A dropdown menu opens.
18 | 3. Select **Fix Vulns** from the dropdown.
19 | VSCode will ask if you want to open the External Link. You must open the external link to continue.
20 | 
21 | 4. Click Open.
22 | An **Authenticate for CLI** prompt appears. The Snyk CLI authorization flow will be used when you authenticate, and your VulnCost use will appear as an instance of the Snyk CLI Client in the Snyk web interface, if you also use that. 
23 | 5. Click **Authenticate** to allow the Snyk CLI to scan your package versions.
24 | An **Authenticated** screen should appear. If it doesn’t, reload the page and try again. 
25 | 6. Click in the name of your package and select **Fix Vulns** again, and you will now see direct and indirect vulnerabilities known to affect your package version, as well as a version to upgrade to for possible remediation in the **Output** panel of VSCode.
26 |
--------------------------------------------------------------------------------
/src/getImports/htmlParser.js:
--------------------------------------------------------------------------------
1 | // import htmlparser2 from 'htmlparser2';
2 | const htmlparser2 = require('htmlparser2');
3 |
4 | const JQUERY = 'https://code.jquery.com/';
5 | const ASPNETCDN = 'https://ajax.aspnetcdn.com/ajax/';
6 |
7 | const MAXCDN = 'https://maxcdn.bootstrapcdn.com/';
8 | const YANDEX = 'https://yastatic.net/';
9 | const BOOTSTRAP = 'https://stackpath.bootstrapcdn.com/';
10 | const pathBased = [MAXCDN, YANDEX, BOOTSTRAP];
11 |
12 | const JSDELIVR = 'https://cdn.jsdelivr.net/';
13 | const UNPKG = 'https://unpkg.com/';
14 | const atBased = [JSDELIVR, UNPKG];
15 |
16 | // packageFromUrl('https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js') // ?
17 |
18 | function packageFromUrl(url) {
19 | let i = url.toLowerCase().indexOf('/ajax/libs/');
20 | url = url.replace(/(.slim)?(\.min)?.js$/, '');
21 |
22 | if (i !== -1) {
23 | i += '/ajax/libs/'.length;
24 | let pkg = url.substring(i); // ?
25 | const [name, version = 'latest'] = pkg.split('/'); // ?
26 | return `${name}@${version}`;
27 | }
28 |
29 | const isPathBased = pathBased.find(_ => url.toLowerCase().startsWith(_));
30 |
31 | if (isPathBased) {
32 | let pkg = url.substring(isPathBased.length); // ?
33 | const seperator = pkg.includes('/') ? '/' : '-';
34 | const [name, version = 'latest'] = pkg.split(seperator);
35 | return `${name}@${version}`; // ?
36 | }
37 |
38 | if (url.toLowerCase().startsWith(JQUERY)) {
39 | let pkg = url.substring(JQUERY.length); // ?
40 | const [name, ...version] = pkg.split('-');
41 | return `${name}@${version.join('-')}`; // ?
42 | }
43 |
44 | if (url.toLowerCase().startsWith(ASPNETCDN)) {
45 | let pkg = url.substring(ASPNETCDN.length); // ?
46 | const [name, ...version] = pkg.split('-');
47 | return `${name.split('/').pop()}@${version.join('-')}`; // ?
48 | }
49 |
50 | const isAtBased = atBased.find(_ => url.toLowerCase().startsWith(_));
51 |
52 | if (isAtBased) {
53 | let pkg = url
54 | .substring(isAtBased.length)
55 | .split('/')
56 | .find(str => str.includes('@'));
57 |
58 | return pkg;
59 | }
60 | return null;
61 | }
62 |
63 | function indexToLineNumber(index, source) {
64 | return source.substring(0, index).split('\n').length;
65 | }
66 |
67 | export function getPackages(fileName, html) {
68 | const packages = [];
69 | const parser = new htmlparser2.Parser(
70 | {
71 | onopentag(name, attribs) {
72 | if (
73 | name === 'script' &&
74 | attribs.src &&
75 | (attribs.type || 'javascript/text').toLowerCase() ===
76 | 'javascript/text'
77 | ) {
78 | const res = packageFromUrl(attribs.src);
79 |
80 | if (res) {
81 | const [name, version] = res.split('@');
82 | const line = indexToLineNumber(parser.startIndex, html);
83 | let startCol = html
84 | .substring(parser.startIndex)
85 | .indexOf(attribs.src);
86 | if (startCol === -1) startCol = 0;
87 | packages.push({
88 | loc: {
89 | start: {
90 | line,
91 | column: startCol,
92 | },
93 | end: {
94 | line,
95 | column: startCol + attribs.src.length,
96 | },
97 | },
98 | fileName,
99 | line,
100 | name,
101 | version,
102 | });
103 | }
104 | }
105 | },
106 | },
107 | { decodeEntities: true }
108 | );
109 | parser.write(html);
110 | parser.end();
111 | return packages;
112 | }
113 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Latest update 🗞️ (start here first 👇)
2 | Vuln Cost is no longer being actively maintained. While you can continue to use this extension until it is officially deprecated, we recommend you install [the official Snyk extension](https://marketplace.visualstudio.com/items?itemName=snyk-security.snyk-vulnerability-scanner). This new extension provides all the functionality supported by Vuln Cost and enables you to find and fix issues in both your open source dependencies AND your custom code.
3 |
4 |
5 | 
6 | Vuln Cost
7 | The world's easiest, Security Scanner for VS Code
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | ## Vuln Cost - Security Scanner for VS Code
20 |
21 | - [Homepage](https://snyk.io/security-scanner-vuln-cost/)
22 | - [Github repository](https://github.com/snyk/vulncost)
23 | - [Issues](https://github.com/snyk/vulncost/issues)
24 |
25 | ## Getting started
26 | - [Getting started guide](https://github.com/snyk/vulncost/blob/master/GETTING-STARTED.MD)
27 |
28 | ## Vulnerability scanning in VS Code
29 |
30 | Find security vulnerabilities in open source packages while you code in JavaScript, TypeScript and HTML.
31 | Receive feedback in-line with your code, such as how many vulnerabilities a package contains that you are importing. And most important, suggesting a fix if known vulnerabilities are found.
32 | If you like the extension, we’d love it if you could [rate it](https://marketplace.visualstudio.com/items?itemName=snyk-security.vscode-vuln-cost&ssr=false#review-details).
33 |
34 | 🔒 Your code and manifest files never leave your machine. Snyk only needs the dependency name and version to test against our constantly updated vulnerability database.
35 |
36 | ## Detect vulnerabilities in third-party open source packages automatically while you code.
37 |
38 |
39 | - **Find security vulnerabilities in the npm packages you import:** see the number of known vulnerabilities in your imported npm packages as soon as you require them!
40 |
41 |
42 | - **See your project vulnerabilities inline, as you code:** see feedback directly in your editor. Vuln Cost displays the number of vulnerabilities your packages add to your project.
43 |
44 |
45 | - **Find security vulnerabilities in your JavaScript packages from well-known CDNs:** Vuln Cost scans any HTML files in your projects and displays vulnerability information about the JavaScript packages you download from your favorite CDN.
46 |
47 |
48 | - **See in-depth information about your vulnerabilities:** access relevant resources that will give you deeper information about the vulnerabilities that directly affect your project.
49 |
50 | Vuln Cost in JavaScript files:
51 |
52 | 
53 |
54 | Vuln Cost in HTML files:
55 |
56 | 
57 |
58 | ## CDN support
59 | Vuln Cost scans HTML files and displays vulnerability information about the JavaScript packages you download from multiple CDN providers.
60 |
61 | #### Currently supported CDN's
62 | - [unpkg.com](https://unpkg.com/)
63 | - [ajax.googleapis.com](https://ajax.googleapis.com)
64 | - [cdn.jsdelivr.net](https://cdn.jsdelivr.net)
65 | - [cdnjs.cloudflare.com](https://cdnjs.cloudflare.com)
66 | - [code.jquery.com](https://code.jquery.com/)
67 | - [maxcdn.bootstrapcdn.com](https://www.bootstrapcdn.com/)
68 | - [yastatic.net](https://yastatic.net/)
69 | - [ajax.aspnetcdn.com](https://ajax.aspnetcdn.com)
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/test/fixtures/st-clean/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "st-clean",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "async-cache": {
8 | "version": "1.1.0",
9 | "resolved": "https://registry.npmjs.org/async-cache/-/async-cache-1.1.0.tgz",
10 | "integrity": "sha1-SppaidBl7F2OUlS9nulrp2xTK1o=",
11 | "requires": {
12 | "lru-cache": "^4.0.0"
13 | }
14 | },
15 | "bl": {
16 | "version": "4.0.0",
17 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.0.tgz",
18 | "integrity": "sha512-QwQvAZZA1Bw1FWnhNj2X5lu+sPxxB2ITH3mqEqYyahN6JZR13ONjk+XiTnBaGEzMPUrAgOkaD68pBH1rvPRPsw==",
19 | "requires": {
20 | "readable-stream": "^3.4.0"
21 | }
22 | },
23 | "fd": {
24 | "version": "0.0.3",
25 | "resolved": "https://registry.npmjs.org/fd/-/fd-0.0.3.tgz",
26 | "integrity": "sha512-iAHrIslQb3U68OcMSP0kkNWabp7sSN6d2TBSb2JO3gcLJVDd4owr/hKM4SFJovFOUeeXeItjYgouEDTMWiVAnA=="
27 | },
28 | "graceful-fs": {
29 | "version": "4.2.3",
30 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
31 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
32 | "optional": true
33 | },
34 | "inherits": {
35 | "version": "2.0.4",
36 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
37 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
38 | },
39 | "lru-cache": {
40 | "version": "4.1.5",
41 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
42 | "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
43 | "requires": {
44 | "pseudomap": "^1.0.2",
45 | "yallist": "^2.1.2"
46 | }
47 | },
48 | "mime": {
49 | "version": "2.4.4",
50 | "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
51 | "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
52 | },
53 | "negotiator": {
54 | "version": "0.6.2",
55 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
56 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
57 | },
58 | "pseudomap": {
59 | "version": "1.0.2",
60 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
61 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
62 | },
63 | "readable-stream": {
64 | "version": "3.6.0",
65 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
66 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
67 | "requires": {
68 | "inherits": "^2.0.3",
69 | "string_decoder": "^1.1.1",
70 | "util-deprecate": "^1.0.1"
71 | }
72 | },
73 | "safe-buffer": {
74 | "version": "5.2.0",
75 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
76 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
77 | },
78 | "st": {
79 | "version": "2.0.0",
80 | "resolved": "https://registry.npmjs.org/st/-/st-2.0.0.tgz",
81 | "integrity": "sha512-drN+aGYnrZPNYIymmNwIY7LXYJ8MqsqXj4fMRue3FOgGMdGjSX10fhJ3qx0sVQPhcWxhEaN4U/eWM4O4dbYNAw==",
82 | "requires": {
83 | "async-cache": "^1.1.0",
84 | "bl": "^4.0.0",
85 | "fd": "~0.0.2",
86 | "graceful-fs": "^4.2.3",
87 | "mime": "^2.4.4",
88 | "negotiator": "~0.6.2"
89 | }
90 | },
91 | "string_decoder": {
92 | "version": "1.3.0",
93 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
94 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
95 | "requires": {
96 | "safe-buffer": "~5.2.0"
97 | }
98 | },
99 | "util-deprecate": {
100 | "version": "1.0.2",
101 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
102 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
103 | },
104 | "yallist": {
105 | "version": "2.1.2",
106 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
107 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Contributor-Agreement.md:
--------------------------------------------------------------------------------
1 | # Snyk Vuln Cost tool contributor agreement
2 |
3 | This Snyk Vuln Cost tool Agreement (this **"Agreement"**) applies to any Contribution you make to any Work.
4 |
5 | This is a binding legal agreement on you and any organization you represent. If you are signing this Agreement on behalf of your employer or other organization, you represent and warrant that you have the authority to agree to this Agreement on behalf of the organization.
6 |
7 | ## 1. Definitions
8 |
9 | **"Contribution"** means any original work, including any modification of or addition to an existing work, that you submit to Snyk Vuln Cost tool repo in any manner for inclusion in any Work.
10 |
11 | **"Snyk", "we"** and **"use"** means Snyk Ltd.
12 |
13 | **"Work"** means any project, work or materials owned or managed by Snyk Ltd.
14 |
15 | **"You"** and **"your"** means you and any organization on whose behalf you are entering this Agreement.
16 |
17 | ## 2. Copyright Assignment, License and Waiver
18 |
19 | **(a) Assignment.** By submitting a Contribution, you assign to Snyk all right, title and interest in any copright you have in the Contribution, and you waive any rights, including any moral rights, database rights, etc., that may affect your ownership of the copyright in the Contribution.
20 |
21 | **(b) License to Snyk.** If your assignment in Section 2(a) is ineffective for any reason, you grant to us and to any recipient of any Work distributed by use, a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable licence to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Contributions and any derivative work created based on a Contribution. If your license grant is ineffective for any reason, you irrevocably waive and covenant to not assert any claim you may have against us, our successors in interest, and any of our direct or indirect licensees and customers, arising out of our, our successors in interest's, or any of our direct or indirect licensees' or customers' use, reproduction, preparation of derivative works, public display, public performance, sublicense, and distribution of a Contribution. You also agree that we may publicly use your name and the name of any organization on whose behalf you're entering into this Agreement in connection with publicizing the Work.
22 |
23 | **(c) License to you.** We grant to you a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable license to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute a Contribution and any derivative works you create based on a Contribution.
24 |
25 | ## 3. Patent License
26 | You grant to us and to any recipient of any Work distributed by us, a perpetual, worldwide, transferable, non-exclusive, no-charge, royalty-free, irrevocable, and sublicensable patent license to make, have made, use, sell, offer to sell, import, and otherwise transfer the Contribution in whole or in part, along or included in any Work under any patent you own, or license from a third party, that is necessarily infringed by the Contribution or by combination of the Contribution with any Work.
27 |
28 | ## 4. Your Representation and Warranties.
29 | By submitting a Contribution, you represent and warrant that: (a) each Contribution you submit is an original work and you can legally grant the rights set out in this Agreement; (b) the Contribution does not, and any exercise of the rights granted by you will not, infringe any third party's intellectual property or other right; and (c) you are not aware of any claims, suits, or actions pertaining to the Contribution. You will notify us immediately if you become aware or have reason to believe that any of your representations and warranties is or becomes inaccurate.
30 |
31 | ##5. Intellectual Property
32 | Except for the assignment and licenses set forth in this Agreement, this Agreement does not transfer any right, title or interest in any intellectual property right of either party to the other. If you choose to provide us with suggestions, ideas for improvement, recommendations or other feedback, on any Work we may use your feedback without any restriction or payment.
33 |
34 | ## Miscellaneous
35 | English law governs this Agreement, excluding any applicable conflict of laws rules or principles, and the parties agree to the exclusive jurisdiction of the courts in England, UK. This Agreement does not create a partnership, agency relationship, or joint venture between the parties. We may assign this Agreement without notice or restriction. If any provision of this Agreement is unenforcable, that provision will be modified to render it enforceable to the extent possible to effect the parties' intention and the remaining provisions will not be affected. The parties may amend this Agreement only in a written amendment signed by both parties. This Agreement comprises the parties' entire agreement relating to the subject matter of this Agreement.
36 |
37 | **Agreed and accepted on my behalf and on behalf of my organization**
38 |
39 | Our contributor agreement is based on the [mongoDB contributor agreement](https://www.mongodb.com/legal/contributor-agreement).
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vscode-vuln-cost",
3 | "displayName": "Vuln Cost - Security Scanner",
4 | "description": "Security Scanner to find and fix vulnerabilities in JavaScript and TypeScript. Inspired by Import Cost",
5 | "license": "MIT",
6 | "version": "1.7.1",
7 | "private": true,
8 | "publisher": "snyk-security",
9 | "scripts": {
10 | "DISABLED_postinstall": "node ./node_modules/vscode/bin/install",
11 | "release": "semantic-release",
12 | "check": "eslint src/* && prettier -c ./src/**/*",
13 | "build": "webpack --mode production",
14 | "vscode:prepublish": "npm run check && npm run build",
15 | "webpack": "webpack --mode development",
16 | "dev": "webpack --mode development --watch",
17 | "test": "jest ."
18 | },
19 | "author": "Snyk ",
20 | "engines": {
21 | "vscode": "^1.40.0"
22 | },
23 | "categories": [
24 | "Other"
25 | ],
26 | "activationEvents": [
27 | "onLanguage:javascript",
28 | "onLanguage:javascriptreact",
29 | "onLanguage:typescript",
30 | "onLanguage:typescriptreact",
31 | "onLanguage:html",
32 | "onLanguage:json"
33 | ],
34 | "keywords": [
35 | "import",
36 | "require",
37 | "vuln",
38 | "vulnerability",
39 | "security"
40 | ],
41 | "main": "./dist/extension",
42 | "contributes": {
43 | "configuration": {
44 | "type": "object",
45 | "title": "Vuln Cost",
46 | "properties": {
47 | "vulnCost.ignorePaths": {
48 | "type": "array",
49 | "default": [],
50 | "description:": "Paths which should not be scanned"
51 | },
52 | "vulnCost.typescriptExtensions": {
53 | "type": "array",
54 | "default": [
55 | "\\.tsx?$"
56 | ],
57 | "description": "File extensions to be parsed by the TypeScript parser"
58 | },
59 | "vulnCost.javascriptExtensions": {
60 | "type": "array",
61 | "default": [
62 | "\\.jsx?$"
63 | ],
64 | "description": "File extensions to be parsed by the JavaScript parser"
65 | },
66 | "vulnCost.htmlExtensions": {
67 | "type": "array",
68 | "default": [
69 | "\\.html?$"
70 | ],
71 | "description": "File extensions to be parsed by the HTML parser"
72 | },
73 | "vulnCost.showDecoration": {
74 | "type": "boolean",
75 | "default": true,
76 | "description": "Display the decoration when starting to check vulnerability"
77 | },
78 | "vulnCost.sendStatistics": {
79 | "type": "boolean",
80 | "default": true,
81 | "description": "Send anonymous statistics on extension usage to server"
82 | },
83 | "vulnCost.debug": {
84 | "type": "boolean",
85 | "default": false,
86 | "description": "Enable debug logging"
87 | },
88 | "vulnCost.timeout": {
89 | "type": "number",
90 | "default": 10000,
91 | "description": "Size calculation timeout in milliseconds (requires restart)"
92 | }
93 | }
94 | },
95 | "commands": [
96 | {
97 | "command": "vulnCost.check",
98 | "title": "Snyk: run vulnerability test"
99 | },
100 | {
101 | "command": "vulnCost.toggle",
102 | "title": "Snyk: Toggle vuln cost"
103 | },
104 | {
105 | "command": "vulnCost.signIn",
106 | "title": "Snyk: Signup/create a Snyk account"
107 | },
108 | {
109 | "command": "vulnCost.signOut",
110 | "title": "Snyk: Sign out"
111 | },
112 | {
113 | "command": "vulnCost.showOutput",
114 | "title": "Snyk: Show vulnerability report"
115 | },
116 | {
117 | "command": "vulnCost.openVulnPage",
118 | "title": "Snyk: Show vulnerability page"
119 | }
120 | ]
121 | },
122 | "prettier": {
123 | "singleQuote": true,
124 | "trailingComma": "es5"
125 | },
126 | "dependencies": {
127 | "@babel/parser": "^7.12.11",
128 | "@babel/traverse": "^7.12.12",
129 | "@babel/types": "^7.12.12",
130 | "@snyk/configstore": "^3.2.0-rc1",
131 | "axios": "0.21.3",
132 | "find-package-json": "^1.2.0",
133 | "htmlparser2": "^4.1.0",
134 | "uuid": "^7.0.3",
135 | "validate-npm-package-name": "^3.0.0"
136 | },
137 | "devDependencies": {
138 | "@babel/preset-env": "^7.9.5",
139 | "@semantic-release/changelog": "^5.0.1",
140 | "@semantic-release/git": "^9.0.0",
141 | "@types/node": "^6.0.40",
142 | "@types/vscode": "^1.40.0",
143 | "babel-jest": "^25.3.0",
144 | "eslint": "^6.8.0",
145 | "jest": "^25.3.0",
146 | "nodemon": "^2.0.2",
147 | "prettier": "^1.19.1",
148 | "semantic-release": "^17.0.7",
149 | "webpack": "^4.41.6",
150 | "webpack-cli": "^3.3.11"
151 | },
152 | "galleryBanner": {
153 | "color": "#1C78C0",
154 | "theme": "dark"
155 | },
156 | "repository": {
157 | "type": "git",
158 | "url": "https://github.com/snyk/vulncost.git"
159 | },
160 | "bugs": {
161 | "url": "https://github.com/snyk/vulncost/issues"
162 | },
163 | "homepage": "https://github.com/snyk/vulncost#readme",
164 | "icon": "images/vuln-cost-logo.png",
165 | "directories": {
166 | "test": "test"
167 | },
168 | "snyk": true
169 | }
170 |
--------------------------------------------------------------------------------
/src/getImports/babelParser.js:
--------------------------------------------------------------------------------
1 | import traverse from '@babel/traverse';
2 | import * as t from '@babel/types';
3 | import * as path from 'path';
4 | import * as glob from 'glob';
5 | import { workspace } from 'vscode';
6 | import { parse as jsParse } from '@babel/parser';
7 | import { TYPESCRIPT } from './parser';
8 | import logger from '../logger';
9 |
10 | const PARSE_PLUGINS = [
11 | 'jsx',
12 | 'asyncFunctions',
13 | 'classConstructorCall',
14 | 'doExpressions',
15 | 'trailingFunctionCommas',
16 | 'objectRestSpread',
17 | ['decorators', { decoratorsBeforeExport: true }],
18 | 'classProperties',
19 | 'exportExtensions',
20 | 'exponentiationOperator',
21 | 'asyncGenerators',
22 | 'functionBind',
23 | 'functionSent',
24 | 'dynamicImport',
25 | ];
26 | const PARSE_JS_PLUGINS = ['flow', ...PARSE_PLUGINS];
27 | const PARSE_TS_PLUGINS = ['typescript', ...PARSE_PLUGINS];
28 |
29 | const configuration = workspace.getConfiguration('vulnCost');
30 | const typescriptRegex = new RegExp(
31 | configuration.typescriptExtensions.join('|')
32 | );
33 | const javascriptRegex = new RegExp(
34 | configuration.javascriptExtensions.join('|')
35 | );
36 |
37 | /**
38 | * @param {string} path
39 | *
40 | * @returns {boolean}
41 | */
42 | function doesFileExist(path) {
43 | const foundFiles = [
44 | ...glob.sync(`${path}/index.*`),
45 | ...glob.sync(`${path}.*`),
46 | ];
47 | if (!foundFiles.length) {
48 | return false;
49 | }
50 | let fileExists = false;
51 |
52 | for (let idx = 0; idx < foundFiles.length; idx++) {
53 | const file = foundFiles[idx];
54 | if (typescriptRegex.test(file) || javascriptRegex.test(file)) {
55 | fileExists = true;
56 | break;
57 | }
58 | }
59 |
60 | return fileExists;
61 | }
62 |
63 | export function getPackages(fileName, source, language) {
64 | const packages = [];
65 | const visitor = {
66 | ImportDeclaration({ node }) {
67 | const configuration = workspace.getConfiguration('vulnCost');
68 | let pathIgnored = false;
69 |
70 | for (let i = 0; i < configuration.ignorePaths.length; i++) {
71 | const path = configuration.ignorePaths[i];
72 | pathIgnored = new RegExp(path).test(node.source.value);
73 |
74 | if (pathIgnored) {
75 | break;
76 | }
77 | }
78 |
79 | if (pathIgnored) {
80 | logger.log(`Import ${node.source.value} matched ignored path: ${path}`);
81 | return;
82 | }
83 |
84 | const target = path.dirname(fileName) + path.sep + node.source.value;
85 |
86 | if (!doesFileExist(target)) {
87 | logger.log(`Found import declaration: ${node.source.value}`);
88 | packages.push({
89 | fileName,
90 | loc: node.source.loc,
91 | name: node.source.value,
92 | line: node.loc.end.line,
93 | string: compileImportString(node),
94 | });
95 | }
96 | },
97 | CallExpression({ node }) {
98 | if (node.callee.name === 'require') {
99 | packages.push({
100 | fileName,
101 | name: getPackageName(node),
102 | line: node.loc.end.line,
103 | loc: node.arguments[0].loc,
104 | string: compileRequireString(node),
105 | });
106 | } else if (node.callee.type === 'Import') {
107 | packages.push({
108 | fileName,
109 | loc: node.arguments[0].loc,
110 | name: getPackageName(node),
111 | line: node.loc.end.line,
112 | string: compileImportExpressionString(node),
113 | });
114 | }
115 | },
116 | };
117 |
118 | const ast = parse(source, language);
119 | traverse(ast, visitor);
120 | return packages;
121 | }
122 |
123 | function parse(source, language) {
124 | const plugins = language === TYPESCRIPT ? PARSE_TS_PLUGINS : PARSE_JS_PLUGINS;
125 | return jsParse(source, {
126 | sourceType: 'module',
127 | plugins,
128 | });
129 | }
130 |
131 | function compileImportString(node) {
132 | let importSpecifiers, importString;
133 | if (node.specifiers && node.specifiers.length > 0) {
134 | importString = []
135 | .concat(node.specifiers)
136 | .sort((s1, s2) => {
137 | // Import specifiers are in statement order, which for mixed imports must be either "defaultImport, * as namespaceImport"
138 | // or "defaultImport, { namedImport [as alias]... } according to current ECMA-262.
139 | // Given that two equivalent import statements can only differ in the order of the items in a NamedImports block,
140 | // we only need to sort these items in relation to each other to normalise the statements for caching purposes.
141 | // Where the node is anything other than ImportSpecifier (Babel terminology for NamedImports), preserve the original statement order.
142 | if (t.isImportSpecifier(s1) && t.isImportSpecifier(s2)) {
143 | return s1.imported.name < s2.imported.name ? -1 : 1;
144 | }
145 | return 0;
146 | })
147 | .map((specifier, i) => {
148 | if (t.isImportNamespaceSpecifier(specifier)) {
149 | return `* as ${specifier.local.name}`;
150 | } else if (t.isImportDefaultSpecifier(specifier)) {
151 | return specifier.local.name;
152 | } else if (t.isImportSpecifier(specifier)) {
153 | if (!importSpecifiers) {
154 | importSpecifiers = '{';
155 | }
156 | importSpecifiers += specifier.imported.name;
157 | if (
158 | node.specifiers[i + 1] &&
159 | t.isImportSpecifier(node.specifiers[i + 1])
160 | ) {
161 | importSpecifiers += ', ';
162 | return undefined;
163 | } else {
164 | const result = importSpecifiers + '}';
165 | importSpecifiers = undefined;
166 | return result;
167 | }
168 | } else {
169 | return undefined;
170 | }
171 | })
172 | .filter(x => x)
173 | .join(', ');
174 | } else {
175 | importString = '* as tmp';
176 | }
177 | return `import ${importString} from '${
178 | node.source.value
179 | }';\nconsole.log(${importString.replace('* as ', '')});`;
180 | }
181 |
182 | function compileRequireString(node) {
183 | return `require('${getPackageName(node)}')`;
184 | }
185 |
186 | function compileImportExpressionString(node) {
187 | return `import('${getPackageName(node)}').then(res => console.log(res));`;
188 | }
189 |
190 | function getPackageName(node) {
191 | return t.isTemplateLiteral(node.arguments[0])
192 | ? node.arguments[0].quasis[0].value.raw
193 | : node.arguments[0].value;
194 | }
195 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changes
2 |
3 | ## [1.7.1](https://github.com/snyk/vulncost/compare/v1.7.0...v1.7.1) (2021-11-26)
4 |
5 |
6 | ### Bug Fixes
7 |
8 | * add link to official Snyk extension ([266c818](https://github.com/snyk/vulncost/commit/266c81812d8a3cd73af7f3172f2b585cbb2600a6))
9 |
10 | # [1.7.0](https://github.com/snyk/vulncost/compare/v1.6.1...v1.7.0) (2021-11-26)
11 |
12 |
13 | ### Features
14 |
15 | * show deprecation notification ([26f2a8e](https://github.com/snyk/vulncost/commit/26f2a8e01656654a8c5f3e6e642062369088aba6))
16 |
17 | ## [1.6.1](https://github.com/snyk/vulncost/compare/v1.6.0...v1.6.1) (2021-09-17)
18 |
19 |
20 | ### Bug Fixes
21 |
22 | * package.json & package-lock.json to reduce vulnerabilities ([f6561c7](https://github.com/snyk/vulncost/commit/f6561c777bca8a6c2f4ba0dc2eb1670012fbea20))
23 |
24 | # [1.6.0](https://github.com/snyk/vulncost/compare/v1.5.0...v1.6.0) (2021-06-02)
25 |
26 |
27 | ### Features
28 |
29 | * adds support for critical severity ([76d2035](https://github.com/snyk/vulncost/commit/76d20357e6e5ae4a6166e68e5b591b3dbb9917ac))
30 |
31 | # [1.5.0](https://github.com/snyk/vulncost/compare/v1.4.0...v1.5.0) (2021-04-20)
32 |
33 |
34 | ### Features
35 |
36 | * Migrate from legacy /vuln endpoint to /test ([1170fd7](https://github.com/snyk/vulncost/commit/1170fd704ac39c45ad9d034fe935562bc5dcd1a8))
37 |
38 | # [1.4.0](https://github.com/snyk/vulncost/compare/v1.3.11...v1.4.0) (2021-02-02)
39 |
40 |
41 | ### Features
42 |
43 | * directing people to the snyk advisor page for better package information ([2bdd9e8](https://github.com/snyk/vulncost/commit/2bdd9e8eba4f04320adaa4f40c303eeb7ce19b22))
44 |
45 | ## [1.3.11](https://github.com/snyk/vulncost/compare/v1.3.10...v1.3.11) (2021-01-26)
46 |
47 |
48 | ### Bug Fixes
49 |
50 | * upgrade multiple dependencies with Snyk ([021e9db](https://github.com/snyk/vulncost/commit/021e9dbbed87bd20e157c1779394ab1e8485a7c1))
51 |
52 | ## [1.3.10](https://github.com/snyk/vulncost/compare/v1.3.9...v1.3.10) (2021-01-13)
53 |
54 |
55 | ### Bug Fixes
56 |
57 | * upgrade multiple dependencies with Snyk ([4478590](https://github.com/snyk/vulncost/commit/4478590a7476bd0a512270dba7e932d0c7eb11ca))
58 |
59 | ## [1.3.9](https://github.com/snyk/vulncost/compare/v1.3.8...v1.3.9) (2021-01-04)
60 |
61 |
62 | ### Bug Fixes
63 |
64 | * upgrade multiple dependencies with Snyk ([6b63859](https://github.com/snyk/vulncost/commit/6b638593895296d5e7e587a2bf96341f4a67da0a))
65 |
66 | ## [1.3.8](https://github.com/snyk/vulncost/compare/v1.3.7...v1.3.8) (2021-01-04)
67 |
68 |
69 | ### Bug Fixes
70 |
71 | * package.json & package-lock.json to reduce vulnerabilities ([fd3931b](https://github.com/snyk/vulncost/commit/fd3931baee2f36934690af41cf2e5dca55fe56df))
72 |
73 | ## [1.3.7](https://github.com/snyk/vulncost/compare/v1.3.6...v1.3.7) (2020-11-13)
74 |
75 |
76 | ### Bug Fixes
77 |
78 | * updating deps ([0566a3d](https://github.com/snyk/vulncost/commit/0566a3de4885cd04ca172122c5e25fea8cf6e5b0))
79 | * updating deps ([2ce0fdb](https://github.com/snyk/vulncost/commit/2ce0fdb90867a360d3210fc2fcaa75e9d5138bdd))
80 |
81 | ## [1.3.6](https://github.com/snyk/vulncost/compare/v1.3.5...v1.3.6) (2020-05-14)
82 |
83 |
84 | ### Bug Fixes
85 |
86 | * change title and text for marketplace ([d8da115](https://github.com/snyk/vulncost/commit/d8da1155ba8834153fea9d5490ce08bb3d35b06a))
87 |
88 | ## [1.3.5](https://github.com/snyk/vulncost/compare/v1.3.4...v1.3.5) (2020-05-07)
89 |
90 |
91 | ### Bug Fixes
92 |
93 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([bb68787](https://github.com/snyk/vulncost/commit/bb6878728e8967837724ba5536d3df011c315495))
94 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([8691e01](https://github.com/snyk/vulncost/commit/8691e017c765cffc84edaf0ddc85bb3eaa6995ed))
95 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([608e3a0](https://github.com/snyk/vulncost/commit/608e3a03d3bffcb726249c0c2d90a0634b36479f))
96 |
97 | ## [1.3.4](https://github.com/snyk/vulncost/compare/v1.3.3...v1.3.4) (2020-05-04)
98 |
99 |
100 | ### Bug Fixes
101 |
102 | * display vulns only on dependencies in package.json ([a976345](https://github.com/snyk/vulncost/commit/a9763452ecec4871c2976324ef21e679ca5661a8))
103 |
104 | ## [1.3.3](https://github.com/snyk/vulncost/compare/v1.3.2...v1.3.3) (2020-05-04)
105 |
106 |
107 | ### Bug Fixes
108 |
109 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([3d18893](https://github.com/snyk/vulncost/commit/3d18893610c10be18f0ffb73c324b3fb6b50946b))
110 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([de71b47](https://github.com/snyk/vulncost/commit/de71b476f7dabbef177958e4e127967d93c89ecc))
111 | * package.json, package-lock.json & .snyk to reduce vulnerabilities ([249ddbf](https://github.com/snyk/vulncost/commit/249ddbfda6735b556bb42e586b4886061e2908bf))
112 |
113 | ## [1.3.2](https://github.com/snyk/vulncost/compare/v1.3.1...v1.3.2) (2020-04-29)
114 |
115 |
116 | ### Bug Fixes
117 |
118 | * property to disable sending anonymous statistics ([e4e14d4](https://github.com/snyk/vulncost/commit/e4e14d4b7d5ad30fa9f07901f5527467ba185ba9))
119 |
120 | ## [1.3.1](https://github.com/snyk/vulncost/compare/v1.3.0...v1.3.1) (2020-04-28)
121 |
122 |
123 | ### Bug Fixes
124 |
125 | * removed unused import ([aacd908](https://github.com/snyk/vulncost/commit/aacd908f68967736dffdac38256f4e6661eb6b7b))
126 | * update package-lock.json ([7b6c5ba](https://github.com/snyk/vulncost/commit/7b6c5baaaf2d5b9fad90b38154aa4597ead91113))
127 |
128 | # [1.3.0](https://github.com/snyk/vulncost/compare/v1.2.5...v1.3.0) (2020-04-28)
129 |
130 |
131 | ### Features
132 |
133 | * improve labels ([1c80dfb](https://github.com/snyk/vulncost/commit/1c80dfbf8fb0231788f799cf4a5dcb0050c7c10e))
134 | * sending statistics to analyse user flow ([f85070d](https://github.com/snyk/vulncost/commit/f85070ded51f1b9e198e9283ebcb1365e15ee748))
135 |
136 | ## [1.2.5](https://github.com/snyk/vulncost/compare/v1.2.4...v1.2.5) (2020-04-26)
137 |
138 |
139 | ### Bug Fixes
140 |
141 | * adjust order of plugins ([6142427](https://github.com/snyk/vulncost/commit/614242738f43a51e92b7ae18c8fa6c8c3e9d5e1e))
142 |
143 | ## [1.2.4](https://github.com/snyk/vulncost/compare/v1.2.3...v1.2.4) (2020-04-26)
144 |
145 |
146 | ### Bug Fixes
147 |
148 | * include package.json in git ([54004e1](https://github.com/snyk/vulncost/commit/54004e113cf337e75308b46878bbebd7ac12bb1d))
149 |
150 | ## [1.2.3](https://github.com/snyk/vulncost/compare/v1.2.2...v1.2.3) (2020-04-26)
151 |
152 |
153 | ### Bug Fixes
154 |
155 | * semantic-release reorder plugins and add --verbose output ([fb0bb6a](https://github.com/snyk/vulncost/commit/fb0bb6a58da2a275cb1ebf02996fba34020b7b34))
156 |
157 | ## [1.2.2](https://github.com/snyk/vulncost/compare/v1.2.1...v1.2.2) (2020-04-26)
158 |
159 |
160 | ### Bug Fixes
161 |
162 | * semantic-release iterate pkg.json version ([35cff53](https://github.com/snyk/vulncost/commit/35cff53ef3d568ae5eea8f355eb00abf6c3e1c0e))
163 |
164 | ## [1.2.1](https://github.com/snyk/vulncost/compare/v1.2.0...v1.2.1) (2020-04-26)
165 |
166 |
167 | ### Bug Fixes
168 |
169 | * removes OS file ([d2ee3b6](https://github.com/snyk/vulncost/commit/d2ee3b60c44bf4e6bd0d5aaddb5e8938692a650a))
170 |
171 | # [1.2.0](https://github.com/snyk/vulncost/compare/v1.1.0...v1.2.0) (2020-04-26)
172 |
173 |
174 | ### Bug Fixes
175 |
176 | * [#19](https://github.com/snyk/vulncost/issues/19) consistent vuln count across auth/unauth requests ([b2c5a16](https://github.com/snyk/vulncost/commit/b2c5a167522e511f30e3685fdb2c9cc493aff89a))
177 | * adds support for assets hosted on github ([fb383b4](https://github.com/snyk/vulncost/commit/fb383b47a36a8d8d67b071c58342083a434ae4f3))
178 | * do not scan local modules ([21c5934](https://github.com/snyk/vulncost/commit/21c5934aac3494be657a1e9274e3f0cc8e10a358))
179 | * prevent npm publication ([647c3ff](https://github.com/snyk/vulncost/commit/647c3ff0ca1b63f121ebcb597676fb0341b40ad2))
180 | * supports eg/file.js ([5b1c6a0](https://github.com/snyk/vulncost/commit/5b1c6a05b95a4360551b8f671335ee7c1e49d94b))
181 |
182 |
183 | ### Features
184 |
185 | * adds Command to remove auth token from local config ([7baa5f8](https://github.com/snyk/vulncost/commit/7baa5f8983162c66ee00affeac957682db32942b))
186 | * adds setting to ignore based on paths ([748c178](https://github.com/snyk/vulncost/commit/748c178867700ec0d07e5a02f1a5cb20abb75db2))
187 | * adds support for ajax.aspnet.com ([241bc45](https://github.com/snyk/vulncost/commit/241bc45833cf7223f479a28b64a8597fd4af5da0))
188 | * use js|typescript config regex ([4113c0f](https://github.com/snyk/vulncost/commit/4113c0f38b682eaadf1f6f98f1e9900099ad2a66))
189 | * decorate dependencies in package.json ([c472e62](https://github.com/snyk/vulncost/commit/c472e62b33885cc1950b5e0a0fb794953557efb7))
190 |
191 | ## 1.1.0
192 |
193 | - White check once when scan is done without vulns found
194 | - Icon
195 |
196 | ## 1.0.1
197 |
198 | Readme update
199 |
200 | ## 1.0.0
201 |
202 | Initial release
203 |
--------------------------------------------------------------------------------
/src/extension.js:
--------------------------------------------------------------------------------
1 | import {
2 | clearPackageCache,
3 | getImports,
4 | JAVASCRIPT,
5 | TYPESCRIPT,
6 | HTML,
7 | PJSON,
8 | } from './getImports';
9 | import * as vscode from 'vscode';
10 | import { calculated, flushDecorations, clearDecorations, clearShown} from './decorator';
11 | import logger from './logger';
12 | import { SnykVulnInfo } from './SnykAction';
13 | import { isAuthed, setToken, clearToken } from './getImports/snykAPI';
14 | import { refreshDiagnostics } from './diagnostics';
15 | import { v4 as uuidv4 } from 'uuid';
16 | import authenticate from './authenticate';
17 | import utm from './utm';
18 | import statistics from './statistics';
19 |
20 | const { window, workspace, commands } = vscode;
21 | const deprecationNoteKey = 'deepcode.deprecationNoteMonth';
22 |
23 | let isActive = true;
24 | let packageWatcher = {};
25 |
26 | export function activate(context) {
27 | try {
28 | logger.init(context);
29 | statistics.init(context);
30 |
31 | void showDeprecationNotification(context);
32 |
33 | if (isAuthed()) {
34 | logger.log('🔒 Using Snyk credentials');
35 | } else {
36 | logger.log('🔓 Using anonymous API');
37 | }
38 |
39 | statistics.sendStartup('authed: ' + isAuthed());
40 |
41 | [JAVASCRIPT, TYPESCRIPT, HTML, PJSON].forEach(language => {
42 | context.subscriptions.push(
43 | vscode.languages.registerCodeActionsProvider(
44 | language,
45 | new SnykVulnInfo(),
46 | {
47 | providedCodeActionKinds: SnykVulnInfo.providedCodeActionKinds,
48 | }
49 | )
50 | );
51 | });
52 |
53 | const diagnostics = vscode.languages.createDiagnosticCollection(
54 | 'snyk-vulns'
55 | );
56 |
57 | context.subscriptions.push(diagnostics);
58 |
59 | workspace.onDidChangeTextDocument(
60 | ev => isActive && processActiveFile(ev.document, diagnostics)
61 | );
62 | window.onDidChangeActiveTextEditor(
63 | ev => ev && isActive && processActiveFile(ev.document, diagnostics)
64 | );
65 | if (window.activeTextEditor && isActive) {
66 | processActiveFile(window.activeTextEditor.document, diagnostics);
67 | }
68 |
69 | context.subscriptions.push(
70 | commands.registerCommand('vulnCost.check', () => {
71 | clearPackageCache();
72 | clearShown();
73 | processActiveFile(window.activeTextEditor.document, diagnostics);
74 | })
75 | );
76 |
77 | context.subscriptions.push(
78 | commands.registerCommand('vulnCost.toggle', () => {
79 | isActive = !isActive;
80 | statistics.send('vulnCost.toggle', `active: ${isActive}`);
81 | if (isActive && window.activeTextEditor) {
82 | processActiveFile(window.activeTextEditor.document, diagnostics);
83 | } else {
84 | deactivate();
85 | clearDecorations();
86 | }
87 | })
88 | );
89 |
90 | context.subscriptions.push(
91 | commands.registerCommand('vulnCost.showOutput', vuln => {
92 | statistics.send('vulnCost.showOutput', vuln.packageName);
93 | if (!vuln) {
94 | return logger.show();
95 | }
96 |
97 | logger.flushWith(vuln.reportSummary.trim());
98 | })
99 | );
100 |
101 | context.subscriptions.push(
102 | commands.registerCommand('vulnCost.signOut', () => {
103 | statistics.send('vulnCost.signOut');
104 | window.showInformationMessage('Removing auth token');
105 |
106 | clearToken();
107 |
108 | return;
109 | })
110 | );
111 |
112 | context.subscriptions.push(
113 | commands.registerCommand('vulnCost.signIn', () => {
114 | if (isAuthed()) {
115 | window.showInformationMessage(
116 | 'You are already connected to your Snyk account'
117 | );
118 | return;
119 | }
120 | statistics.send('vulnCost.signIn');
121 | const token = uuidv4();
122 | const url = `https://app.snyk.io/login?${utm}&token=${token}`;
123 |
124 | vscode.env.openExternal(vscode.Uri.parse(url)).then(() => {
125 | authenticate(token)
126 | .then(res => {
127 | setToken(res.data.api);
128 | window.showInformationMessage(
129 | 'Your Snyk account is now connected.'
130 | );
131 |
132 | // and and reset vulns after auth
133 | if (isActive && window.activeTextEditor) {
134 | commands.executeCommand('vulnCost.check');
135 | }
136 | })
137 | .catch(e => {
138 | logger.log(e.stack);
139 | window.showErrorMessage(e.message);
140 | });
141 | });
142 | })
143 | );
144 |
145 | context.subscriptions.push(
146 | commands.registerCommand('vulnCost.openVulnPage', pkg => {
147 | statistics.send('vulnCost.openVulnPage', pkg);
148 | const url = `https://snyk.io/advisor/npm-package/${pkg}?${utm}`;
149 | vscode.env.openExternal(vscode.Uri.parse(url));
150 | return;
151 | })
152 | );
153 | } catch (e) {
154 | console.log(e.message);
155 | logger.log('wrapping error: ' + e);
156 | }
157 | }
158 |
159 | export function deactivate() {}
160 |
161 | function createPackageWatcher(fileName) {
162 | if (packageWatcher[fileName]) {
163 | console.log('watching already', fileName);
164 | return;
165 | }
166 |
167 | // FIXME investigate why this doesn't work in a multi-root workspace (might be a vscode bug)
168 | const watcher = vscode.workspace.createFileSystemWatcher(fileName);
169 | watcher.onDidChange(() => {
170 | logger.log(
171 | 'package.json change detected, clear vuln cache and if file active, running checks again'
172 | );
173 | clearPackageCache();
174 | clearShown();
175 | if (isActive && window.activeTextEditor) {
176 | commands.executeCommand('vulnCost.check');
177 | }
178 | });
179 |
180 | packageWatcher[fileName] = watcher;
181 | }
182 |
183 | let emitters = {};
184 | async function processActiveFile(document, diagnostics) {
185 | if (document && language(document)) {
186 | const { fileName } = document;
187 | if (emitters[fileName]) {
188 | emitters[fileName].removeAllListeners();
189 | }
190 |
191 | emitters[fileName] = getImports(
192 | fileName,
193 | document.getText(),
194 | language(document)
195 | );
196 |
197 | emitters[fileName].on('package', createPackageWatcher);
198 | emitters[fileName].on('error', e =>
199 | logger.log(`vulnCost error: ${e.stack}`)
200 | );
201 | emitters[fileName].on('start', packages => {
202 | flushDecorations(fileName, packages);
203 | });
204 | emitters[fileName].on('calculated', packageInfo => {calculated(packageInfo);});
205 |
206 | emitters[fileName].on('done', packages => {
207 | flushDecorations(fileName, packages, true);
208 | refreshDiagnostics(document, diagnostics, packages);
209 | });
210 | }
211 | }
212 |
213 | function language({ fileName, languageId }) {
214 | const configuration = workspace.getConfiguration('vulnCost');
215 | const typescriptRegex = new RegExp(
216 | configuration.typescriptExtensions.join('|')
217 | );
218 | const javascriptRegex = new RegExp(
219 | configuration.javascriptExtensions.join('|')
220 | );
221 | const htmlRegex = new RegExp(configuration.htmlExtensions.join('|'));
222 | if (
223 | languageId === 'typescript' ||
224 | languageId === 'typescriptreact' ||
225 | typescriptRegex.test(fileName)
226 | ) {
227 | return TYPESCRIPT;
228 | }
229 |
230 | if (
231 | languageId === 'javascript' ||
232 | languageId === 'javascriptreact' ||
233 | javascriptRegex.test(fileName)
234 | ) {
235 | return JAVASCRIPT;
236 | }
237 |
238 | if (languageId === 'html' || htmlRegex.test(fileName)) {
239 | return HTML;
240 | }
241 |
242 | if (languageId === 'json' && fileName.endsWith('package.json')) {
243 | return PJSON;
244 | }
245 |
246 | return undefined;
247 | }
248 |
249 | async function showDeprecationNotification(context) {
250 | const currentMonth = new Date().getMonth();
251 | const lastMessageMonth = context.globalState.get(deprecationNoteKey);
252 |
253 | if (!lastMessageMonth || currentMonth != lastMessageMonth) {
254 | context.globalState.update(deprecationNoteKey, currentMonth);
255 |
256 | const deprecationMessage = `The VulnCost extension is deprecated. Please use [Snyk Vulnerability Scanner](command:workbench.extensions.search?%22snyk-security.snyk-vulnerability-scanner%22) with included VulnCost's functionality and more.`;
257 | const installCommandText = 'Install Snyk Vulnerability Scanner';
258 |
259 | const selected = await vscode.window.showInformationMessage(deprecationMessage, installCommandText);
260 | if (selected === installCommandText) {
261 | vscode.commands.executeCommand('workbench.extensions.search', 'snyk-security.snyk-vulnerability-scanner');
262 | }
263 | }
264 | }
265 |
--------------------------------------------------------------------------------
/test/fixtures/st.json:
--------------------------------------------------------------------------------
1 | {"ok":false,"vulnerabilities":[{"CVSSv3":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L","alternativeIds":["SNYK-JS-MIME-10788"],"creationTime":"2017-09-26T05:48:40.307000Z","credit":["Cristian-Alexandru Staicu"],"cvssScore":3.7,"description":"## Overview\n\n[mime](https://www.npmjs.com/package/mime) is a comprehensive, compact MIME type module.\n\n\nAffected versions of this package are vulnerable to Regular Expression Denial of Service (ReDoS).\nIt uses regex the following regex `/.*[\\.\\/\\\\]/` in its lookup, which can cause a slowdown of 2 seconds for 50k characters.\n\n## Details\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\r\n\r\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\r\n\r\nLet’s take the following regular expression as an example:\r\n```js\r\nregex = /A(B|C+)+D/\r\n```\r\n\r\nThis regular expression accomplishes the following:\r\n- `A` The string must start with the letter 'A'\r\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\r\n- `D` Finally, we ensure this section of the string ends with a 'D'\r\n\r\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\r\n\r\nIt most cases, it doesn't take very long for a regex engine to find a match:\r\n\r\n```bash\r\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\r\n0.04s user 0.01s system 95% cpu 0.052 total\r\n\r\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\r\n1.79s user 0.02s system 99% cpu 1.812 total\r\n```\r\n\r\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\r\n\r\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\r\n\r\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\r\n1. CCC\r\n2. CC+C\r\n3. C+CC\r\n4. C+C+C.\r\n\r\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\r\n\r\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\r\n\r\n| String | Number of C's | Number of steps |\r\n| -------|-------------:| -----:|\r\n| ACCCX | 3 | 38\r\n| ACCCCX | 4 | 71\r\n| ACCCCCX | 5 | 136\r\n| ACCCCCCCCCCCCCCX | 14 | 65,553\r\n\r\n\r\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\n\nUpgrade `mime` to version 1.4.1, 2.0.3 or higher.\n\n\n## References\n\n- [GitHub Commit](https://github.com/broofa/node-mime/commit/1df903fdeb9ae7eaa048795b8d580ce2c98f40b0)\n\n- [GitHub Commit](https://github.com/broofa/node-mime/commit/855d0c4b8b22e4a80b9401a81f2872058eae274d)\n\n- [GitHub Issue](https://github.com/broofa/node-mime/issues/167)\n\n- [NPM Security Advisory](https://www.npmjs.com/advisories/535)\n","disclosureTime":"2017-09-07T21:00:00Z","exploit":"Not Defined","fixedIn":["1.4.1","2.0.3"],"functions":[{"functionId":{"className":null,"filePath":"mime.js","functionName":"mime.module.exports.lookup"},"version":["<1.2.6"]},{"functionId":{"className":null,"filePath":"mime.js","functionName":"Mime.prototype.lookup"},"version":[">=1.2.6 <1.4.1"]},{"functionId":{"className":null,"filePath":"Mime.js","functionName":"Mime.prototype.getType"},"version":[">=2.0.0 <2.0.3"]}],"functions_new":[{"functionId":{"filePath":"mime.js","functionName":"mime.module.exports.lookup"},"version":["<1.2.6"]},{"functionId":{"filePath":"mime.js","functionName":"Mime.prototype.lookup"},"version":[">=1.2.6 <1.4.1"]},{"functionId":{"filePath":"Mime.js","functionName":"Mime.prototype.getType"},"version":[">=2.0.0 <2.0.3"]}],"id":"npm:mime:20170907","identifiers":{"ALTERNATIVE":["SNYK-JS-MIME-10788"],"CVE":["CVE-2017-16138"],"CWE":["CWE-400"],"NSP":[535]},"language":"js","modificationTime":"2019-12-23T13:05:14.510572Z","moduleName":"mime","packageManager":"npm","packageName":"mime","patches":[{"comments":[],"id":"patch:npm:mime:20170907:0","modificationTime":"2019-12-03T11:40:45.877450Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/mime/20170907/mime_20170907_0_0_855d0c4b8b22e4a80b9401a81f2872058eae274d.patch"],"version":"=1.2.11 || =1.3.4"}],"publicationTime":"2017-09-27T05:48:40Z","references":[{"title":"GitHub Commit","url":"https://github.com/broofa/node-mime/commit/1df903fdeb9ae7eaa048795b8d580ce2c98f40b0"},{"title":"GitHub Commit","url":"https://github.com/broofa/node-mime/commit/855d0c4b8b22e4a80b9401a81f2872058eae274d"},{"title":"GitHub Issue","url":"https://github.com/broofa/node-mime/issues/167"},{"title":"NPM Security Advisory","url":"https://www.npmjs.com/advisories/535"}],"semver":{"vulnerable":["<1.4.1",">=2.0.0 <2.0.3"]},"severity":"low","title":"Regular Expression Denial of Service (ReDoS)","from":["st@0.2.4","mime@1.2.11"],"upgradePath":["st@1.2.1","mime@1.4.1"],"version":"1.2.11","name":"mime","isUpgradable":true,"isPatchable":true,"isPinnable":false},{"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","alternativeIds":["SNYK-JS-NEGOTIATOR-10104"],"creationTime":"2016-06-16T18:00:02.240000Z","credit":["Adam Baldwin"],"cvssScore":7.5,"description":"## Overview\n\n[negotiator](https://npmjs.org/package/negotiator) is an HTTP content negotiator for Node.js.\n\n\nAffected versions of this package are vulnerable to Regular Expression Denial of Service (DoS)\nwhen parsing `Accept-Language` http header.\n\n## Details\nDenial of Service (DoS) describes a family of attacks, all aimed at making a system inaccessible to its original and legitimate users. There are many types of DoS attacks, ranging from trying to clog the network pipes to the system by generating a large volume of traffic from many machines (a Distributed Denial of Service - DDoS - attack) to sending crafted requests that cause a system to crash or take a disproportional amount of time to process.\r\n\r\nThe Regular expression Denial of Service (ReDoS) is a type of Denial of Service attack. Regular expressions are incredibly powerful, but they aren't very intuitive and can ultimately end up making it easy for attackers to take your site down.\r\n\r\nLet’s take the following regular expression as an example:\r\n```js\r\nregex = /A(B|C+)+D/\r\n```\r\n\r\nThis regular expression accomplishes the following:\r\n- `A` The string must start with the letter 'A'\r\n- `(B|C+)+` The string must then follow the letter A with either the letter 'B' or some number of occurrences of the letter 'C' (the `+` matches one or more times). The `+` at the end of this section states that we can look for one or more matches of this section.\r\n- `D` Finally, we ensure this section of the string ends with a 'D'\r\n\r\nThe expression would match inputs such as `ABBD`, `ABCCCCD`, `ABCBCCCD` and `ACCCCCD`\r\n\r\nIt most cases, it doesn't take very long for a regex engine to find a match:\r\n\r\n```bash\r\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCD\")'\r\n0.04s user 0.01s system 95% cpu 0.052 total\r\n\r\n$ time node -e '/A(B|C+)+D/.test(\"ACCCCCCCCCCCCCCCCCCCCCCCCCCCCX\")'\r\n1.79s user 0.02s system 99% cpu 1.812 total\r\n```\r\n\r\nThe entire process of testing it against a 30 characters long string takes around ~52ms. But when given an invalid string, it takes nearly two seconds to complete the test, over ten times as long as it took to test a valid string. The dramatic difference is due to the way regular expressions get evaluated.\r\n\r\nMost Regex engines will work very similarly (with minor differences). The engine will match the first possible way to accept the current character and proceed to the next one. If it then fails to match the next one, it will backtrack and see if there was another way to digest the previous character. If it goes too far down the rabbit hole only to find out the string doesn’t match in the end, and if many characters have multiple valid regex paths, the number of backtracking steps can become very large, resulting in what is known as _catastrophic backtracking_.\r\n\r\nLet's look at how our expression runs into this problem, using a shorter string: \"ACCCX\". While it seems fairly straightforward, there are still four different ways that the engine could match those three C's:\r\n1. CCC\r\n2. CC+C\r\n3. C+CC\r\n4. C+C+C.\r\n\r\nThe engine has to try each of those combinations to see if any of them potentially match against the expression. When you combine that with the other steps the engine must take, we can use [RegEx 101 debugger](https://regex101.com/debugger) to see the engine has to take a total of 38 steps before it can determine the string doesn't match.\r\n\r\nFrom there, the number of steps the engine must use to validate a string just continues to grow.\r\n\r\n| String | Number of C's | Number of steps |\r\n| -------|-------------:| -----:|\r\n| ACCCX | 3 | 38\r\n| ACCCCX | 4 | 71\r\n| ACCCCCX | 5 | 136\r\n| ACCCCCCCCCCCCCCX | 14 | 65,553\r\n\r\n\r\nBy the time the string includes 14 C's, the engine has to take over 65,000 steps just to see if the string is valid. These extreme situations can cause them to work very slowly (exponentially related to input size, as shown above), allowing an attacker to exploit this and can cause the service to excessively consume CPU, resulting in a Denial of Service.\n\n## Remediation\n\nUpgrade `negotiator` to version 0.6.1 or higher.\n\n\n## References\n\n- [GitHub Commit](https://github.com/jshttp/negotiator/commit/26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c)\n\n- [OSWAP Advisory](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS)\n","disclosureTime":"2016-06-16T17:36:06Z","exploit":"Not Defined","fixedIn":["0.6.1"],"functions":[{"functionId":{"className":null,"filePath":"lib/language.js","functionName":"parseLanguage"},"version":["<0.6.1"]}],"functions_new":[{"functionId":{"filePath":"lib/language.js","functionName":"parseLanguage"},"version":["<0.6.1"]}],"id":"npm:negotiator:20160616","identifiers":{"ALTERNATIVE":["SNYK-JS-NEGOTIATOR-10104"],"CVE":["CVE-2016-10539"],"CWE":["CWE-400"],"NSP":[106]},"language":"js","modificationTime":"2019-12-02T14:39:17.044101Z","moduleName":"negotiator","packageManager":"npm","packageName":"negotiator","patches":[{"comments":[],"id":"patch:npm:negotiator:20160616:3","modificationTime":"2019-12-03T11:40:45.832684Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/negotiator/20160616/negotiator_20160616_0_3_26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c.patch"],"version":"0.1.0"},{"comments":[],"id":"patch:npm:negotiator:20160616:2","modificationTime":"2019-12-03T11:40:45.831546Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/negotiator/20160616/negotiator_20160616_0_2_26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c.patch"],"version":"<= 0.4.7 > 0.1.0"},{"comments":[],"id":"patch:npm:negotiator:20160616:1","modificationTime":"2019-12-03T11:40:45.830538Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/negotiator/20160616/negotiator_20160616_0_1_26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c.patch"],"version":"<= 0.5.3 > 0.4.7"},{"comments":[],"id":"patch:npm:negotiator:20160616:0","modificationTime":"2019-12-03T11:40:45.829417Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/negotiator/20160616/negotiator_20160616_0_0_26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c.patch"],"version":"0.6.0"}],"publicationTime":"2016-06-16T17:36:06Z","references":[{"title":"GitHub Commit","url":"https://github.com/jshttp/negotiator/commit/26a05ec15cf7d1fa56000d66ebe9c9a1a62cb75c"},{"title":"OSWAP Advisory","url":"https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS"}],"semver":{"vulnerable":["<0.6.1"]},"severity":"high","title":"Regular Expression Denial of Service (DoS)","from":["st@0.2.4","negotiator@0.2.8"],"upgradePath":["st@1.1.0","negotiator@0.6.1"],"version":"0.2.8","name":"negotiator","isUpgradable":true,"isPatchable":true,"isPinnable":false},{"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","alternativeIds":["SNYK-JS-ST-10012"],"creationTime":"2014-02-06T07:33:48Z","credit":["Charlie Somerville"],"cvssScore":5.3,"description":"## Overview\r\nVersions prior to 0.2.5 did not properly prevent path traversal. Literal dots in a path were resolved out, but url encoded dots were not. Thus, a request like ``` /%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd ``` would leak sensitive files and data from the server.\r\n\r\nAs of version 0.2.5, any ```'/../'``` in the request path, urlencoded or not, will be replaced with ```'/'```. If your application depends on url traversal, then you are encouraged to please refactor so that you do not depend on having ```..``` in url paths, as this tends to expose data that you may be surprised to be exposing.\r\n\r\n## Details\r\nA Directory Traversal attack (also known as path traversal) aims to access files and directories that are stored outside the intended folder. By manipulating files with \"dot-dot-slash (../)\" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on file system, including application source code, configuration, and other critical system files.\r\n\r\nDirectory Traversal vulnerabilities can be generally divided into two types:\r\n\r\n- **Information Disclosure**: Allows the attacker to gain information about the folder structure or read the contents of sensitive files on the system.\r\n\r\n`st` is a module for serving static files on web pages, and contains a [vulnerability of this type](https://snyk.io/vuln/npm:st:20140206). In our example, we will serve files from the `public` route.\r\n\r\nIf an attacker requests the following URL from our server, it will in turn leak the sensitive private key of the root user.\r\n\r\n```\r\ncurl http://localhost:8080/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa\r\n```\r\n**Note** `%2e` is the URL encoded version of `.` (dot).\r\n\r\n- **Writing arbitrary files**: Allows the attacker to create or replace existing files. This type of vulnerability is also known as `Zip-Slip`. \r\n\r\nOne way to achieve this is by using a malicious `zip` archive that holds path traversal filenames. When each filename in the zip archive gets concatenated to the target extraction folder, without validation, the final path ends up outside of the target folder. If an executable or a configuration file is overwritten with a file containing malicious code, the problem can turn into an arbitrary code execution issue quite easily.\r\n\r\nThe following is an example of a `zip` archive with one benign file and one malicious file. Extracting the malicious file will result in traversing out of the target folder, ending up in `/root/.ssh/` overwriting the `authorized_keys` file:\r\n\r\n```\r\n2018-04-15 22:04:29 ..... 19 19 good.txt\r\n2018-04-15 22:04:42 ..... 20 20 ../../../../../../root/.ssh/authorized_keys\r\n```\r\n\r\n\r\n## Remediation\r\nUpgrade to version 0.2.5 or greater.\r\n\r\n## References\r\n- https://github.com/isaacs/st#security-status\r\n- http://blog.npmjs.org/post/80277229932/newly-paranoid-maintainers","disclosureTime":"2014-02-06T07:33:48Z","exploit":"Not Defined","fixedIn":["0.2.5"],"functions":[{"functionId":{"className":null,"filePath":"st.js","functionName":"Mount.prototype.getPath"},"version":[">0.0.1 <0.2.5"]}],"functions_new":[{"functionId":{"filePath":"st.js","functionName":"Mount.prototype.getPath"},"version":[">0.0.1 <0.2.5"]}],"id":"npm:st:20140206","identifiers":{"ALTERNATIVE":["SNYK-JS-ST-10012"],"CVE":["CVE-2014-3744"],"CWE":["CWE-22"],"NSP":[36]},"language":"js","modificationTime":"2019-03-05T12:09:02.337335Z","moduleName":"st","packageManager":"npm","packageName":"st","patches":[{"comments":[],"id":"patch:npm:st:20140206:0","modificationTime":"2019-12-03T11:40:45.736720Z","urls":["https://snyk-patches.s3.amazonaws.com/npm/st/20140206/st-20140206_0_0_6b54ce2d2fb912eadd31e2c25c65456d2c8666e1.patch"],"version":"<0.2.5 >0.1.4"}],"publicationTime":"2014-02-06T07:33:48Z","references":[{"title":"BLOG.NPMJS.ORG","url":"http://blog.npmjs.org/post/80277229932/newly-paranoid-maintainers"},{"title":"GITHUB.COM","url":"https://github.com/isaacs/st%23security-status"},{"title":"GitHub Commit","url":"https://github.com/isaacs/st/commit/6b54ce2d2fb912eadd31e2c25c65456d2c8666e1"}],"semver":{"vulnerable":["<0.2.5"]},"severity":"medium","title":"Directory Traversal","from":["st@0.2.4"],"upgradePath":["st@0.2.5"],"version":"0.2.4","name":"st","isUpgradable":true,"isPatchable":true,"isPinnable":false},{"CVSSv3":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N/E:H/RL:O/RC:C","alternativeIds":["SNYK-JS-ST-10820"],"creationTime":"2017-10-13T18:54:37Z","credit":["Xin Gao"],"cvssScore":4.3,"description":"## Overview\n[`st`](https://www.npmjs.com/package/st) is a module for serving static files.\n\nAffected versions of this package are vulnerable to Open Redirect. A malicious user could send a specially crafted request, which would automatically redirect the request to another domain, controlled by the attacker.\n\n**Note:** `st` will only redirect if requests are served from the root(`/`) and not from a subdirectory\n\n## References\n- [GitHub Commit](https://github.com/isaacs/st/commit/579960c629f12a27428e2da84c54f517e37b0a16)\n","disclosureTime":"2017-10-13T23:01:42Z","exploit":"High","fixedIn":["1.2.2"],"functions":[],"functions_new":[],"id":"npm:st:20171013","identifiers":{"ALTERNATIVE":["SNYK-JS-ST-10820"],"CVE":["CVE-2017-16224"],"CWE":["CWE-601"],"NSP":[547]},"language":"js","modificationTime":"2019-05-30T14:51:34.696578Z","moduleName":"st","packageManager":"npm","packageName":"st","patches":[],"publicationTime":"2017-10-15T07:10:40.818000Z","references":[{"title":"GitHub Commit","url":"https://github.com/isaacs/st/commit/579960c629f12a27428e2da84c54f517e37b0a16"}],"semver":{"vulnerable":["<1.2.2"]},"severity":"medium","title":"Open Redirect","from":["st@0.2.4"],"upgradePath":["st@1.2.2"],"version":"0.2.4","name":"st","isUpgradable":true,"isPatchable":false,"isPinnable":false}],"numDependencies":6,"severityMap":{"high":1,"medium":2,"low":1},"org":"remy","packageManager":"npm","summary":"4 vulnerable dependency paths","alerts":[{"msg":"Snyk CLI supports Node.js v8.0.0 and higher. Support for Node.js v6.5.0+ is temporarily available, but will be discontinued after January 2020. Please upgrade your runtime version in order to get Snyk CLI updates.The latest CLI version that supports Node.js 4 is `snyk@1.88.2`.","name":"env-deprecation","type":"info"}]}
2 |
--------------------------------------------------------------------------------
/test/fixtures/webpack.json:
--------------------------------------------------------------------------------
1 | {"ok":false,"vulnerabilities":[{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":true,"isPatchable":false,"isPinnable":false},{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","terser-webpack-plugin@1.4.3","cacache@12.0.3","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,false,false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":false,"isPatchable":false,"isPinnable":false},{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","terser-webpack-plugin@1.4.3","cacache@12.0.3","move-concurrently@1.0.1","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,false,false,false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":false,"isPatchable":false,"isPinnable":false},{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","terser-webpack-plugin@1.4.3","cacache@12.0.3","move-concurrently@1.0.1","copy-concurrently@1.0.5","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,false,false,false,false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":false,"isPatchable":false,"isPinnable":false},{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","watchpack@1.6.0","chokidar@2.1.8","fsevents@1.2.11","node-pre-gyp@0.14.0","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,false,false,false,false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":false,"isPatchable":false,"isPinnable":false},{"CVSSv3":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L/E:P/RL:O/RC:C","alternativeIds":[],"creationTime":"2020-03-11T08:25:47.093051Z","credit":["Snyk Security Team"],"cvssScore":5.6,"description":"## Overview\n\n[minimist](https://www.npmjs.com/package/minimist) is a parse argument options module.\n\n\nAffected versions of this package are vulnerable to Prototype Pollution.\nThe library could be tricked into adding or modifying properties of `Object.prototype` using a `constructor` or `__proto__` payload.\r\n\r\n## PoC by Snyk\r\n```\r\nrequire('minimist')('--__proto__.injected0 value0'.split(' '));\r\nconsole.log(({}).injected0 === 'value0'); // true\r\n\r\nrequire('minimist')('--constructor.prototype.injected1 value1'.split(' '));\r\nconsole.log(({}).injected1 === 'value1'); // true\r\n```\n\n## Details\nPrototype Pollution is a vulnerability affecting JavaScript. Prototype Pollution refers to the ability to inject properties into existing JavaScript language construct prototypes, such as objects. JavaScript allows all Object attributes to be altered, including their magical attributes such as `_proto_`, `constructor` and `prototype`. An attacker manipulates these attributes to overwrite, or pollute, a JavaScript application object prototype of the base object by injecting other values. Properties on the `Object.prototype` are then inherited by all the JavaScript objects through the prototype chain. When that happens, this leads to either denial of service by triggering JavaScript exceptions, or it tampers with the application source code to force the code path that the attacker injects, thereby leading to remote code execution.\r\n\r\nThere are two main ways in which the pollution of prototypes occurs:\r\n\r\n- Unsafe `Object` recursive merge\r\n \r\n- Property definition by path\r\n \r\n\r\n### Unsafe Object recursive merge\r\n\r\nThe logic of a vulnerable recursive merge function follows the following high-level model:\r\n```\r\nmerge (target, source)\r\n\r\n foreach property of source\r\n\r\n if property exists and is an object on both the target and the source\r\n\r\n merge(target[property], source[property])\r\n\r\n else\r\n\r\n target[property] = source[property]\r\n```\r\n
\r\n\r\nWhen the source object contains a property named `_proto_` defined with `Object.defineProperty()` , the condition that checks if the property exists and is an object on both the target and the source passes and the merge recurses with the target, being the prototype of `Object` and the source of `Object` as defined by the attacker. Properties are then copied on the `Object` prototype.\r\n\r\nClone operations are a special sub-class of unsafe recursive merges, which occur when a recursive merge is conducted on an empty object: `merge({},source)`.\r\n\r\n`lodash` and `Hoek` are examples of libraries susceptible to recursive merge attacks.\r\n\r\n### Property definition by path\r\n\r\nThere are a few JavaScript libraries that use an API to define property values on an object based on a given path. The function that is generally affected contains this signature: `theFunction(object, path, value)`\r\n\r\nIf the attacker can control the value of “path”, they can set this value to `_proto_.myValue`. `myValue` is then assigned to the prototype of the class of the object.\r\n\r\n## Types of attacks\r\n\r\nThere are a few methods by which Prototype Pollution can be manipulated:\r\n\r\n| Type |Origin |Short description |\r\n|--|--|--|\r\n| **Denial of service (DoS)**|Client |This is the most likely attack.
DoS occurs when `Object` holds generic functions that are implicitly called for various operations (for example, `toString` and `valueOf`).
The attacker pollutes `Object.prototype.someattr` and alters its state to an unexpected value such as `Int` or `Object`. In this case, the code fails and is likely to cause a denial of service.
**For example:** if an attacker pollutes `Object.prototype.toString` by defining it as an integer, if the codebase at any point was reliant on `someobject.toString()` it would fail. |\r\n |**Remote Code Execution**|Client|Remote code execution is generally only possible in cases where the codebase evaluates a specific attribute of an object, and then executes that evaluation.
**For example:** `eval(someobject.someattr)`. In this case, if the attacker pollutes `Object.prototype.someattr` they are likely to be able to leverage this in order to execute code.|\r\n|**Property Injection**|Client|The attacker pollutes properties that the codebase relies on for their informative value, including security properties such as cookies or tokens.
**For example:** if a codebase checks privileges for `someuser.isAdmin`, then when the attacker pollutes `Object.prototype.isAdmin` and sets it to equal `true`, they can then achieve admin privileges.|\r\n\r\n## Affected environments\r\n\r\nThe following environments are susceptible to a Prototype Pollution attack:\r\n\r\n- Application server\r\n \r\n- Web server\r\n \r\n\r\n## How to prevent\r\n\r\n1. Freeze the prototype— use `Object.freeze (Object.prototype)`.\r\n \r\n2. Require schema validation of JSON input.\r\n \r\n3. Avoid using unsafe recursive merge functions.\r\n \r\n4. Consider using objects without prototypes (for example, `Object.create(null)`), breaking the prototype chain and preventing pollution.\r\n \r\n5. As a best practice use `Map` instead of `Object`.\r\n\r\n### For more information on this vulnerability type:\r\n\r\n[Arteau, Oliver. “JavaScript prototype pollution attack in NodeJS application.” GitHub, 26 May 2018](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf)\n\n## Remediation\n\nUpgrade `minimist` to version 0.2.1, 1.2.2 or higher.\n\n\n## References\n\n- [Command Injection PoC](https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a)\n\n- [GitHub Fix Commit](https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94)\n","disclosureTime":"2020-03-10T08:22:24Z","exploit":"Proof of Concept","fixedIn":["0.2.1","1.2.2"],"functions":[],"functions_new":[],"id":"SNYK-JS-MINIMIST-559764","identifiers":{"CVE":["CVE-2020-7598"],"CWE":["CWE-400"]},"language":"js","modificationTime":"2020-03-13T09:58:42.256410Z","moduleName":"minimist","packageManager":"npm","packageName":"minimist","patches":[],"publicationTime":"2020-03-11T08:22:19Z","references":[{"title":"Command Injection PoC","url":"https://gist.github.com/Kirill89/47feb345b09bf081317f08dd43403a8a"},{"title":"GitHub Fix Commit","url":"https://github.com/substack/minimist/commit/63e7ed05aa4b1889ec2f3b196426db4500cbda94"}],"semver":{"vulnerable":["<0.2.1",">=1.0.0 <1.2.2"]},"severity":"medium","title":"Prototype Pollution","from":["webpack@4.39.0","watchpack@1.6.0","chokidar@2.1.8","fsevents@1.2.11","node-pre-gyp@0.14.0","tar@4.4.13","mkdirp@0.5.1","minimist@0.0.8"],"upgradePath":[false,false,false,false,false,false,"mkdirp@1.0.0"],"version":"0.0.8","name":"minimist","isUpgradable":false,"isPatchable":false,"isPinnable":false}],"numDependencies":342,"severityMap":{"high":0,"medium":1,"low":0},"org":"remy","packageManager":"npm","summary":"6 vulnerable dependency paths","alerts":[{"msg":"Snyk CLI supports Node.js v8.0.0 and higher. Support for Node.js v6.5.0+ is temporarily available, but will be discontinued after January 2020. Please upgrade your runtime version in order to get Snyk CLI updates.The latest CLI version that supports Node.js 4 is `snyk@1.88.2`.","name":"env-deprecation","type":"info"}]}
2 |
--------------------------------------------------------------------------------