diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f9d30bc --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@event1.io. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b28a4e9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +## Contributing + +First fork this project. + +* git clone +* npm install + +* git checkout -b my-fix + +#### fix some code... + +* git commit -m "added this feature" +* git push origin my-fix + +Lastly, open a pull request on Github. diff --git a/README.md b/README.md index 8180eae..dda7a63 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,15 @@

Generate a HTML report for NPM Audit

-## Install +## 📝 Table of Contents + +- [Getting Started](#getting_started) +- [Usage](#usage) +- [Contributing](CONTRIBUTING.md) +- [Authors](#authors) +- [Acknowledgments](#acknowledgement) + +## 🏁 Getting Started ``` $ npm install -g npm-audit-html @@ -19,7 +27,7 @@ $ npm install -g npm-audit-html > This package uses async/await and requires Node.js 7.6 -## Usage +## 🎈 Usage To generate a report, run the following: @@ -35,9 +43,15 @@ If you want to specify the output file, add the `--output` option: npm audit --json | npm-audit-html --output report.html ``` -## Contributors +## ✍️ Authors + +- [@nprail](https://github.com/nprail) - Maintainer + +See also the list of [contributors](https://github.com/eventOneHQ/npm-audit-html/contributors) who participated in this project. + +## 🎉 Acknowledgements -[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/0)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/0)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/1)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/1)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/2)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/2)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/3)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/3)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/4)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/4)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/5)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/5)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/6)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/6)[![](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/images/7)](https://sourcerer.io/fame/nprail/Filiosoft/npm-audit-html/links/7) +- Hat tip to anyone whose code was used ## License diff --git a/index.js b/index.js index f8176b8..e8bbae3 100644 --- a/index.js +++ b/index.js @@ -2,49 +2,71 @@ const program = require('commander') const updateNotifier = require('update-notifier') +const fs = require('fs-extra') const reporter = require('./lib/reporter') const pkg = require('./package.json') updateNotifier({ pkg }).notify() +let stdin = '' + program .version(pkg.version) .option('-o, --output [output]', 'output file') + .option('-i, --input [input]', 'input file') + .option( + '-c, --theme [theme name]', + 'template theme `dark` or `light` (defaults to `light`)' + ) .option('-t, --template [handlebars file]', 'handlebars template file') + .action(async (cmd, env) => { + try { + let data + if (cmd.input) { + data = await fs.readJson(cmd.input) + } else if (stdin) { + data = JSON.parse(stdin) + } else { + console.log('No input') + return process.exit(1) + } -const genReport = (stdin, output = 'npm-audit.html', template) => { - if (!stdin) { - console.log('No JSON') - return process.exit(1) - } + await genReport(data, cmd.output, cmd.template, cmd.theme) + } catch (err) { + console.error('Failed to parse NPM Audit JSON!') + return process.exit(1) + } + }) - let json +const genReport = async ( + data, + output = 'npm-audit.html', + template, + theme = 'light' +) => { try { - json = JSON.parse(stdin) + if (!data) { + console.log('No JSON') + return process.exit(1) + } + + const templateFile = template || `${__dirname}/templates/template.hbs` + + await reporter(data, templateFile, output, theme) + + console.log(`Vulnerability snapshot saved at ${output}`) + process.exit(0) } catch (err) { - console.error('Failed to parse NPM Audit JSON!') - return process.exit(1) + console.log('An error occurred!') + console.log(err) + process.exit(1) } - - const templateFile = template || `${__dirname}/templates/template.hbs` - - reporter(json, templateFile, output) - .then(() => { - console.log(`Vulnerability snapshot saved at ${output}`) - process.exit(0) - }) - .catch(err => { - console.log('An error occurred!') - console.log(err) - process.exit(1) - }) } if (process.stdin.isTTY) { program.parse(process.argv) } else { - let stdin = '' process.stdin.on('readable', function () { const chunk = this.read() if (chunk !== null) { @@ -53,6 +75,5 @@ if (process.stdin.isTTY) { }) process.stdin.on('end', function () { program.parse(process.argv) - genReport(stdin, program.output, program.template) }) } diff --git a/lib/reporter.js b/lib/reporter.js index d16d559..593e4ba 100644 --- a/lib/reporter.js +++ b/lib/reporter.js @@ -4,12 +4,30 @@ const moment = require('moment') const marked = require('marked') const fs = require('fs-extra') const chalk = require('chalk') +const numeral = require('numeral') +const highlight = require('highlight.js') -const bootstrapClassSeverityMap = { - low: 'primary', - moderate: 'secondary', - high: 'warning', - critical: 'danger' +const severityMap = { + info: { + color: 'info', + number: 5 + }, + low: { + color: 'primary', + number: 4 + }, + moderate: { + color: 'secondary', + number: 3 + }, + high: { + color: 'warning', + number: 2 + }, + critical: { + color: 'danger', + number: 1 + } } const generateTemplate = async (data, template) => { @@ -50,7 +68,7 @@ const modifyData = async data => { return data } -module.exports = async (data, templateFile, outputFile) => { +module.exports = async (data, templateFile, outputFile, theme) => { try { if (!data.metadata) { if (data.updated) { @@ -77,6 +95,7 @@ module.exports = async (data, templateFile, outputFile) => { } const modifiedData = await modifyData(data) + modifiedData.theme = theme const report = await generateTemplate(modifiedData, templateFile) await writeReport(report, outputFile) } catch (err) { @@ -84,15 +103,37 @@ module.exports = async (data, templateFile, outputFile) => { } } -Handlebars.registerHelper('moment', (date, format) => { - return moment.utc(date).format(format) -}) +Handlebars.registerHelper('moment', (date, format) => + moment.utc(date).format(format) +) -Handlebars.registerHelper('severityClass', severity => { - const bootstrapClass = bootstrapClassSeverityMap[severity] - return bootstrapClass -}) +Handlebars.registerHelper('numeral', (number, format) => + numeral(number).format(format) +) -Handlebars.registerHelper('markdown', source => { - return marked(source) +Handlebars.registerHelper('if_eq', (a, b, opts) => { + if (a === b) { + return opts.fn(this) + } else { + return opts.inverse(this) + } }) + +Handlebars.registerHelper( + 'severityClass', + severity => severityMap[severity].color +) + +Handlebars.registerHelper( + 'severityNumber', + severity => severityMap[severity].number +) + +Handlebars.registerHelper('markdown', source => + marked(source, { + highlight: code => { + return highlight.highlightAuto(code).value + }, + gfm: true + }) +) diff --git a/package-lock.json b/package-lock.json index 2a74024..36b3f48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3999,6 +3999,11 @@ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" }, + "highlight.js": { + "version": "9.16.2", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.16.2.tgz", + "integrity": "sha512-feMUrVLZvjy0oC7FVJQcSQRqbBq9kwqnYE4+Kj9ZjbHh3g+BisiPgF49NyQbVLNdrL/qqZr3Ca9yOKwgn2i/tw==" + }, "home-or-tmp": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-3.0.0.tgz", @@ -9234,6 +9239,11 @@ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, + "numeral": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz", + "integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", diff --git a/package.json b/package.json index 8d769af..412d468 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,10 @@ "commander": "^3.0.0", "fs-extra": "^8.1.0", "handlebars": "^4.1.2", + "highlight.js": "^9.16.2", "marked": "^0.7.0", "moment": "^2.24.0", + "numeral": "^2.0.6", "terminal-link": "^1.3.0", "update-notifier": "^3.0.1" }, diff --git a/templates/template.hbs b/templates/template.hbs index bbd0ee6..2a3d180 100644 --- a/templates/template.hbs +++ b/templates/template.hbs @@ -7,92 +7,313 @@ - + + + NPM Audit Report + + {{#if_eq theme 'dark'}} + + {{/if_eq}} - +
-
-
-

NPM Audit Report

+

NPM Audit Report

-

{{metadata.vulnerabilities.total}} known vulnerabilities | {{metadata.totalDependencies}} dependencies | - {{moment date "MMMM Do YYYY, h:mm:ss a"}}

-

+
+
+
+
+
+ {{numeral metadata.vulnerabilities.total "0,0"}} +
+

Known vulnerabilities

+
+
+
+
+
+ {{numeral metadata.totalDependencies "0,0"}} +
+

Dependencies

+
+
+
+
+
+ {{moment date "MMMM Do YYYY, h:mm:ss a"}} +
+

Last updated

+
+
-
-
- {{#each advisories}} -
+
-

- {{severity}} - {{title}} -

-
-
-
    -
  • Module: - {{module_name}} -
  • -
  • Published: {{moment created "MMMM Do YYYY"}}
  • -
  • Reported by: {{reported_by.name}}
  • -
  • {{cwe}}
  • - {{#each cves}} -
  • {{this}}
  • - {{/each}} -
-
-
-
    -
  • Vulnerable: {{vulnerable_versions}}
  • -
  • Patched: {{patched_versions}}
  • -
  • CVSS: {{metadata.exploitability}}
  • -
-
-
-

Overview

-

{{{markdown overview}}}

- -

Findings

- {{#each findings}} -
    - {{#each paths}} -
  • {{this}}
  • - {{/each}} -
- {{/each}} - - {{#if recommendation}} -

Remediation

-

{{{markdown recommendation}}}

- {{/if}} {{#if references}} -

References

-

{{{markdown references}}}

- {{/if}} {{#if action}} -

Action

+
+ {{numeral metadata.vulnerabilities.critical "0,0"}} +
+

+ critical +

+
+
+
+
+
+ {{numeral metadata.vulnerabilities.high "0,0"}} +
+

+ high +

+
+
+
+
+
+ {{numeral metadata.vulnerabilities.moderate "0,0"}} +
+

+ moderate +

+
+
+
+
+
+ {{numeral metadata.vulnerabilities.low "0,0"}} +
+

+ low +

+
+
+
+
+
+ {{numeral metadata.vulnerabilities.info "0,0"}} +

- npm {{action.action}} {{action.module}} {{#if action.depth}}--depth={{action.depth}}{{/if}} + info

- {{/if}} +
+
+
+
+ +
+
+ + + + + + + + + + + + {{#each advisories}} + + + + + + + {{/each}} + +
NameModuleSeverityCVEs
+ {{title}} + + {{module_name}} + {{severity}} + {{cwe}} + {{#each cves}} + , {{this}} + {{/each}} +
+
+
+
+ - More about this vulnerability + {{#each advisories}} +
+ {{/each}} + + + + + + \ No newline at end of file