├── .github
├── snyk-logo.png
├── pie-my-vulns-screenshot.png
├── ISSUE_TEMPLATE
│ ├── 3-help.md
│ ├── 2-feature-request.md
│ └── 1-bug-report.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── pie-my-vulns-logo.svg
├── Dockerfile
├── .prettierrc.js
├── jest.e2e.js
├── __tests__
├── __e2e__
│ ├── project3
│ │ ├── package.json
│ │ └── package-lock.json
│ ├── project1
│ │ ├── package.json
│ │ └── package-lock.json
│ ├── project2
│ │ └── package.json
│ └── cli.e2e.js
├── Reporters
│ └── DependencyTypeReporter.test.js
└── Audit.test.js
├── jest.default.js
├── jsdoc.json
├── codefresh.yml
├── src
├── Parsers
│ ├── SeverityParser.js
│ ├── DependencyTypeParser.js
│ └── RemediationTypeParser.js
├── Reporters
│ ├── DependencyTypeReporter.js
│ ├── SeverityReporter.js
│ └── RemediationTypeReporter.js
└── Audit.js
├── .dockerignore
├── CONTRIBUTING.md
├── .gitignore
├── SECURITY.md
├── README.md
├── bin
└── pie-my-vulns.js
├── CODE_OF_CONDUCT.md
├── .circleci
└── config.yml
├── package.json
└── LICENSE
/.github/snyk-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lirantal/pie-my-vulns/HEAD/.github/snyk-logo.png
--------------------------------------------------------------------------------
/.github/pie-my-vulns-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lirantal/pie-my-vulns/HEAD/.github/pie-my-vulns-screenshot.png
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:12.16.2-alpine3.9
2 | WORKDIR /app
3 | COPY . /app
4 | RUN npm ci --production
5 | USER node
6 | ENTRYPOINT node ./bin/pie-my-vulns.js --directory=/tmp/tested-app
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/3-help.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: '⁉️ Need help?'
3 | about: Please describe the problem.
4 | ---
5 |
6 |
9 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | printWidth: 100,
3 | tabWidth: 2,
4 | singleQuote: true,
5 | semi: false,
6 | trailingComma: 'none',
7 | useTabs: false,
8 | bracketSpacing: true
9 | }
10 |
--------------------------------------------------------------------------------
/jest.e2e.js:
--------------------------------------------------------------------------------
1 | const jestConfig = require('./jest.default')
2 | jestConfig.testMatch = ['**/__e2e__/*.e2e.js']
3 | jestConfig.collectCoverage = false
4 | jestConfig.coverageThreshold = null
5 | jestConfig.testTimeout = 30000
6 | module.exports = jestConfig
7 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
6 |
7 | - **Library Version**:
8 | - **OS**:
9 | - **Node.js Version**:
10 |
11 |
12 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/project3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project3",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "react": "^16.12.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/jest.default.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | verbose: true,
4 | notify: true,
5 | collectCoverage: true,
6 | coverageThreshold: {
7 | global: {
8 | branches: 80,
9 | functions: 80,
10 | lines: 80,
11 | statements: 80
12 | }
13 | },
14 | testPathIgnorePatterns: ['/__tests__/.*/__fixtures__/.*'],
15 | collectCoverageFrom: ['index.js', 'src/**/*.{js,ts}'],
16 | testMatch: ['**/*.test.js']
17 | }
18 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/project1/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project1",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "helmet": "^2.3.0"
14 | },
15 | "devDependencies": {
16 | "timespan": "^2.3.0",
17 | "uglify-js": "^2.4.24"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/project2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project1",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "helmet": "^2.3.0"
14 | },
15 | "devDependencies": {
16 | "timespan": "^2.3.0",
17 | "uglify-js": "^2.4.24"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tags": {
3 | "allowUnknownTags": true,
4 | "dictionaries": ["jsdoc", "closure"]
5 | },
6 | "source": {
7 | "include": ["./index.js"],
8 | "exclude": ["./node_modules", "./.nyc_output", "./coverage", "./out", "./tests"],
9 | "includePattern": ".+\\.js(doc|x)?$",
10 | "excludePattern": "(^|\\/|\\\\)_"
11 | },
12 | "plugins": [],
13 | "templates": {},
14 | "opts": {
15 | "destination": "./out",
16 | "recurse": true,
17 | "private": true
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/2-feature-request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680 Feature request"
3 | about: Suggest an idea for this project
4 | ---
5 |
6 |
11 |
12 | **Is your feature request related to a problem? Please describe.**
13 | Please describe the problem you are trying to solve.
14 |
15 | **Describe the solution you'd like**
16 | Please describe the desired behavior.
17 |
18 | **Describe alternatives you've considered**
19 | Please describe alternative solutions or features you have considered.
20 |
--------------------------------------------------------------------------------
/codefresh.yml:
--------------------------------------------------------------------------------
1 | version: '1.0'
2 | steps:
3 | main_clone:
4 | type: git-clone
5 | repo: lirantal/pie-my-vulns
6 | BuildingDockerImage:
7 | title: Building Docker Image
8 | type: build
9 | registry: dockerhub
10 | image_name: lirantal/pie-my-vulns
11 | working_directory: ./
12 | dockerfile: Dockerfile
13 | tag: '${{CF_BRANCH_TAG_NORMALIZED}}'
14 | PushToRegistry:
15 | title: Pushing to Docker Registry
16 | type: push
17 | registry: dockerhub
18 | candidate: '${{BuildingDockerImage}}'
19 | tags:
20 | - master
21 | - latest
22 | when:
23 | branch:
24 | only:
25 | - master
26 | stages: []
27 |
--------------------------------------------------------------------------------
/src/Parsers/SeverityParser.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class VulnerabilitySeverityParser {
4 | constructor(vulnsData) {
5 | this.vulnsData = vulnsData
6 | this.vulnsMap = {
7 | high: 0,
8 | medium: 0,
9 | low: 0
10 | }
11 | }
12 |
13 | parse() {
14 | if (!this.vulnsData.vulnerabilities) {
15 | return this.vulnsMap
16 | }
17 |
18 | this.vulnsData.vulnerabilities.map(vulnItem => {
19 | this.vulnsMap[vulnItem.severity] += 1
20 | })
21 |
22 | return this.vulnsMap
23 | }
24 |
25 | getHighSeverityCount() {
26 | return this.vulnsMap['high']
27 | }
28 |
29 | getMediumSeverityCount() {
30 | return this.vulnsMap['medium']
31 | }
32 |
33 | getLowSeverityCount() {
34 | return this.vulnsMap['low']
35 | }
36 | }
37 |
38 | module.exports = VulnerabilitySeverityParser
39 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Directory for instrumented libs generated by jscoverage/JSCover
9 | lib-cov
10 |
11 | # Coverage directory used by tools like istanbul
12 | coverage
13 |
14 | # Dependency directories
15 | node_modules/
16 | jspm_packages/
17 |
18 | # Optional npm cache directory
19 | .npm
20 |
21 | # Optional eslint cache
22 | .eslintcache
23 |
24 | # Optional REPL history
25 | .node_repl_history
26 |
27 | # Output of 'npm pack'
28 | *.tgz
29 |
30 | # Yarn Integrity file
31 | .yarn-integrity
32 |
33 | # dotenv environment variables file
34 | .env
35 | .env.test
36 |
37 | # Tests
38 | __tests__
39 | jest*
40 |
41 | # Open source licence
42 | LICENCE
43 |
44 | # MD files
45 | *.md
46 |
47 | # Documentation and formatting
48 | .prettierrc.js
49 | jsdoc.json
50 |
51 | # Source control and CI/CD
52 | .circleci/
53 | .github/
54 | .gitignore
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
4 |
5 | The following is a set of guidelines for contributing to pie-my-vulns.
6 | These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
7 |
8 | ## Code of Conduct
9 |
10 | This project and everyone participating in it is governed by a [Code of Conduct](./CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.
11 |
12 | ## How to contribute to pie-my-vulns
13 |
14 |
15 |
16 | ### Tests
17 |
18 | Make sure you the code you're adding has decent test coverage.
19 |
20 | Running project tests and coverage:
21 |
22 | ```bash
23 | npm run test
24 | ```
25 |
26 | ### Commit Guidelines
27 |
28 | The project uses the commitizen tool for standardizing changelog style commit and a git pre-commit hook to enforce them.
29 |
--------------------------------------------------------------------------------
/src/Parsers/DependencyTypeParser.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-prototype-builtins */
2 | 'use strict'
3 |
4 | class DependencyTypeParser {
5 | constructor(vulnsData) {
6 | this.vulnsData = vulnsData
7 | this.vulnsMap = {
8 | prodDependency: 0,
9 | devDependency: 0
10 | }
11 | }
12 |
13 | parse() {
14 | if (!this.vulnsData.vulnerabilities) {
15 | return this.vulnsMap
16 | }
17 |
18 | const vulnItemScarce = this.vulnsData.vulnerabilities[0]
19 | if (vulnItemScarce.hasOwnProperty('parentDepType') !== true) {
20 | return false
21 | }
22 |
23 | this.vulnsData.vulnerabilities.map(vulnItem => {
24 | if (vulnItem.parentDepType === 'prod') {
25 | this.vulnsMap.prodDependency += 1
26 | } else {
27 | this.vulnsMap.devDependency += 1
28 | }
29 | })
30 | }
31 |
32 | getProdDependencyCount() {
33 | return this.vulnsMap['prodDependency']
34 | }
35 |
36 | getDevDependencyCount() {
37 | return this.vulnsMap['devDependency']
38 | }
39 | }
40 |
41 | module.exports = DependencyTypeParser
42 |
--------------------------------------------------------------------------------
/src/Parsers/RemediationTypeParser.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | class RemediationTypeParser {
4 | constructor(vulnsData) {
5 | this.vulnsData = vulnsData
6 | this.vulnsMap = {
7 | isPatchable: 0,
8 | isUpgradable: 0,
9 | none: 0
10 | }
11 | }
12 |
13 | parse() {
14 | if (!this.vulnsData.vulnerabilities) {
15 | return this.vulnsMap
16 | }
17 |
18 | this.vulnsData.vulnerabilities.map(vulnItem => {
19 | if (vulnItem.isUpgradable === true) {
20 | this.vulnsMap['isUpgradable'] += 1
21 | return true
22 | }
23 |
24 | if (vulnItem.isPatchable === true) {
25 | this.vulnsMap['isPatchable'] += 1
26 | return true
27 | }
28 |
29 | this.vulnsMap['none'] += 1
30 | return true
31 | })
32 |
33 | return this.vulnsMap
34 | }
35 |
36 | getUpgradableCount() {
37 | return this.vulnsMap['isUpgradable']
38 | }
39 |
40 | getPatchableCount() {
41 | return this.vulnsMap['isPatchable']
42 | }
43 |
44 | getNoRemediationCount() {
45 | return this.vulnsMap['none']
46 | }
47 | }
48 |
49 | module.exports = RemediationTypeParser
50 |
--------------------------------------------------------------------------------
/src/Reporters/DependencyTypeReporter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Pie = require('cli-pie')
4 | const DependencyTypeParser = require('../Parsers/DependencyTypeParser')
5 | const DEFAULT_PIE_SIZE = 4
6 |
7 | class DependencyTypeReporter {
8 | constructor({ data, pieSize, colorFul }) {
9 | this.options = {
10 | pieSize: pieSize || DEFAULT_PIE_SIZE,
11 | colorFul: !!colorFul
12 | }
13 | this.data = data
14 | }
15 |
16 | getTitle() {
17 | return `Vulnerabilities by dependency source:`
18 | }
19 |
20 | getResult() {
21 | const depTypeParser = new DependencyTypeParser(this.data)
22 | const res = depTypeParser.parse()
23 | if (res === false) {
24 | return false
25 | }
26 |
27 | var pieChart = new Pie(
28 | this.options.pieSize,
29 | [
30 | {
31 | label: 'Production Dependencies',
32 | value: depTypeParser.getProdDependencyCount(),
33 | color: [0, 255, 0]
34 | },
35 | {
36 | label: 'Development Dependencies',
37 | value: depTypeParser.getDevDependencyCount(),
38 | color: [0, 0, 255]
39 | }
40 | ],
41 | {
42 | legend: true,
43 | flat: true,
44 | no_ansi: !this.options.colorFul
45 | }
46 | )
47 |
48 | return pieChart.toString()
49 | }
50 | }
51 |
52 | module.exports = DependencyTypeReporter
53 |
--------------------------------------------------------------------------------
/src/Reporters/SeverityReporter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Pie = require('cli-pie')
4 | const VulnerabilitySeverityParser = require('../Parsers/SeverityParser')
5 | const DEFAULT_PIE_SIZE = 4
6 |
7 | class SeverityReporter {
8 | constructor({ data, pieSize, colorFul }) {
9 | this.options = {
10 | pieSize: pieSize || DEFAULT_PIE_SIZE,
11 | colorFul: !!colorFul
12 | }
13 | this.data = data
14 | }
15 |
16 | getTitle() {
17 | return `Vulnerabilities by severity:`
18 | }
19 |
20 | getResult() {
21 | const vulnSeverityParser = new VulnerabilitySeverityParser(this.data)
22 | vulnSeverityParser.parse()
23 |
24 | var pieChart = new Pie(
25 | this.options.pieSize,
26 | [
27 | {
28 | label: 'High severity',
29 | value: vulnSeverityParser.getHighSeverityCount(),
30 | color: [255, 0, 255]
31 | },
32 | {
33 | label: 'Medium severity',
34 | value: vulnSeverityParser.getMediumSeverityCount(),
35 | color: [179, 26, 107]
36 | },
37 | {
38 | label: 'Low severity',
39 | value: vulnSeverityParser.getLowSeverityCount(),
40 | color: [89, 87, 117]
41 | }
42 | ],
43 | {
44 | legend: true,
45 | flat: true,
46 | no_ansi: !this.options.colorFul
47 | }
48 | )
49 |
50 | return pieChart.toString()
51 | }
52 | }
53 |
54 | module.exports = SeverityReporter
55 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/1-bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Create a bug report
4 | ---
5 |
6 |
7 |
8 | ## Expected Behavior
9 |
10 |
11 |
12 |
13 | ## Current Behavior
14 |
15 |
16 |
17 |
18 | ## Possible Solution
19 |
20 |
21 |
22 |
23 | ## Steps to Reproduce (for bugs)
24 |
25 |
26 |
27 |
28 | 1.
29 | 2.
30 | 3.
31 | 4.
32 |
33 | ## Context
34 |
35 |
36 |
37 |
38 | ## Your Environment
39 |
40 |
41 |
42 | - Library Version used:
43 | - Node.js version (e.g. Node.js 5.4):
44 | - Operating System and version (desktop or mobile):
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 | .env.test
60 |
61 | # parcel-bundler cache (https://parceljs.org/)
62 | .cache
63 |
64 | # next.js build output
65 | .next
66 |
67 | # nuxt.js build output
68 | .nuxt
69 |
70 | # vuepress build output
71 | .vuepress/dist
72 |
73 | # Serverless directories
74 | .serverless/
75 |
76 | # FuseBox cache
77 | .fusebox/
78 |
79 | # DynamoDB Local files
80 | .dynamodb/
81 |
--------------------------------------------------------------------------------
/src/Reporters/RemediationTypeReporter.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const Pie = require('cli-pie')
4 | const RemediationTypeParser = require('../Parsers/RemediationTypeParser')
5 | const DEFAULT_PIE_SIZE = 4
6 |
7 | class RemediationTypeReporter {
8 | constructor({ data, pieSize, colorFul }) {
9 | this.options = {
10 | pieSize: pieSize || DEFAULT_PIE_SIZE,
11 | colorFul: !!colorFul
12 | }
13 | this.data = data
14 | }
15 |
16 | getTitle() {
17 | return `Vulnerabilities by remediation action:`
18 | }
19 |
20 | getResult() {
21 | const vulnSeverityParser = new RemediationTypeParser(this.data)
22 | vulnSeverityParser.parse()
23 |
24 | var pieChart = new Pie(
25 | this.options.pieSize,
26 | [
27 | {
28 | label: 'Upgradable vulnerabilities',
29 | value: vulnSeverityParser.getUpgradableCount(),
30 | color: [0, 255, 0]
31 | },
32 | {
33 | label: 'Patchable vulnerabilities',
34 | value: vulnSeverityParser.getPatchableCount(),
35 | color: [0, 0, 255]
36 | },
37 | {
38 | label: 'No remediation available',
39 | value: vulnSeverityParser.getNoRemediationCount(),
40 | color: [255, 0, 50]
41 | }
42 | ],
43 | {
44 | legend: true,
45 | flat: true,
46 | no_ansi: !this.options.colorFul
47 | }
48 | )
49 |
50 | return pieChart.toString()
51 | }
52 | }
53 |
54 | module.exports = RemediationTypeReporter
55 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Responsible disclosure security policy
4 |
5 | A responsible disclosure policy helps protect users of the project from publicly disclosed security vulnerabilities without a fix by employing a process where vulnerabilities are first triaged in a private manner, and only publicly disclosed after a reasonable time period that allows patching the vulnerability and provides an upgrade path for users.
6 |
7 | When contacting us directly via email, we will do our best efforts to respond in a reasonable time to resolve the issue. When contacting a security program their disclosure policy will provide details on time-frame, processes and paid bounties.
8 |
9 | We kindly ask you to refrain from malicious acts that put our users, the project, or any of the project’s team members at risk.
10 |
11 | ## Reporting a security issue
12 |
13 | We consider the security of our systems a top priority. But no matter how much effort we put into system security, there can still be vulnerabilities present.
14 |
15 | If you discover a security vulnerability, please use one of the following means of communications to report it to us:
16 |
17 | - Report the security issue to the Node.js Security WG through the [HackerOne program](https://hackerone.com/nodejs-ecosystem) for ecosystem modules on npm, or to [Snyk Security Team](https://snyk.io/vulnerability-disclosure). They will help triage the security issue and work with all involved parties to remediate and release a fix.
18 |
19 | Note that time-frame and processes are subject to each program’s own policy.
20 |
21 | - Report the security issue to the project maintainers directly.
22 |
23 | Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions.
24 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Types of changes
8 |
9 |
10 |
11 | - [ ] Bug fix (non-breaking change which fixes an issue)
12 | - [ ] New feature (non-breaking change which adds functionality)
13 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
14 |
15 | ## Related Issue
16 |
17 |
18 |
19 |
20 |
21 |
22 | ## Motivation and Context
23 |
24 |
25 |
26 | ## How Has This Been Tested?
27 |
28 |
29 |
30 |
31 |
32 |
33 | ## Screenshots (if appropriate):
34 |
35 | ## Checklist:
36 |
37 |
38 |
39 |
40 | - [ ] I have updated the documentation (if required).
41 | - [ ] I have read the **CONTRIBUTING** document.
42 | - [ ] I have added tests to cover my changes.
43 | - [ ] All new and existing tests passed.
44 | - [ ] I added a picture of a cute animal cause it's fun
45 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/project3/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project3",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "js-tokens": {
8 | "version": "4.0.0",
9 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
10 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
11 | },
12 | "loose-envify": {
13 | "version": "1.4.0",
14 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
15 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
16 | "requires": {
17 | "js-tokens": "^3.0.0 || ^4.0.0"
18 | }
19 | },
20 | "object-assign": {
21 | "version": "4.1.1",
22 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
23 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
24 | },
25 | "prop-types": {
26 | "version": "15.7.2",
27 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
28 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
29 | "requires": {
30 | "loose-envify": "^1.4.0",
31 | "object-assign": "^4.1.1",
32 | "react-is": "^16.8.1"
33 | }
34 | },
35 | "react": {
36 | "version": "16.12.0",
37 | "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz",
38 | "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==",
39 | "requires": {
40 | "loose-envify": "^1.1.0",
41 | "object-assign": "^4.1.1",
42 | "prop-types": "^15.6.2"
43 | }
44 | },
45 | "react-is": {
46 | "version": "16.12.0",
47 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
48 | "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/__tests__/Reporters/DependencyTypeReporter.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable security/detect-object-injection */
2 | const DepTypeReporter = require('../../src/Reporters/DependencyTypeReporter')
3 | const RemediationTypeReporter = require('../../src/Reporters/RemediationTypeReporter')
4 | const SevReporter = require('../../src/Reporters/SeverityReporter')
5 |
6 | const reporterFunctions = [
7 | ['DepTypeReporter', DepTypeReporter],
8 | ['RemediationTypeReporter', RemediationTypeReporter],
9 | ['SevReporter', SevReporter]
10 | ]
11 |
12 | const data = require('../__fixtures__/output.json')
13 |
14 | describe('Reporters', () => {
15 | describe('Reporter instantiation', () => {
16 | test.each(reporterFunctions)(
17 | '%s: constructor correctly sets data',
18 | (reporterName, Reporter) => {
19 | const reporter = new Reporter({
20 | data,
21 | pieSize: 1,
22 | colorFul: false
23 | })
24 |
25 | expect(reporter.options.pieSize).toBe(1)
26 | expect(reporter.options.colorFul).toBe(false)
27 | }
28 | )
29 | })
30 |
31 | describe('Reporter titles', () => {
32 | const expectedReporterTitle = {
33 | DepTypeReporter: 'Vulnerabilities by dependency source:',
34 | RemediationTypeReporter: 'Vulnerabilities by remediation action:',
35 | SevReporter: 'Vulnerabilities by severity:'
36 | }
37 |
38 | test.each(reporterFunctions)('%s: correctly returns a title', (reporterName, Reporter) => {
39 | const reporter = new Reporter({
40 | data
41 | })
42 |
43 | expect(reporter.getTitle()).toBe(expectedReporterTitle[reporterName])
44 | })
45 | })
46 |
47 | describe('Reporter results', () => {
48 | const expectedStringInText = {
49 | DepTypeReporter: [
50 | /Production Dependencies \(14\.75%\)/,
51 | /Development Dependencies \(85\.25%\)/
52 | ],
53 | RemediationTypeReporter: [
54 | /Upgradable vulnerabilities \(53\.92%\)/,
55 | /Patchable vulnerabilities \(9\.22%\)/,
56 | /No remediation available \(36\.87%\)/
57 | ],
58 | SevReporter: [
59 | /High severity \(43\.32%\)/,
60 | /Medium severity \(41\.01%\)/,
61 | /Low severity \(15\.67%\)/
62 | ]
63 | }
64 |
65 | test.each(reporterFunctions)('%s: returns a piechart string', (reporterName, Reporter) => {
66 | const reporter = new Reporter({
67 | data
68 | })
69 |
70 | const pieChartString = reporter.getResult()
71 |
72 | expectedStringInText[reporterName].forEach(regexPattern => {
73 | expect(pieChartString).toMatch(regexPattern)
74 | })
75 | })
76 | })
77 | })
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | pie-my-vulns
3 |
4 |
5 |
6 | Visualize your project security vulnerabilities as a pie chart in the terminal
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
Many thanks to
for supporting open source security
24 |
25 |
26 |
27 | # About
28 |
29 | Visualize your project security vulnerabilities as a pie chart in the terminal
30 |
31 | # Usage
32 |
33 | ## Command line
34 |
35 | Using Node.js's npx command to run a one-off scan inside a project's directory:
36 |
37 | ```bash
38 | npx pie-my-vulns
39 | ```
40 |
41 | To scan a specific project directory use the `--directory` option, for example:
42 |
43 | ```bash
44 | npx pie-my-vulns --directory=path/to/project/dir
45 | ```
46 |
47 | To pipe existing Snyk json (obtained via `snyk test --json`) through stdin
48 |
49 | ```bash
50 | cat snyk.json | npx pie-my-vulns
51 | ```
52 |
53 | ## Docker container
54 |
55 | Using docker to run a one-off scan inside a docker container.
56 | Pass the directory to scan to the `source` environment variable:
57 |
58 | ```
59 | docker run --mount type=bind,source=,target=/tmp/tested-app pie-my-vulns
60 | ```
61 |
62 | # Install
63 |
64 | You can install globally via:
65 |
66 | ```bash
67 | npm install -g pie-my-vulns
68 | ```
69 |
70 | # Contributing
71 |
72 | Please consult [CONTRIBUTING](./CONTRIBUTING.md) for guidelines on contributing to this project.
73 |
74 | # Author
75 |
76 | **pie-my-vulns** © [Liran Tal](https://github.com/lirantal), Released under the [Apache-2.0](./LICENSE) License.
77 |
--------------------------------------------------------------------------------
/bin/pie-my-vulns.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | /* eslint-disable no-process-exit */
3 | 'use strict'
4 | const parseArgs = require('minimist')
5 | const fs = require('fs')
6 | const debug = require('debug')('pie-my-vulns')
7 | const Audit = require('../src/Audit')
8 | const SeverityReporter = require('../src/Reporters/SeverityReporter')
9 | const RemediationTypeReporter = require('../src/Reporters/RemediationTypeReporter')
10 | const DependencyTypeReporter = require('../src/Reporters/DependencyTypeReporter')
11 |
12 | const EXIT_CODE_ERROR = 1
13 | const EXIT_CODE_VULNS = 2
14 | const EXIT_CODE_VULNS_NONE = 0
15 | const STDIN_FILE_DESCRIPTOR = 0
16 | const reportsList = [SeverityReporter, DependencyTypeReporter, RemediationTypeReporter]
17 |
18 | async function main() {
19 | let vulnerabilitiesResult = ''
20 |
21 | try {
22 | // Let's check if there's any input being piped from stdin
23 | if (fs.fstatSync(STDIN_FILE_DESCRIPTOR).isFIFO()) {
24 | vulnerabilitiesResult = await readFromStdin()
25 | } else {
26 | const argv = parseArgs(process.argv.slice(2))
27 | const { directory } = argv
28 | const audit = new Audit()
29 | vulnerabilitiesResult = await audit.test({ directory })
30 | }
31 | } catch (error) {
32 | printError(error)
33 | }
34 |
35 | console.log()
36 |
37 | if (!isVulnerabilitiesDetected(vulnerabilitiesResult)) {
38 | console.log('0 vulnerabilities found')
39 | process.exit(EXIT_CODE_VULNS_NONE)
40 | }
41 |
42 | reportsList.forEach(Reporter => {
43 | const reporter = new Reporter({
44 | data: vulnerabilitiesResult,
45 | colorFul: true
46 | })
47 |
48 | const stdoutText = reporter.getResult()
49 | if (stdoutText !== false) {
50 | console.log(reporter.getTitle())
51 | console.log(stdoutText)
52 | console.log()
53 | }
54 | })
55 |
56 | console.log('Summary:')
57 | console.log(
58 | ` - [${vulnerabilitiesResult.vulnerabilities.length}] Total number of vulnerabilities found`
59 | )
60 | console.log(
61 | ` - [${vulnerabilitiesResult.dependencyCount}] Total number of dependencies scanned\n`
62 | )
63 |
64 | process.exit(EXIT_CODE_VULNS)
65 | }
66 |
67 | async function readFromStdin() {
68 | return new Promise(resolve => {
69 | let data = ''
70 | const stdin = process.stdin
71 | stdin.on('readable', function() {
72 | const chunk = this.read()
73 | if (chunk !== null) {
74 | data += chunk
75 | }
76 | })
77 | stdin.on('end', function() {
78 | return resolve(JSON.parse(data))
79 | })
80 | })
81 | }
82 |
83 | function printError(error) {
84 | const githubIssueURL = 'https://github.com/lirantal/pie-my-vulns/issues'
85 |
86 | debug(error)
87 | console.error()
88 | console.error(`Unexpected failure: ${error.message}`)
89 | console.error()
90 | console.error(`To enable debug information invoke the CLI with a DEBUG=pie* prefix.`)
91 | console.error()
92 | console.error(`Please open an issue at: ${githubIssueURL}`)
93 |
94 | process.exit(EXIT_CODE_ERROR)
95 | }
96 |
97 | function isVulnerabilitiesDetected(vulnsData) {
98 | return vulnsData && vulnsData.vulnerabilities && vulnsData.vulnerabilities.length > 0
99 | }
100 |
101 | main().catch(error => {
102 | printError(error)
103 | })
104 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | education, socio-economic status, nationality, personal appearance, race,
10 | religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | - Using welcoming and inclusive language
18 | - Being respectful of differing viewpoints and experiences
19 | - Gracefully accepting constructive criticism
20 | - Focusing on what is best for the community
21 | - Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | - The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | - Trolling, insulting/derogatory comments, and personal or political attacks
28 | - Public or private harassment
29 | - Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | - Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at liran.tal@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/cli.e2e.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable security/detect-child-process */
2 | const os = require('os')
3 | const path = require('path')
4 | const util = require('util')
5 | const childProcess = require('child_process')
6 | const spawnAsync = util.promisify(childProcess.execFile)
7 | const exec = childProcess.execSync
8 |
9 | const cliBinPath = path.join(__dirname, '../../bin/pie-my-vulns.js')
10 |
11 | describe('End-to-End CLI', () => {
12 | beforeAll(() => {
13 | let cmdForToken = `npx snyk config set "api=$SNYK_TEST_TOKEN"`
14 | if (os.platform() === 'win32') {
15 | cmdForToken = `npx snyk config set "api=%SNYK_TEST_TOKEN%"`
16 | }
17 | exec(cmdForToken)
18 | })
19 |
20 | test('CLI should return error code 2 when vulnerabilities are found', async () => {
21 | expect.assertions(1)
22 |
23 | try {
24 | await spawnAsync('node', [cliBinPath], {
25 | cwd: path.join(__dirname, 'project1')
26 | })
27 | } catch (err) {
28 | expect(err.code).toBe(2) // means that vulnerabilities were found
29 | }
30 | })
31 |
32 | test('CLI should show vulnerabilities breakdown numbers and their titles', async () => {
33 | expect.hasAssertions()
34 |
35 | try {
36 | await spawnAsync('node', [cliBinPath], {
37 | cwd: path.join(__dirname, 'project1')
38 | })
39 | } catch (err) {
40 | expect(err.stdout).toContain('Medium severity (20.00%)')
41 | expect(err.stdout).toContain('High severity (0.00%)')
42 | expect(err.stdout).toContain('Low severity (80.00%)')
43 | expect(err.stdout).toContain('Patchable vulnerabilities (0.00%)')
44 | expect(err.stdout).toContain('No remediation available (0.00%)')
45 | expect(err.stdout).toContain('Total number of vulnerabilities found')
46 | expect(err.stdout).toContain('Total number of dependencies scanned')
47 | expect(err.stdout).toContain('Vulnerabilities by severity:')
48 | expect(err.stdout).toContain('Vulnerabilities by remediation action:')
49 | }
50 | })
51 |
52 | test('CLI should return error code 1 when there is an issue', async () => {
53 | expect.assertions(2)
54 |
55 | try {
56 | await spawnAsync('node', [cliBinPath], {
57 | cwd: path.join(__dirname, 'project2')
58 | })
59 | } catch (err) {
60 | expect(err.code).toBe(1)
61 | expect(err.stderr).toContain('Unexpected failure: missing node_modules folders')
62 | }
63 | })
64 |
65 | test('CLI should return error code 0 when no vulnerabilities are found', async () => {
66 | const { stdout, err } = await spawnAsync('node', [cliBinPath], {
67 | cwd: path.join(__dirname, 'project3')
68 | })
69 | expect(err).toBe(undefined)
70 | expect(stdout).toContain('0 vulnerabilities found')
71 | })
72 |
73 | test('CLI should accept path to project directory from command argument', async () => {
74 | const project3Dir = path.join(__dirname, 'project3')
75 | const { stdout, err } = await spawnAsync('node', [cliBinPath, '--directory', project3Dir], {
76 | cwd: path.join(__dirname, 'project1')
77 | })
78 |
79 | expect(err).toBe(undefined)
80 | expect(stdout).toContain('0 vulnerabilities found')
81 | })
82 |
83 | if (os.platform() === 'linux') {
84 | test('CLI should be able to read Snyk JSON from stdin', async () => {
85 | try {
86 | await exec(`cat snyk.json | node ${cliBinPath}`, {
87 | cwd: __dirname
88 | })
89 | } catch (err) {
90 | expect(err.status).toBe(2) // means that vulnerabilities were found
91 | }
92 | })
93 | }
94 | })
95 |
--------------------------------------------------------------------------------
/src/Audit.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable security/detect-child-process */
2 | 'use strict'
3 | const Ora = require('ora')
4 |
5 | const path = require('path')
6 | const Util = require('util')
7 | const ChildProcess = require('child_process')
8 |
9 | const nodeCliCommand = 'node'
10 | const auditCliCommand = path.join(__dirname, '../node_modules/snyk/dist/cli/index.js')
11 | const auditCliArgs = [auditCliCommand, 'test', '--json']
12 | const ERROR_VULNS_FOUND = 1
13 | const ERROR_UNAUTHENTICATED = 2
14 | const JSON_BUFFER_SIZE = 50 * 1024 * 1024
15 |
16 | const shellEnvVariables = Object.assign({}, process.env, {
17 | SNYK_UTM_MEDIUM: 'cli',
18 | SNYK_UTM_SOURCE: 'cli',
19 | SNYK_UTM_CAMPAIGN: 'pie-my-vulns'
20 | })
21 |
22 | class Audit {
23 | async authenticate() {
24 | return new Promise((resolve, reject) => {
25 | const process = ChildProcess.execFile(nodeCliCommand, [auditCliCommand, 'auth'], {
26 | env: shellEnvVariables
27 | })
28 | process.stdout.on('data', chunk => {
29 | const httpsLinkMatch = chunk.match(/https:\/\/.*/g)
30 | if (httpsLinkMatch && httpsLinkMatch.length > 0) {
31 | const httpsLink = httpsLinkMatch[0].trim()
32 | console.log()
33 | console.log(`or you can hit this link:`)
34 | console.log(` ${httpsLink}`)
35 | }
36 | })
37 |
38 | process.on('close', () => {
39 | return resolve()
40 | })
41 |
42 | process.on('error', error => {
43 | return reject(new Error(error))
44 | })
45 | })
46 | }
47 |
48 | async test({ directory = '' } = {}, firstScan = true) {
49 | const testSpinner = new Ora({})
50 |
51 | // Start the spinner only for the first invocation
52 | if (firstScan) {
53 | console.log()
54 | testSpinner.start('Scanning project dependencies')
55 | }
56 |
57 | const ExecFile = Util.promisify(ChildProcess.execFile)
58 | const args = [...auditCliArgs, ...(directory ? [directory] : [])]
59 | let testResults = []
60 | try {
61 | // allow for 50MB of buffer for a large JSON output
62 | await ExecFile(nodeCliCommand, args, {
63 | maxBuffer: JSON_BUFFER_SIZE,
64 | env: shellEnvVariables
65 | })
66 | } catch (error) {
67 | const errorMessage = error.stdout
68 |
69 | // error: can't detect package manifest
70 | if (errorMessage && errorMessage.indexOf('Could not detect supported target files') !== -1) {
71 | testSpinner.fail('Scan failed')
72 | throw new Error(`can't detect package manifest files\ntry running in the project's rootdir`)
73 | }
74 |
75 | if (errorMessage && errorMessage.indexOf("we can't test without dependencies") !== -1) {
76 | testSpinner.fail('Scan failed')
77 | throw new Error(`missing node_modules folders\ninstall dependencies and try again`)
78 | }
79 |
80 | if (error.code === ERROR_VULNS_FOUND) {
81 | // we are authenticated as a user for Snyk
82 | // but vulnerabilities have been found
83 | testResults = JSON.parse(error.stdout)
84 | } else if (error.code === ERROR_UNAUTHENTICATED) {
85 | // user is not authenticated and we need
86 | // to trigger the auth process
87 | testSpinner.stop()
88 |
89 | console.log(`Seems like you're not authenticated to Snyk,`)
90 | console.log(`so redirecting you now and after login I'll show scan results here`)
91 | await this.authenticate()
92 | return this.test({ directory }, false)
93 | } else {
94 | throw error
95 | }
96 | }
97 |
98 | testSpinner.succeed('Scan completed successfully')
99 | return testResults
100 | }
101 | }
102 |
103 | module.exports = Audit
104 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | win: circleci/windows@2
5 |
6 | jobs:
7 | build:
8 | working_directory: ~/project
9 | docker:
10 | - image: circleci/node:12
11 | steps:
12 | - checkout
13 | - restore_cache:
14 | key: dependency-cache-{{ checksum "package-lock.json" }}
15 | - run:
16 | name: install
17 | command: npm install --ignore-engines
18 | - save_cache:
19 | key: dependency-cache-{{ checksum "package-lock.json" }}
20 | paths:
21 | - ./node_modules
22 | - persist_to_workspace:
23 | root: ~/project
24 | paths:
25 | - ./node_modules
26 |
27 | lint:
28 | working_directory: ~/project
29 | docker:
30 | - image: circleci/node:12
31 | steps:
32 | - checkout
33 | - attach_workspace:
34 | at: ./
35 | - run:
36 | name: lint
37 | command: npm run lint
38 |
39 | test_linux:
40 | working_directory: ~/project
41 | docker:
42 | - image: circleci/node:12
43 | steps:
44 | - checkout
45 | - attach_workspace:
46 | at: ./
47 | - run:
48 | name: test
49 | command: npm run test
50 | - run:
51 | name: test_e2e
52 | command: npm run test:e2e
53 | - run:
54 | name: coverage
55 | command: npx codecov
56 |
57 | test_windows:
58 | executor: win/default
59 | working_directory: ~/project
60 | steps:
61 | - checkout
62 | - run:
63 | name: install
64 | command: npm install --ignore-engines
65 | - run:
66 | name: test
67 | command: npm run test
68 | - run:
69 | name: test_e2e
70 | command: npm run test:e2e
71 |
72 | test-e2e-linux:
73 | working_directory: ~/project
74 | docker:
75 | - image: circleci/node:12
76 | - image: verdaccio/verdaccio:4
77 | steps:
78 | - checkout
79 | - attach_workspace:
80 | at: ./
81 | - run:
82 | name: npm-verbosity
83 | command: npm config set loglevel verbose
84 | - run:
85 | name: npm-set-registry
86 | command: npm set registry http://0.0.0.0:4873
87 | - run:
88 | name: create-registry-user
89 | command: |
90 | curl -X PUT http://0.0.0.0:4873/-/user/org.couchdb.user:liran -H "Content-Type: application/json" -d '{"name":"liran","password":"liran","email":"liran@example.com","type":"user"}' | node -pe 'JSON.parse(fs.readFileSync(0)).token' | xargs npm config set _authToken
91 | - run:
92 | name: npm-publish
93 | command: npm publish --verbose
94 | - run:
95 | name: npx-pie-my-vulns
96 | command: |
97 | cd __tests__/__e2e__/project1
98 | npm install --ignore-engines
99 | npx snyk config set "api=${SNYK_TEST_TOKEN}"
100 | set +e
101 | npx pie-my-vulns
102 | [[ $? = 2 ]] && exit 0 || exit 1
103 |
104 | release:
105 | working_directory: ~/project
106 | docker:
107 | - image: circleci/node:12
108 | steps:
109 | - checkout
110 | - attach_workspace:
111 | at: ./
112 | - run:
113 | name: release
114 | command: npm run semantic-release
115 |
116 | workflows:
117 | version: 2.1
118 | project:
119 | jobs:
120 | - build
121 | - lint:
122 | requires:
123 | - build
124 | - test_linux:
125 | requires:
126 | - lint
127 | - test_windows:
128 | requires:
129 | - lint
130 | - test-e2e-linux:
131 | requires:
132 | - test_linux
133 | - test_windows
134 | - release:
135 | filters:
136 | branches:
137 | only:
138 | - master
139 | requires:
140 | - test-e2e-linux
141 |
--------------------------------------------------------------------------------
/__tests__/Audit.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable security/detect-child-process */
2 | const EventEmitter = require('events')
3 | const { execFile } = require('child_process')
4 | jest.mock('child_process')
5 |
6 | const Audit = require('../src/Audit')
7 |
8 | class ChildProcessStdoutMock extends EventEmitter {}
9 | class ChildProcessSuccessMock extends EventEmitter {
10 | constructor() {
11 | super()
12 | this.self = this
13 | }
14 |
15 | get stdout() {
16 | const stdout = new ChildProcessStdoutMock()
17 |
18 | setImmediate(() => {
19 | stdout.emit(
20 | 'data',
21 | `
22 | Now redirecting you to our auth page, go ahead and log in,
23 | and once the auth is complete, return to this prompt and you'll
24 | be ready to start using snyk.
25 |
26 | If you can't wait use this url:
27 | https://snyk.io/login?token=98723-28763-28g830-f2iu873b
28 | `
29 | )
30 | }, 0)
31 |
32 | setImmediate(() => {
33 | this.self.emit('close')
34 | }, 0)
35 |
36 | return stdout
37 | }
38 | }
39 |
40 | describe('Audit', () => {
41 | beforeEach(() => {
42 | jest.resetAllMocks()
43 | })
44 |
45 | test('authenticate() should invoke auditing with auth argument', async () => {
46 | jest.spyOn(global.console, 'log')
47 |
48 | execFile.mockImplementation(() => {
49 | const process = new ChildProcessSuccessMock()
50 | return process
51 | })
52 |
53 | const audit = new Audit()
54 | await audit.authenticate()
55 |
56 | expect(execFile).toHaveBeenCalled()
57 | expect(execFile).toHaveBeenCalledWith('node', [expect.anything(), 'auth'], {
58 | env: expect.any(Object)
59 | })
60 | expect(console.log.mock.calls).toEqual([
61 | [],
62 | ['or you can hit this link:'],
63 | [' https://snyk.io/login?token=98723-28763-28g830-f2iu873b']
64 | ])
65 | })
66 |
67 | test('when test() returns vulns found exit code they should be returned', async () => {
68 | execFile.mockImplementation(
69 | jest.fn((file, args, options, callback) => {
70 | const err = new Error()
71 | err.code = 1
72 | err.stdout = JSON.stringify({ vulnsCount: 15 })
73 | return callback(err)
74 | })
75 | )
76 |
77 | const audit = new Audit()
78 | const data = await audit.test()
79 |
80 | expect(data).toEqual({
81 | vulnsCount: 15
82 | })
83 |
84 | expect(execFile).toHaveBeenCalled()
85 | })
86 |
87 | test('when test() not being authenticated should run the authenticate flow', async () => {
88 | execFile.mockImplementationOnce(
89 | jest.fn((file, args, options, callback) => {
90 | const err = new Error()
91 | err.code = 2
92 | err.stdout = JSON.stringify({ vulnsCount: 15 })
93 | return callback(err)
94 | })
95 | )
96 |
97 | execFile.mockImplementationOnce(
98 | jest.fn((file, args, options, callback) => {
99 | const err = new Error()
100 | err.code = 1
101 | err.stdout = JSON.stringify({ vulnsCount: 15 })
102 | return callback(err)
103 | })
104 | )
105 |
106 | const audit = new Audit()
107 |
108 | const spy = jest.spyOn(audit, 'authenticate')
109 | audit.authenticate.mockResolvedValue(true)
110 |
111 | await audit.test()
112 |
113 | expect(audit.authenticate).toHaveBeenCalled()
114 | spy.mockRestore()
115 | })
116 |
117 | test('when test() throws an error about supported target files', async () => {
118 | expect.assertions(2)
119 | execFile.mockImplementation(
120 | jest.fn((file, args, options, callback) => {
121 | const err = new Error()
122 | err.code = 1
123 | err.stdout = 'Could not detect supported target files'
124 | return callback(err)
125 | })
126 | )
127 |
128 | const audit = new Audit()
129 | try {
130 | await audit.test()
131 | } catch (err) {
132 | expect(err.message).toContain(
133 | `can't detect package manifest files\ntry running in the project's rootdir`
134 | )
135 | }
136 |
137 | expect(execFile).toHaveBeenCalled()
138 | })
139 |
140 | test('when test() throws an error about deps not being available', async () => {
141 | expect.assertions(2)
142 | execFile.mockImplementation(
143 | jest.fn((file, args, options, callback) => {
144 | const err = new Error()
145 | err.code = 1
146 | err.stdout = "we can't test without dependencies"
147 | return callback(err)
148 | })
149 | )
150 |
151 | const audit = new Audit()
152 | try {
153 | await audit.test()
154 | } catch (err) {
155 | expect(err.message).toContain(
156 | `missing node_modules folders\ninstall dependencies and try again`
157 | )
158 | }
159 |
160 | expect(execFile).toHaveBeenCalled()
161 | })
162 | })
163 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pie-my-vulns",
3 | "version": "0.0.0-development",
4 | "description": "Visualize your project security vulnerabilities as a pie chart in the terminal",
5 | "bin": {
6 | "pie-my-vulns": "./bin/pie-my-vulns.js"
7 | },
8 | "engines": {
9 | "node": ">=8.0.0"
10 | },
11 | "scripts": {
12 | "lint": "eslint . && npm run lint:lockfile",
13 | "lint:lockfile": "lockfile-lint --path package-lock.json --type npm --validate-https --allowed-hosts npm yarn",
14 | "lint:fix": "eslint . --fix",
15 | "format": "prettier --config .prettierrc.js --write '**/*.js'",
16 | "test": "jest -c jest.default.js",
17 | "test:e2e": "jest -c jest.e2e.js",
18 | "test:watch": "jest --watch",
19 | "coverage:view": "open-cli coverage/lcov-report/index.html",
20 | "semantic-release": "semantic-release"
21 | },
22 | "author": {
23 | "name": "Liran Tal",
24 | "email": "liran.tal@gmail.com",
25 | "url": "https://github.com/lirantal"
26 | },
27 | "license": "Apache-2.0",
28 | "keywords": [
29 | "pie",
30 | "chart",
31 | "vulnerabilities",
32 | "security",
33 | "terminal",
34 | "console",
35 | "visualize"
36 | ],
37 | "homepage": "https://github.com/lirantal/pie-my-vulns",
38 | "bugs": {
39 | "url": "https://github.com/lirantal/pie-my-vulns/issues"
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "https://github.com/lirantal/pie-my-vulns.git"
44 | },
45 | "dependencies": {
46 | "cli-pie": "^2.4.2",
47 | "minimist": "1.2.5",
48 | "debug": "^4.3.1",
49 |
50 | "snyk": "^1.685.0",
51 | "ora": "5.4.1"
52 | },
53 | "devDependencies": {
54 | "@commitlint/cli": "^7.2.1",
55 | "@commitlint/config-conventional": "^7.1.2",
56 | "@semantic-release/changelog": "^3.0.4",
57 | "@semantic-release/commit-analyzer": "^6.2.0",
58 | "@semantic-release/git": "^7.0.16",
59 | "@semantic-release/github": "^5.4.2",
60 | "@semantic-release/npm": "^5.1.13",
61 | "@semantic-release/release-notes-generator": "^7.2.1",
62 | "babel-eslint": "^10.0.1",
63 | "babel-plugin-syntax-async-functions": "^6.13.0",
64 | "babel-plugin-transform-regenerator": "^6.26.0",
65 | "babel-preset-env": "^1.6.1",
66 | "cz-conventional-changelog": "^1.2.0",
67 | "eslint": "^6.0.1",
68 | "eslint-config-standard": "^13.0.1",
69 | "eslint-plugin-import": "^2.18.0",
70 | "eslint-plugin-jest": "^22.7.2",
71 | "eslint-plugin-node": "^9.1.0",
72 | "eslint-plugin-promise": "^4.2.1",
73 | "eslint-plugin-security": "^1.4.0",
74 | "eslint-plugin-standard": "^4.0.0",
75 | "husky": "^3.0.0",
76 | "jest": "^24.8.0",
77 | "lint-staged": "^9.2.0",
78 | "lockfile-lint": "^2.0.1",
79 | "open-cli": "^5.0.0",
80 | "prettier": "^1.18.2",
81 | "semantic-release": "^15.13.19"
82 | },
83 | "husky": {
84 | "hooks": {
85 | "commit-msg": "commitlint --env HUSKY_GIT_PARAMS",
86 | "pre-commit": "lint-staged",
87 | "post-merge": "npm install",
88 | "pre-push": "npm run lint && npm run test"
89 | }
90 | },
91 | "lint-staged": {
92 | "**/*.js": [
93 | "npm run format",
94 | "git add"
95 | ]
96 | },
97 | "commitlint": {
98 | "extends": [
99 | "@commitlint/config-conventional"
100 | ]
101 | },
102 | "standard": {
103 | "env": [
104 | "jest"
105 | ],
106 | "parser": "babel-eslint",
107 | "ignore": [
108 | "**/out/"
109 | ]
110 | },
111 | "eslintIgnore": [
112 | "coverage/**"
113 | ],
114 | "eslintConfig": {
115 | "env": {
116 | "node": true,
117 | "es6": true,
118 | "jest": true
119 | },
120 | "plugins": [
121 | "import",
122 | "standard",
123 | "node",
124 | "security",
125 | "jest"
126 | ],
127 | "extends": [
128 | "standard",
129 | "plugin:node/recommended"
130 | ],
131 | "rules": {
132 | "no-process-exit": "warn",
133 | "jest/no-disabled-tests": "error",
134 | "jest/no-focused-tests": "error",
135 | "jest/no-identical-title": "error",
136 | "node/no-unsupported-features": "off",
137 | "node/no-unpublished-require": "off",
138 | "security/detect-non-literal-fs-filename": "error",
139 | "security/detect-unsafe-regex": "error",
140 | "security/detect-buffer-noassert": "error",
141 | "security/detect-child-process": "error",
142 | "security/detect-disable-mustache-escape": "error",
143 | "security/detect-eval-with-expression": "error",
144 | "security/detect-no-csrf-before-method-override": "error",
145 | "security/detect-non-literal-regexp": "error",
146 | "security/detect-object-injection": "warn",
147 | "security/detect-possible-timing-attacks": "error",
148 | "security/detect-pseudoRandomBytes": "error",
149 | "space-before-function-paren": "off",
150 | "object-curly-spacing": "off"
151 | },
152 | "parserOptions": {
153 | "ecmaVersion": 8,
154 | "ecmaFeatures": {
155 | "impliedStrict": true
156 | }
157 | }
158 | },
159 | "release": {
160 | "branch": "master",
161 | "analyzeCommits": {
162 | "preset": "angular",
163 | "releaseRules": [
164 | {
165 | "type": "docs",
166 | "release": "patch"
167 | },
168 | {
169 | "type": "refactor",
170 | "release": "patch"
171 | },
172 | {
173 | "type": "style",
174 | "release": "patch"
175 | }
176 | ]
177 | }
178 | },
179 | "plugins": [
180 | "@semantic-release/commit-analyzer",
181 | "@semantic-release/release-notes-generator",
182 | [
183 | "@semantic-release/changelog",
184 | {
185 | "changelogFile": "CHANGELOG.md"
186 | }
187 | ],
188 | "@semantic-release/npm",
189 | [
190 | "@semantic-release/git",
191 | {
192 | "assets": [
193 | "CHANGELOG.md"
194 | ]
195 | }
196 | ],
197 | "@semantic-release/github"
198 | ]
199 | }
200 |
--------------------------------------------------------------------------------
/__tests__/__e2e__/project1/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project1",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "amdefine": {
8 | "version": "1.0.1",
9 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
10 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
11 | "dev": true
12 | },
13 | "async": {
14 | "version": "0.2.10",
15 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
16 | "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
17 | "dev": true
18 | },
19 | "camelcase": {
20 | "version": "1.2.1",
21 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
22 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
23 | "dev": true
24 | },
25 | "camelize": {
26 | "version": "1.0.0",
27 | "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
28 | "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
29 | },
30 | "connect": {
31 | "version": "3.4.1",
32 | "resolved": "https://registry.npmjs.org/connect/-/connect-3.4.1.tgz",
33 | "integrity": "sha1-ohNh0/QJnvdhzabcSpc7seuwo00=",
34 | "requires": {
35 | "debug": "~2.2.0",
36 | "finalhandler": "0.4.1",
37 | "parseurl": "~1.3.1",
38 | "utils-merge": "1.0.0"
39 | }
40 | },
41 | "content-security-policy-builder": {
42 | "version": "1.0.0",
43 | "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-1.0.0.tgz",
44 | "integrity": "sha1-Ef1AxcwpimxyWjX5rPcegqtdMkM=",
45 | "requires": {
46 | "dashify": "^0.2.0"
47 | }
48 | },
49 | "core-util-is": {
50 | "version": "1.0.2",
51 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
52 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
53 | },
54 | "dashify": {
55 | "version": "0.2.2",
56 | "resolved": "https://registry.npmjs.org/dashify/-/dashify-0.2.2.tgz",
57 | "integrity": "sha1-agdBWgHJH69KMuONnfunH2HLIP4="
58 | },
59 | "debug": {
60 | "version": "2.2.0",
61 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
62 | "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
63 | "requires": {
64 | "ms": "0.7.1"
65 | }
66 | },
67 | "decamelize": {
68 | "version": "1.2.0",
69 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
70 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
71 | "dev": true
72 | },
73 | "depd": {
74 | "version": "1.1.0",
75 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz",
76 | "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM="
77 | },
78 | "dns-prefetch-control": {
79 | "version": "0.1.0",
80 | "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz",
81 | "integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI="
82 | },
83 | "dont-sniff-mimetype": {
84 | "version": "1.0.0",
85 | "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz",
86 | "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g="
87 | },
88 | "ee-first": {
89 | "version": "1.1.1",
90 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
91 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
92 | },
93 | "escape-html": {
94 | "version": "1.0.3",
95 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
96 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
97 | },
98 | "finalhandler": {
99 | "version": "0.4.1",
100 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.1.tgz",
101 | "integrity": "sha1-haF8bFmpRxfSYtYSMNSw6+PUoU0=",
102 | "requires": {
103 | "debug": "~2.2.0",
104 | "escape-html": "~1.0.3",
105 | "on-finished": "~2.3.0",
106 | "unpipe": "~1.0.0"
107 | }
108 | },
109 | "frameguard": {
110 | "version": "2.0.0",
111 | "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-2.0.0.tgz",
112 | "integrity": "sha1-MMLBSeXjUF+eFW+bxJGkOEIOSH4="
113 | },
114 | "helmet": {
115 | "version": "2.3.0",
116 | "resolved": "https://registry.npmjs.org/helmet/-/helmet-2.3.0.tgz",
117 | "integrity": "sha1-1lXIW1Wwo79yKkwsZuSLeLQWG5E=",
118 | "requires": {
119 | "connect": "3.4.1",
120 | "dns-prefetch-control": "0.1.0",
121 | "dont-sniff-mimetype": "1.0.0",
122 | "frameguard": "2.0.0",
123 | "helmet-csp": "1.2.2",
124 | "hide-powered-by": "1.0.0",
125 | "hpkp": "1.2.0",
126 | "hsts": "1.0.0",
127 | "ienoopen": "1.0.0",
128 | "nocache": "1.0.1",
129 | "referrer-policy": "1.0.0",
130 | "x-xss-protection": "1.0.0"
131 | }
132 | },
133 | "helmet-csp": {
134 | "version": "1.2.2",
135 | "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-1.2.2.tgz",
136 | "integrity": "sha1-CFwDB9V/yWzZczfxcKuL/qmeXfc=",
137 | "requires": {
138 | "camelize": "1.0.0",
139 | "content-security-policy-builder": "1.0.0",
140 | "lodash.reduce": "4.5.0",
141 | "platform": "1.3.1"
142 | }
143 | },
144 | "hide-powered-by": {
145 | "version": "1.0.0",
146 | "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz",
147 | "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys="
148 | },
149 | "hpkp": {
150 | "version": "1.2.0",
151 | "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-1.2.0.tgz",
152 | "integrity": "sha1-g/LLOLJs/yHa8m4v9LVxJpId7GU="
153 | },
154 | "hsts": {
155 | "version": "1.0.0",
156 | "resolved": "https://registry.npmjs.org/hsts/-/hsts-1.0.0.tgz",
157 | "integrity": "sha1-mOEDnverpVQFe2sOMlhMCxFDpBQ=",
158 | "requires": {
159 | "core-util-is": "1.0.2"
160 | }
161 | },
162 | "ienoopen": {
163 | "version": "1.0.0",
164 | "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.0.0.tgz",
165 | "integrity": "sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms="
166 | },
167 | "lodash.reduce": {
168 | "version": "4.5.0",
169 | "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.5.0.tgz",
170 | "integrity": "sha1-r30uxiEGJEHnfVv0CKHgce+GaRw="
171 | },
172 | "ms": {
173 | "version": "0.7.1",
174 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
175 | "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
176 | },
177 | "nocache": {
178 | "version": "1.0.1",
179 | "resolved": "https://registry.npmjs.org/nocache/-/nocache-1.0.1.tgz",
180 | "integrity": "sha1-aVyfc2kmp1VPc2X6JeCHlBBlvTY=",
181 | "requires": {
182 | "depd": "1.1.0"
183 | }
184 | },
185 | "on-finished": {
186 | "version": "2.3.0",
187 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
188 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
189 | "requires": {
190 | "ee-first": "1.1.1"
191 | }
192 | },
193 | "parseurl": {
194 | "version": "1.3.3",
195 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
196 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
197 | },
198 | "platform": {
199 | "version": "1.3.1",
200 | "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.1.tgz",
201 | "integrity": "sha1-SSIQiSM1vTExwKCN2i2T7DVD5CM="
202 | },
203 | "referrer-policy": {
204 | "version": "1.0.0",
205 | "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.0.0.tgz",
206 | "integrity": "sha1-9g7tyS+UKwGmEYEh7JMtZuj9fhQ="
207 | },
208 | "source-map": {
209 | "version": "0.1.34",
210 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
211 | "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=",
212 | "dev": true,
213 | "requires": {
214 | "amdefine": ">=0.0.4"
215 | }
216 | },
217 | "timespan": {
218 | "version": "2.3.0",
219 | "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz",
220 | "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=",
221 | "dev": true
222 | },
223 | "uglify-js": {
224 | "version": "2.4.24",
225 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
226 | "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=",
227 | "dev": true,
228 | "requires": {
229 | "async": "~0.2.6",
230 | "source-map": "0.1.34",
231 | "uglify-to-browserify": "~1.0.0",
232 | "yargs": "~3.5.4"
233 | }
234 | },
235 | "uglify-to-browserify": {
236 | "version": "1.0.2",
237 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
238 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
239 | "dev": true
240 | },
241 | "unpipe": {
242 | "version": "1.0.0",
243 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
244 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
245 | },
246 | "utils-merge": {
247 | "version": "1.0.0",
248 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz",
249 | "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg="
250 | },
251 | "window-size": {
252 | "version": "0.1.0",
253 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
254 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
255 | "dev": true
256 | },
257 | "wordwrap": {
258 | "version": "0.0.2",
259 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
260 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
261 | "dev": true
262 | },
263 | "x-xss-protection": {
264 | "version": "1.0.0",
265 | "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz",
266 | "integrity": "sha1-iYr7k4abJGYc+cUvnujbjtB2Tdk="
267 | },
268 | "yargs": {
269 | "version": "3.5.4",
270 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
271 | "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=",
272 | "dev": true,
273 | "requires": {
274 | "camelcase": "^1.0.2",
275 | "decamelize": "^1.0.0",
276 | "window-size": "0.1.0",
277 | "wordwrap": "0.0.2"
278 | }
279 | }
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | Copyright 2019 Liran Tal .
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | https://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
--------------------------------------------------------------------------------
/.github/pie-my-vulns-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------