Repository: sonatype-nexus-community/nancy
Pull request #81 (2020-02-23 20:15:47)

Update Readme.md to have new Nancy Gitter channel
@DarthHater created a nancy specific Gitter channel the other day. Making updates to the ReadMe to make it easily findable. 

cc @bhamail / @DarthHater



Patch:
diff --git a/README.md b/README.md
index 9e973bc..fc55f17 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,10 @@
 <p align="center">
     <img src="https://github.com/sonatype-nexus-community/nancy/blob/master/docs/images/nancy.png" width="350"/>
 </p>
+
 <p align="center">
+    <a href="https://gitter.im/sonatype-nexus-community/nancy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge">
+    <img src="https://badges.gitter.im/sonatype-nexus-community/nancy.svg" alt="Gitter"></img></a>
     <a href="https://travis-ci.org/sonatype-nexus-community/nancy"><img src="https://travis-ci.org/sonatype-nexus-community/nancy.svg?branch=master" alt="Build Status"></img></a>
 </p>
 
@@ -22,17 +25,21 @@ Options:
   -clean-cache
     	Deletes local cache directory
   -exclude-vulnerability value
-        Comma separated list of CVEs to exclude
+    	Comma separated list of CVEs to exclude
   -exclude-vulnerability-file string
-        Path to a file containing newline separated CVEs to be excluded (default "./.nancy-ignore")
+    	Path to a file containing newline separated CVEs to be excluded (default "./.nancy-ignore")
+  -help
+    	provides help text on how to use nancy
   -no-color
-        indicate output should not be colorized
+    	indicate output should not be colorized
   -noColor
-        indicate output should not be colorized (deprecated: please use no-color)
+    	indicate output should not be colorized (deprecated: please use no-color)
+  -output string
+    	Styling for output format. ["json-pretty" "text" "csv" "json"] (default "text")
   -quiet
-        indicate output should contain only packages with vulnerabilities
+    	indicate output should contain only packages with vulnerabilities
   -version
-        prints current nancy version
+    	prints current nancy version
 ```
 
 `nancy` currently works for projects that use `dep` or `go mod` for dependencies.
@@ -75,6 +82,177 @@ CVN-123 # Mitigated the risk of this since we only use one method in this packag
 CVN-543
 ``` 
 
+#### Output
+
+We support multiple different output formats. Examples can be found below for each. [This intentionally vulnerable repo](https://github.com/sonatype-nexus-community/intentionally-vulnerable-golang-project) was used to generate the example output.
+Quiet option is supported in text and csv. json formatting will ignore the Quiet option and output the same values if it's passed or not.  
+
+*text (default)*
+```
+Nancy version: development
+!!!!! WARNING !!!!!
+Scanning cannot be completed on the following package(s) since they do not use semver.
+[1/1]pkg:golang/github.com/go-gitea/gitea@1.3.0.rc1
+
+------------------------------------------------------------
+[1/10]pkg:golang/github.com/bitly/oauth2_proxy@0.1  [Vulnerable]   1 known vulnerabilities affecting installed version
+
+[CVE-2017-1000070]  URL Redirection to Untrusted Site ("Open Redirect")
+The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819
+
+ID:9eb9a5bc-8310-4104-bf85-3a820d28ba79
+Details:https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79
+[2/10]pkg:golang/github.com/cockroachdb/cockroach@2.1.4   No known vulnerabilities against package/version
+------------------------------------------------------------
+[3/10]pkg:golang/github.com/ethereum/go-ethereum@1.8.15  [Vulnerable]   1 known vulnerabilities affecting installed version
+
+CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')
+The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended.
+
+...
+
+Audited dependencies:10,Vulnerable:6
+```
+
+*json*
+```json
+{"audited":[{"Coordinates":"pkg:golang/github.com/bitly/oauth2_proxy@0.1","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/bitly/oauth2_proxy@0.1","Vulnerabilities":[{"Id":"9eb9a5bc-8310-4104-bf85-3a820d28ba79","Title":"[CVE-2017-1000070]  URL Redirection to Untrusted Site (\"Open Redirect\")","Description":"The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"CVE-2017-1000070","Reference":"https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/cockroachdb/cockroach@2.1.4","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/cockroachdb/cockroach@2.1.4","Vulnerabilities":[],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/ethereum/go-ethereum@1.8.15","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/ethereum/go-ethereum@1.8.15","Vulnerabilities":[{"Id":"4efaed86-e62e-4c0c-b812-36c07e61ede4","Title":"CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')","Description":"The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/4efaed86-e62e-4c0c-b812-36c07e61ede4","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/elastic/beats@5.6.3","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/elastic/beats@5.6.3","Vulnerabilities":[{"Id":"8e4d562d-517b-4d00-a845-a7a3e2be41db","Title":"[CVE-2017-11480]  Improper Access Control","Description":"Packetbeat versions prior to 5.6.4 are affected by a denial of service flaw in the PostgreSQL protocol handler. If Packetbeat is listening for PostgreSQL traffic and a user is able to send arbitrary network traffic to the monitored port, the attacker could prevent Packetbeat from properly logging other PostgreSQL traffic.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","Cve":"CVE-2017-11480","Reference":"https://ossindex.sonatype.org/vuln/8e4d562d-517b-4d00-a845-a7a3e2be41db","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/etcd-io/etcd@3.3.0","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/etcd-io/etcd@3.3.0","Vulnerabilities":[{"Id":"5c876f5e-2814-4822-baf0-1092fc63ec25","Title":"[CVE-2018-1098]  Cross-Site Request Forgery (CSRF)","Description":"A cross-site request forgery flaw was found in etcd 3.3.1 and earlier. An attacker can set up a website that tries to send a POST request to the etcd server and modify a key. Adding a key is done with PUT so it is theoretically safe (can't PUT from an HTML form or such) but POST allows creating in-order keys that an attacker can send.","CvssScore":"8.8","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","Cve":"CVE-2018-1098","Reference":"https://ossindex.sonatype.org/vuln/5c876f5e-2814-4822-baf0-1092fc63ec25","Excluded":false},{"Id":"8a190129-526c-4ee0-b663-92f38139c165","Title":"[CVE-2018-1099]  Improper Input Validation","Description":"DNS rebinding vulnerability found in etcd 3.3.1 and earlier. An attacker can control his DNS records to direct to localhost, and trick the browser into sending requests to localhost (or any other address).","CvssScore":"5.5","CvssVector":"CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","Cve":"CVE-2018-1099","Reference":"https://ossindex.sonatype.org/vuln/8a190129-526c-4ee0-b663-92f38139c165","Excluded":false},{"Id":"69b9f08b-8eda-4125-8e84-b7d67a7c9ee5","Title":"[CVE-2018-16886]  Improper Authentication","Description":"etcd versions 3.2.x before 3.2.26 and 3.3.x before 3.3.11 are vulnerable to an improper authentication issue when role-based access control (RBAC) is used and client-cert-auth is enabled. If an etcd client server TLS certificate contains a Common Name (CN) which matches a valid RBAC username, a remote attacker may authenticate as that user with any valid (trusted) client certificate in a REST API request to the gRPC-gateway.","CvssScore":"8.1","CvssVector":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","Cve":"CVE-2018-16886","Reference":"https://ossindex.sonatype.org/vuln/69b9f08b-8eda-4125-8e84-b7d67a7c9ee5","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/github/hub@2.0.0","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/github/hub@2.0.0","Vulnerabilities":[],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/gogs/gogs@0.9.45","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/gogs/gogs@0.9.45","Vulnerabilities":[{"Id":"a4c682fa-9c9f-4e9e-b218-720d5125b17f","Title":"CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","Description":"The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.","CvssScore":"9.9","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/a4c682fa-9c9f-4e9e-b218-720d5125b17f","Excluded":false},{"Id":"304fa9e0-012e-4385-88b2-88c0c5ec3247","Title":"[CVE-2018-15192] An SSRF vulnerability in webhooks in Gitea through 1.5.0-rc2 and Gogs through 0....","Description":"An SSRF vulnerability in webhooks in Gitea through 1.5.0-rc2 and Gogs through 0.11.53 allows remote attackers to access intranet services.","CvssScore":"8.6","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","Cve":"CVE-2018-15192","Reference":"https://ossindex.sonatype.org/vuln/304fa9e0-012e-4385-88b2-88c0c5ec3247","Excluded":false},{"Id":"a8c20c84-1f6a-472a-ba1b-3eaedb2a2a14","Title":"[CVE-2018-20303]  Improper Limitation of a Pathname to a Restricted Directory (\"Path Traversal\")","Description":"In pkg/tool/path.go in Gogs before 0.11.82.1218, a directory traversal in the file-upload functionality can allow an attacker to create a file under data/sessions on the server, a similar issue to CVE-2018-18925.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","Cve":"CVE-2018-20303","Reference":"https://ossindex.sonatype.org/vuln/a8c20c84-1f6a-472a-ba1b-3eaedb2a2a14","Excluded":false},{"Id":"bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2","Title":"[CVE-2018-18925] Gogs 0.11.66 allows remote code execution because it does not properly validate ...","Description":"Gogs 0.11.66 allows remote code execution because it does not properly validate session IDs, as demonstrated by a \"..\" session-file forgery in the file session provider in file.go. This is related to session ID handling in the go-macaron/session code for Macaron.","CvssScore":"9.8","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","Cve":"CVE-2018-18925","Reference":"https://ossindex.sonatype.org/vuln/bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2","Excluded":false},{"Id":"bbbdbb94-f65a-475c-9e9f-6793778fbd9b","Title":"[CVE-2018-15178]  URL Redirection to Untrusted Site (\"Open Redirect\")","Description":"Open redirect vulnerability in Gogs before 0.12 allows remote attackers to redirect users to arbitrary websites and conduct phishing attacks via an initial /\\ substring in the user/login redirect_to parameter, related to the function isValidRedirect in routes/user/auth.go.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"CVE-2018-15178","Reference":"https://ossindex.sonatype.org/vuln/bbbdbb94-f65a-475c-9e9f-6793778fbd9b","Excluded":false},{"Id":"fc70a115-52cc-44ea-a33d-793267f860dd","Title":"CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","Description":"The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/fc70a115-52cc-44ea-a33d-793267f860dd","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/goharbor/harbor@1.7.2","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/goharbor/harbor@1.7.2","Vulnerabilities":[],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/gophish/gophish@0.1.1","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/gophish/gophish@0.1.1","Vulnerabilities":[{"Id":"0416e202-2705-431d-9915-8ed93334ca58","Title":"CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","Description":"The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/0416e202-2705-431d-9915-8ed93334ca58","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/ipfs/go-ipfs@0.4.18","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/ipfs/go-ipfs@0.4.18","Vulnerabilities":[],"InvalidSemVer":false}],"exclusions":[],"invalid":[{"Coordinates":"pkg:golang/github.com/go-gitea/gitea@1.3.0.rc1","Reference":"","Vulnerabilities":null,"InvalidSemVer":true}],"num_audited":10,"num_vulnerable":6,"version":"development","vulnerable":[{"Coordinates":"pkg:golang/github.com/bitly/oauth2_proxy@0.1","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/bitly/oauth2_proxy@0.1","Vulnerabilities":[{"Id":"9eb9a5bc-8310-4104-bf85-3a820d28ba79","Title":"[CVE-2017-1000070]  URL Redirection to Untrusted Site (\"Open Redirect\")","Description":"The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"CVE-2017-1000070","Reference":"https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/ethereum/go-ethereum@1.8.15","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/ethereum/go-ethereum@1.8.15","Vulnerabilities":[{"Id":"4efaed86-e62e-4c0c-b812-36c07e61ede4","Title":"CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')","Description":"The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/4efaed86-e62e-4c0c-b812-36c07e61ede4","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/elastic/beats@5.6.3","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/elastic/beats@5.6.3","Vulnerabilities":[{"Id":"8e4d562d-517b-4d00-a845-a7a3e2be41db","Title":"[CVE-2017-11480]  Improper Access Control","Description":"Packetbeat versions prior to 5.6.4 are affected by a denial of service flaw in the PostgreSQL protocol handler. If Packetbeat is listening for PostgreSQL traffic and a user is able to send arbitrary network traffic to the monitored port, the attacker could prevent Packetbeat from properly logging other PostgreSQL traffic.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","Cve":"CVE-2017-11480","Reference":"https://ossindex.sonatype.org/vuln/8e4d562d-517b-4d00-a845-a7a3e2be41db","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/etcd-io/etcd@3.3.0","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/etcd-io/etcd@3.3.0","Vulnerabilities":[{"Id":"5c876f5e-2814-4822-baf0-1092fc63ec25","Title":"[CVE-2018-1098]  Cross-Site Request Forgery (CSRF)","Description":"A cross-site request forgery flaw was found in etcd 3.3.1 and earlier. An attacker can set up a website that tries to send a POST request to the etcd server and modify a key. Adding a key is done with PUT so it is theoretically safe (can't PUT from an HTML form or such) but POST allows creating in-order keys that an attacker can send.","CvssScore":"8.8","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","Cve":"CVE-2018-1098","Reference":"https://ossindex.sonatype.org/vuln/5c876f5e-2814-4822-baf0-1092fc63ec25","Excluded":false},{"Id":"8a190129-526c-4ee0-b663-92f38139c165","Title":"[CVE-2018-1099]  Improper Input Validation","Description":"DNS rebinding vulnerability found in etcd 3.3.1 and earlier. An attacker can control his DNS records to direct to localhost, and trick the browser into sending requests to localhost (or any other address).","CvssScore":"5.5","CvssVector":"CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N","Cve":"CVE-2018-1099","Reference":"https://ossindex.sonatype.org/vuln/8a190129-526c-4ee0-b663-92f38139c165","Excluded":false},{"Id":"69b9f08b-8eda-4125-8e84-b7d67a7c9ee5","Title":"[CVE-2018-16886]  Improper Authentication","Description":"etcd versions 3.2.x before 3.2.26 and 3.3.x before 3.3.11 are vulnerable to an improper authentication issue when role-based access control (RBAC) is used and client-cert-auth is enabled. If an etcd client server TLS certificate contains a Common Name (CN) which matches a valid RBAC username, a remote attacker may authenticate as that user with any valid (trusted) client certificate in a REST API request to the gRPC-gateway.","CvssScore":"8.1","CvssVector":"CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H","Cve":"CVE-2018-16886","Reference":"https://ossindex.sonatype.org/vuln/69b9f08b-8eda-4125-8e84-b7d67a7c9ee5","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/gogs/gogs@0.9.45","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/gogs/gogs@0.9.45","Vulnerabilities":[{"Id":"a4c682fa-9c9f-4e9e-b218-720d5125b17f","Title":"CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')","Description":"The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.","CvssScore":"9.9","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/a4c682fa-9c9f-4e9e-b218-720d5125b17f","Excluded":false},{"Id":"304fa9e0-012e-4385-88b2-88c0c5ec3247","Title":"[CVE-2018-15192] An SSRF vulnerability in webhooks in Gitea through 1.5.0-rc2 and Gogs through 0....","Description":"An SSRF vulnerability in webhooks in Gitea through 1.5.0-rc2 and Gogs through 0.11.53 allows remote attackers to access intranet services.","CvssScore":"8.6","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","Cve":"CVE-2018-15192","Reference":"https://ossindex.sonatype.org/vuln/304fa9e0-012e-4385-88b2-88c0c5ec3247","Excluded":false},{"Id":"a8c20c84-1f6a-472a-ba1b-3eaedb2a2a14","Title":"[CVE-2018-20303]  Improper Limitation of a Pathname to a Restricted Directory (\"Path Traversal\")","Description":"In pkg/tool/path.go in Gogs before 0.11.82.1218, a directory traversal in the file-upload functionality can allow an attacker to create a file under data/sessions on the server, a similar issue to CVE-2018-18925.","CvssScore":"7.5","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","Cve":"CVE-2018-20303","Reference":"https://ossindex.sonatype.org/vuln/a8c20c84-1f6a-472a-ba1b-3eaedb2a2a14","Excluded":false},{"Id":"bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2","Title":"[CVE-2018-18925] Gogs 0.11.66 allows remote code execution because it does not properly validate ...","Description":"Gogs 0.11.66 allows remote code execution because it does not properly validate session IDs, as demonstrated by a \"..\" session-file forgery in the file session provider in file.go. This is related to session ID handling in the go-macaron/session code for Macaron.","CvssScore":"9.8","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","Cve":"CVE-2018-18925","Reference":"https://ossindex.sonatype.org/vuln/bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2","Excluded":false},{"Id":"bbbdbb94-f65a-475c-9e9f-6793778fbd9b","Title":"[CVE-2018-15178]  URL Redirection to Untrusted Site (\"Open Redirect\")","Description":"Open redirect vulnerability in Gogs before 0.12 allows remote attackers to redirect users to arbitrary websites and conduct phishing attacks via an initial /\\ substring in the user/login redirect_to parameter, related to the function isValidRedirect in routes/user/auth.go.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"CVE-2018-15178","Reference":"https://ossindex.sonatype.org/vuln/bbbdbb94-f65a-475c-9e9f-6793778fbd9b","Excluded":false},{"Id":"fc70a115-52cc-44ea-a33d-793267f860dd","Title":"CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","Description":"The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/fc70a115-52cc-44ea-a33d-793267f860dd","Excluded":false}],"InvalidSemVer":false},{"Coordinates":"pkg:golang/github.com/gophish/gophish@0.1.1","Reference":"https://ossindex.sonatype.org/component/pkg:golang/github.com/gophish/gophish@0.1.1","Vulnerabilities":[{"Id":"0416e202-2705-431d-9915-8ed93334ca58","Title":"CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')","Description":"The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.","CvssScore":"6.1","CvssVector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","Cve":"","Reference":"https://ossindex.sonatype.org/vuln/0416e202-2705-431d-9915-8ed93334ca58","Excluded":false}],"InvalidSemVer":false}]}
+```
+
+*json-pretty*
+```json
+{
+  "audited": [
+    {
+      "Coordinates": "pkg:golang/github.com/bitly/oauth2_proxy@0.1",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/bitly/oauth2_proxy@0.1",
+      "Vulnerabilities": [
+        {
+          "Id": "9eb9a5bc-8310-4104-bf85-3a820d28ba79",
+          "Title": "[CVE-2017-1000070]  URL Redirection to Untrusted Site (\"Open Redirect\")",
+          "Description": "The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819",
+          "CvssScore": "6.1",
+          "CvssVector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
+          "Cve": "CVE-2017-1000070",
+          "Reference": "https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79",
+          "Excluded": false
+        }
+      ],
+      "InvalidSemVer": false
+    },
+    {
+      "Coordinates": "pkg:golang/github.com/cockroachdb/cockroach@2.1.4",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/cockroachdb/cockroach@2.1.4",
+      "Vulnerabilities": [],
+      "InvalidSemVer": false
+    },
+    {
+      "Coordinates": "pkg:golang/github.com/ethereum/go-ethereum@1.8.15",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/ethereum/go-ethereum@1.8.15",
+      "Vulnerabilities": [
+        {
+          "Id": "4efaed86-e62e-4c0c-b812-36c07e61ede4",
+          "Title": "CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')",
+          "Description": "The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended.",
+          "CvssScore": "7.5",
+          "CvssVector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+          "Cve": "",
+          "Reference": "https://ossindex.sonatype.org/vuln/4efaed86-e62e-4c0c-b812-36c07e61ede4",
+          "Excluded": false
+        }
+      ],
+      "InvalidSemVer": false
+    },
+    ...
+  ],
+  "exclusions": [],
+  "invalid": [
+    {
+      "Coordinates": "pkg:golang/github.com/go-gitea/gitea@1.3.0.rc1",
+      "Reference": "",
+      "Vulnerabilities": null,
+      "InvalidSemVer": true
+    }
+  ],
+  "num_audited": 10,
+  "num_vulnerable": 6,
+  "version": "development",
+  "vulnerable": [
+    {
+      "Coordinates": "pkg:golang/github.com/bitly/oauth2_proxy@0.1",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/bitly/oauth2_proxy@0.1",
+      "Vulnerabilities": [
+        {
+          "Id": "9eb9a5bc-8310-4104-bf85-3a820d28ba79",
+          "Title": "[CVE-2017-1000070]  URL Redirection to Untrusted Site (\"Open Redirect\")",
+          "Description": "The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819",
+          "CvssScore": "6.1",
+          "CvssVector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
+          "Cve": "CVE-2017-1000070",
+          "Reference": "https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79",
+          "Excluded": false
+        }
+      ],
+      "InvalidSemVer": false
+    },
+    {
+      "Coordinates": "pkg:golang/github.com/ethereum/go-ethereum@1.8.15",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/ethereum/go-ethereum@1.8.15",
+      "Vulnerabilities": [
+        {
+          "Id": "4efaed86-e62e-4c0c-b812-36c07e61ede4",
+          "Title": "CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')",
+          "Description": "The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended.",
+          "CvssScore": "7.5",
+          "CvssVector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+          "Cve": "",
+          "Reference": "https://ossindex.sonatype.org/vuln/4efaed86-e62e-4c0c-b812-36c07e61ede4",
+          "Excluded": false
+        }
+      ],
+      "InvalidSemVer": false
+    },
+    {
+      "Coordinates": "pkg:golang/github.com/elastic/beats@5.6.3",
+      "Reference": "https://ossindex.sonatype.org/component/pkg:golang/github.com/elastic/beats@5.6.3",
+      "Vulnerabilities": [
+        {
+          "Id": "8e4d562d-517b-4d00-a845-a7a3e2be41db",
+          "Title": "[CVE-2017-11480]  Improper Access Control",
+          "Description": "Packetbeat versions prior to 5.6.4 are affected by a denial of service flaw in the PostgreSQL protocol handler. If Packetbeat is listening for PostgreSQL traffic and a user is able to send arbitrary network traffic to the monitored port, the attacker could prevent Packetbeat from properly logging other PostgreSQL traffic.",
+          "CvssScore": "7.5",
+          "CvssVector": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
+          "Cve": "CVE-2017-11480",
+          "Reference": "https://ossindex.sonatype.org/vuln/8e4d562d-517b-4d00-a845-a7a3e2be41db",
+          "Excluded": false
+        }
+      ],
+      "InvalidSemVer": false
+    },
+    ...
+  ]
+}
+```
+
+*csv*
+```csv
+Summary
+Audited Count,Vulnerable Count,Build Version
+10,6,development
+
+Invalid Package(s)
+Count,Package,Reason
+[1/1],pkg:golang/github.com/go-gitea/gitea@1.3.0.rc1,Does not use SemVer
+
+Audited Package(s)
+Count,Package,Is Vulnerable,Num Vulnerabilities,Vulnerabilities
+[1/10],pkg:golang/github.com/bitly/oauth2_proxy@0.1,true,1,"[{""Id"":""9eb9a5bc-8310-4104-bf85-3a820d28ba79"",""Title"":""[CVE-2017-1000070]  URL Redirection to Untrusted Site (\""Open Redirect\"")"",""Description"":""The Bitly oauth2_proxy in version 2.1 and earlier was affected by an open redirect vulnerability during the start and termination of the 2-legged OAuth flow. This issue was caused by improper input validation and a violation of RFC-6819"",""CvssScore"":""6.1"",""CvssVector"":""CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"",""Cve"":""CVE-2017-1000070"",""Reference"":""https://ossindex.sonatype.org/vuln/9eb9a5bc-8310-4104-bf85-3a820d28ba79"",""Excluded"":false}]"
+[2/10],pkg:golang/github.com/cockroachdb/cockroach@2.1.4,false,0,[]
+[3/10],pkg:golang/github.com/ethereum/go-ethereum@1.8.15,true,1,"[{""Id"":""4efaed86-e62e-4c0c-b812-36c07e61ede4"",""Title"":""CWE-400: Uncontrolled Resource Consumption ('Resource Exhaustion')"",""Description"":""The software does not properly restrict the size or amount of resources that are requested or influenced by an actor, which can be used to consume more resources than intended."",""CvssScore"":""7.5"",""CvssVector"":""CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"",""Cve"":"""",""Reference"":""https://ossindex.sonatype.org/vuln/4efaed86-e62e-4c0c-b812-36c07e61ede4"",""Excluded"":false}]"
+...
+```
+
+
 ### Usage in CI
 
 You can see an example of using `nancy` in Travis-CI at [this intentionally vulnerable repo we made](https://github.com/sonatype-nexus-community/intentionally-vulnerable-golang-project).
@@ -152,4 +330,4 @@ Have fun creating and using `nancy` and the [Sonatype OSS Index](https://ossinde
 
 Looking to contribute to our code but need some help? There's a few ways to get information:
 
-* Chat with us on [Gitter](https://gitter.im/sonatype/nexus-developers)
+* Chat with us on [Gitter](https://gitter.im/sonatype-nexus-community/nancy)
diff --git a/audit/auditlog.go b/audit/auditlog.go
index cf22a03..75e664f 100644
--- a/audit/auditlog.go
+++ b/audit/auditlog.go
@@ -14,83 +14,53 @@
 package audit
 
 import (
-	"fmt"
-	"log"
-	"strconv"
-
-	"github.com/logrusorgru/aurora"
+	log "github.com/sirupsen/logrus"
+	"github.com/sonatype-nexus-community/nancy/buildversion"
 	"github.com/sonatype-nexus-community/nancy/types"
+	"os"
 )
 
-func LogInvalidSemVerWarning(noColor bool, quiet bool, invalidPurls []string) {
-	if !quiet {
-		packageCount := len(invalidPurls)
-		warningMessage := "!!!!! WARNING !!!!!\nScanning cannot be completed on the following package(s) since they do not use semver."
-		au := aurora.NewAurora(!noColor)
-		fmt.Println(au.Red(warningMessage))
-
-		for i := 0; i < len(invalidPurls); i++ {
-			idx := i + 1
-			purl := invalidPurls[i]
-			fmt.Println("["+strconv.Itoa(idx)+"/"+strconv.Itoa(packageCount)+"]", au.Bold(purl))
-		}
-		fmt.Println()
-	}
-}
-
-func logPackage(noColor bool, idx int, packageCount int, coordinate types.Coordinate) {
-	au := aurora.NewAurora(!noColor)
-	log.Println("["+strconv.Itoa(idx)+"/"+strconv.Itoa(packageCount)+"]",
-		au.Bold(coordinate.Coordinates),
-		au.Gray(20-1,"   No known vulnerabilities against package/version"))
-}
-
-func logVulnerablePackage(noColor bool, idx int, packageCount int, coordinate types.Coordinate) {
-	au := aurora.NewAurora(!noColor)
-	fmt.Println("------------------------------------------------------------")
-	fmt.Println("["+strconv.Itoa(idx)+"/"+strconv.Itoa(packageCount)+"]",
-		au.Bold(au.Red(coordinate.Coordinates+"  [Vulnerable]")),
-		"   "+strconv.Itoa(len(coordinate.Vulnerabilities)),
-		"known vulnerabilities affecting installed version")
-
-	for j := 0; j < len(coordinate.Vulnerabilities); j++ {
-		if !coordinate.Vulnerabilities[j].Excluded {
-			fmt.Printf("\n%s\n%s\n\nID:%s\nDetails:%s\n",
-				coordinate.Vulnerabilities[j].Title,
-				coordinate.Vulnerabilities[j].Description,
-				coordinate.Vulnerabilities[j].Id,
-				coordinate.Vulnerabilities[j].Reference)
-		}
-	}
-
-	return
-}
-
 // LogResults will given a number of expected results and the results themselves, log the
 // results.
-func LogResults(noColor bool, packageCount int, coordinates []types.Coordinate, exclusions []string) int {
+func LogResults(formatter log.Formatter, packageCount int, coordinates []types.Coordinate, invalidCoordinates []types.Coordinate, exclusions []string) int {
 	vulnerableCount := 0
 
 	for _, c := range coordinates {
 		c.ExcludeVulnerabilities(exclusions)
 	}
 
+	var auditedCoordinates []types.Coordinate
+	var vulnerableCoordinates []types.Coordinate
+
 	for i := 0; i < len(coordinates); i++ {
 		coordinate := coordinates[i]
-		idx := i + 1
-
-		if !coordinate.IsVulnerable() {
-			logPackage(noColor, idx, packageCount, coordinate)
-		} else {
-			logVulnerablePackage(noColor, idx, packageCount, coordinate)
+		if coordinate.IsVulnerable() {
 			vulnerableCount++
+			vulnerableCoordinates = append(vulnerableCoordinates, coordinate)
 		}
+		auditedCoordinates = append(auditedCoordinates, coordinate)
 	}
 
-	fmt.Println()
-	au := aurora.NewAurora(!noColor)
-	fmt.Println("Audited dependencies:", strconv.Itoa(packageCount)+",",
-		"Vulnerable:", au.Bold(au.Red(strconv.Itoa(vulnerableCount))))
+	if invalidCoordinates == nil {
+		invalidCoordinates = make([]types.Coordinate, 0)
+	}
+	if exclusions == nil {
+		exclusions = make([]string, 0)
+	}
+	if vulnerableCoordinates == nil {
+		vulnerableCoordinates = make([]types.Coordinate, 0)
+	}
+	log.SetFormatter(formatter)
+	log.SetOutput(os.Stdout)
+	log.WithFields(log.Fields{
+		"exclusions":     exclusions,
+		"num_audited":    packageCount,
+		"num_vulnerable": vulnerableCount,
+		"audited":        auditedCoordinates,
+		"vulnerable":     vulnerableCoordinates,
+		"invalid":        invalidCoordinates,
+		"version":        buildversion.BuildVersion,
+	}).Info("")
 
 	return vulnerableCount
 }
diff --git a/audit/auditlogtextformatter.go b/audit/auditlogtextformatter.go
new file mode 100644
index 0000000..cdc0166
--- /dev/null
+++ b/audit/auditlogtextformatter.go
@@ -0,0 +1,108 @@
+package audit
+
+import (
+	"errors"
+	"fmt"
+	"github.com/logrusorgru/aurora"
+	. "github.com/sirupsen/logrus"
+	"github.com/sonatype-nexus-community/nancy/types"
+	"strconv"
+	"strings"
+)
+
+type AuditLogTextFormatter struct {
+	Quiet   *bool
+	NoColor *bool
+}
+
+func logPackage(sb *strings.Builder, noColor bool, quiet bool, idx int, packageCount int, coordinate types.Coordinate) {
+	if !quiet {
+		au := aurora.NewAurora(!noColor)
+		packageLog := "[" + strconv.Itoa(idx) + "/" + strconv.Itoa(packageCount) + "]" +
+			au.Bold(coordinate.Coordinates).String() +
+			au.Gray(20-1, "   No known vulnerabilities against package/version\n").String()
+		sb.WriteString(packageLog)
+	}
+}
+
+func logInvalidSemVerWarning(sb *strings.Builder, noColor bool, quiet bool, invalidPurls []types.Coordinate) {
+	if !quiet {
+		packageCount := len(invalidPurls)
+		if packageCount > 0 {
+			warningMessage := "!!!!! WARNING !!!!!\nScanning cannot be completed on the following package(s) since they do not use semver.\n"
+			au := aurora.NewAurora(!noColor)
+			sb.WriteString(au.Red(warningMessage).String())
+
+			for i := 0; i < len(invalidPurls); i++ {
+				idx := i + 1
+				purl := invalidPurls[i].Coordinates
+				sb.WriteString("[" + strconv.Itoa(idx) + "/" + strconv.Itoa(packageCount) + "]" + au.Bold(purl).String() + "\n")
+			}
+			sb.WriteString("\n")
+		}
+	}
+}
+
+func logVulnerablePackage(sb *strings.Builder, noColor bool, idx int, packageCount int, coordinate types.Coordinate) {
+	au := aurora.NewAurora(!noColor)
+	sb.WriteString("------------------------------------------------------------\n")
+
+	vulnLog := "[" + strconv.Itoa(idx) + "/" + strconv.Itoa(packageCount) + "]" +
+		au.Bold(au.Red(coordinate.Coordinates+"  [Vulnerable]")).String() +
+		"   " + strconv.Itoa(len(coordinate.Vulnerabilities)) +
+		" known vulnerabilities affecting installed version\n"
+	sb.WriteString(vulnLog)
+
+	for j := 0; j < len(coordinate.Vulnerabilities); j++ {
+		if !coordinate.Vulnerabilities[j].Excluded {
+			sb.WriteString(fmt.Sprintf("\n%s\n%s\n\nID:%s\nDetails:%s\n",
+				coordinate.Vulnerabilities[j].Title,
+				coordinate.Vulnerabilities[j].Description,
+				coordinate.Vulnerabilities[j].Id,
+				coordinate.Vulnerabilities[j].Reference))
+		}
+	}
+
+	return
+}
+
+func (f *AuditLogTextFormatter) Format(entry *Entry) ([]byte, error) {
+	auditedEntries := entry.Data["audited"]
+	invalidEntries := entry.Data["invalid"]
+	packageCount := entry.Data["num_audited"]
+	numVulnerable := entry.Data["num_vulnerable"]
+	buildVersion := entry.Data["version"]
+	if auditedEntries != nil && invalidEntries != nil && packageCount != nil && numVulnerable != nil && buildVersion != nil {
+		auditedEntries := entry.Data["audited"].([]types.Coordinate)
+		invalidEntries := entry.Data["invalid"].([]types.Coordinate)
+		packageCount := entry.Data["num_audited"].(int)
+		numVulnerable := entry.Data["num_vulnerable"].(int)
+		buildVersion := entry.Data["version"].(string)
+
+		var sb strings.Builder
+
+		if !*f.Quiet {
+			sb.WriteString("Nancy version: " + buildVersion + "\n")
+		}
+
+		logInvalidSemVerWarning(&sb, *f.NoColor, *f.Quiet, invalidEntries)
+		for idx := 0; idx < len(auditedEntries); idx++ {
+			coordinate := auditedEntries[idx]
+			if !coordinate.IsVulnerable() && !coordinate.InvalidSemVer {
+				logPackage(&sb, *f.NoColor, *f.Quiet, idx+1, packageCount, coordinate)
+			}
+			if coordinate.IsVulnerable() {
+				logVulnerablePackage(&sb, *f.NoColor, idx+1, packageCount, coordinate)
+			}
+		}
+
+		sb.WriteString("\n")
+		au := aurora.NewAurora(!*f.NoColor)
+		sb.WriteString("Audited dependencies:" + strconv.Itoa(packageCount) + "," +
+			"Vulnerable:" + au.Bold(au.Red(strconv.Itoa(numVulnerable))).String() + "\n")
+
+		return []byte(sb.String()), nil
+	} else {
+		return nil, errors.New("fields passed did not match the expected values for an audit log. You should probably look at setting the formatter to something else")
+	}
+}
diff --git a/audit/csvformatter.go b/audit/csvformatter.go
new file mode 100644
index 0000000..efc1405
--- /dev/null
+++ b/audit/csvformatter.go
@@ -0,0 +1,79 @@
+package audit
+
+import (
+	"bytes"
+	"encoding/csv"
+	"encoding/json"
+	"errors"
+	. "github.com/sirupsen/logrus"
+	"github.com/sonatype-nexus-community/nancy/types"
+	"strconv"
+)
+
+type CsvFormatter struct {
+	Quiet   *bool
+}
+
+func (f *CsvFormatter) Format(entry *Entry) ([]byte, error) {
+	// Note this doesn't include Time, Level and Message which are available on
+	// the Entry. Consult `godoc` on information about those fields or read the
+	// source of the official loggers.
+	auditedEntries := entry.Data["audited"]
+	invalidEntries := entry.Data["invalid"]
+	packageCount := entry.Data["num_audited"]
+	numVulnerable := entry.Data["num_vulnerable"]
+	buildVersion := entry.Data["version"]
+
+	if auditedEntries != nil && invalidEntries != nil && packageCount != nil && numVulnerable != nil && buildVersion != nil {
+		auditedEntries := entry.Data["audited"].([]types.Coordinate)
+		invalidEntries := entry.Data["invalid"].([]types.Coordinate)
+		packageCount := entry.Data["num_audited"].(int)
+		numVulnerable := entry.Data["num_vulnerable"].(int)
+		buildVersion := entry.Data["version"].(string)
+
+		var summaryHeader = []string{"Audited Count", "Vulnerable Count", "Build Version"}
+		var invalidHeader = []string{"Count", "Package", "Reason"}
+		var auditedHeader = []string{"Count", "Package", "Is Vulnerable", "Num Vulnerabilities", "Vulnerabilities"}
+		var summaryRow = []string{strconv.Itoa(packageCount), strconv.Itoa(numVulnerable), buildVersion}
+
+		var buf bytes.Buffer
+		w := csv.NewWriter(&buf)
+
+		w.Write([]string{"Summary"})
+		w.Write(summaryHeader)
+		w.Write(summaryRow)
+
+		if (!*f.Quiet) {
+			invalidCount := len(invalidEntries)
+			if invalidCount > 0 {
+				w.Write([]string{""})
+				w.Write([]string{"Invalid Package(s)"})
+				w.Write(invalidHeader)
+				for i := 1; i <= invalidCount; i++ {
+					invalidEntry := invalidEntries[i-1]
+					w.Write([]string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(invalidCount) + "]", invalidEntry.Coordinates, "Does not use SemVer"})
+				}
+			}
+		}
+
+		if !*f.Quiet || numVulnerable > 0 {
+			w.Write([]string{""})
+			w.Write([]string{"Audited Package(s)"})
+			w.Write(auditedHeader)
+		}
+		for i := 1; i <= len(auditedEntries); i++ {
+			auditEntry := auditedEntries[i-1]
+			if auditEntry.IsVulnerable() || !*f.Quiet {
+				jsonVulns, _ := json.Marshal(auditEntry.Vulnerabilities)
+				w.Write([]string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(packageCount) + "]", auditEntry.Coordinates, strconv.FormatBool(auditEntry.IsVulnerable()), strconv.Itoa(len(auditEntry.Vulnerabilities)), string(jsonVulns)})
+			}
+		}
+
+		w.Flush()
+
+		return buf.Bytes(), nil
+	} else {
+		return nil, errors.New("fields passed did not match the expected values for an audit log. You should probably look at setting the formatter to something else")
+	}
+
+}
diff --git a/audit/jsonformatter.go b/audit/jsonformatter.go
new file mode 100644
index 0000000..0434852
--- /dev/null
+++ b/audit/jsonformatter.go
@@ -0,0 +1,22 @@
+package audit
+
+import (
+	"encoding/json"
+
+	. "github.com/sirupsen/logrus"
+)
+
+type JsonFormatter struct {
+	PrettyPrint bool
+}
+
+func (f *JsonFormatter) Format(entry *Entry) ([]byte, error) {
+	// Note this doesn't include Time, Level and Message which are available on
+	// the Entry. Consult `godoc` on information about those fields or read the
+	// source of the official loggers.
+	if f.PrettyPrint {
+		return json.MarshalIndent(entry.Data, "", "  ")
+	} else {
+		return json.Marshal(entry.Data)
+	}
+}
diff --git a/configuration/parse.go b/configuration/parse.go
index e6f9bc5..e03f5dc 100644
--- a/configuration/parse.go
+++ b/configuration/parse.go
@@ -6,20 +6,24 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"reflect"
 	"strings"
 
+	"github.com/sirupsen/logrus"
+	"github.com/sonatype-nexus-community/nancy/audit"
 	"github.com/sonatype-nexus-community/nancy/types"
 )
 
 type Configuration struct {
-	UseStdIn   bool
-	Help       bool
-	NoColor    bool
-	Quiet      bool
-	Version    bool
+	UseStdIn bool
+	Help bool
+	NoColor bool
+	Quiet bool
+	Version bool
 	CleanCache bool
-	CveList    types.CveListFlag
-	Path       string
+	CveList types.CveListFlag
+	Path    string
+	Formatter logrus.Formatter
 }
 
 var unixComments = regexp.MustCompile(`#.*$`)
@@ -27,8 +31,16 @@ var unixComments = regexp.MustCompile(`#.*$`)
 func Parse(args []string) (Configuration, error) {
 	config := Configuration{}
 	var excludeVulnerabilityFilePath string
+	var outputFormat string
 	var noColorDeprecated bool
 
+	var outputFormats = map[string]logrus.Formatter{
+		"json":        &audit.JsonFormatter{},
+		"json-pretty": &audit.JsonFormatter{PrettyPrint: true},
+		"text":        &audit.AuditLogTextFormatter{Quiet: &config.Quiet, NoColor: &config.NoColor},
+		"csv":         &audit.CsvFormatter{Quiet: &config.Quiet},
+	}
+
 	flag.BoolVar(&config.Help, "help", false, "provides help text on how to use nancy")
 	flag.BoolVar(&config.NoColor, "no-color", false, "indicate output should not be colorized")
 	flag.BoolVar(&noColorDeprecated, "noColor", false, "indicate output should not be colorized (deprecated: please use no-color)")
@@ -37,9 +49,10 @@ func Parse(args []string) (Configuration, error) {
 	flag.BoolVar(&config.CleanCache, "clean-cache", false, "Deletes local cache directory")
 	flag.Var(&config.CveList, "exclude-vulnerability", "Comma separated list of CVEs to exclude")
 	flag.StringVar(&excludeVulnerabilityFilePath, "exclude-vulnerability-file", "./.nancy-ignore", "Path to a file containing newline separated CVEs to be excluded")
+	flag.StringVar(&outputFormat, "output", "text", "Styling for output format. "+fmt.Sprintf("%+q", reflect.ValueOf(outputFormats).MapKeys()))
 
 	flag.Usage = func() {
-		_, _ = fmt.Fprintf(os.Stderr, "Usage: \nnancy [options] </path/to/Gopkg.lock>\nnancy [options] </path/to/go.sum>\n\nOptions:\n")
+		_, _ = fmt.Fprintf(os.Stderr, "Usage: \ngo list -m all | nancy [options]\nnancy [options] </path/to/Gopkg.lock>\nnancy [options] </path/to/go.sum>\n\nOptions:\n")
 		flag.PrintDefaults()
 		os.Exit(2)
 	}
@@ -63,6 +76,15 @@ func Parse(args []string) (Configuration, error) {
 		config.NoColor = noColorDeprecated
 	}
 
+	if outputFormats[outputFormat] != nil {
+		config.Formatter = outputFormats[outputFormat]
+	} else {
+		fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+		fmt.Println("!!! Output format of", strings.TrimSpace(outputFormat), "is not valid. Defaulting to text output")
+		fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
+		config.Formatter = outputFormats["text"]
+	}
+
 	err = getCVEExcludesFromFile(&config, excludeVulnerabilityFilePath)
 	if err != nil {
 		return config, err
diff --git a/go.mod b/go.mod
index 71ceac1..bd91ee7 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,6 @@ module github.com/sonatype-nexus-community/nancy
 
 require (
 	github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7 // indirect
-	github.com/BurntSushi/toml v0.3.1
 	github.com/Flaque/filet v0.0.0-20190209224823-fc4d33cfcf93
 	github.com/Masterminds/semver v0.0.0-20180403130225-3c92f33da7a8
 	github.com/Masterminds/vcs v1.13.1 // indirect
@@ -13,6 +12,7 @@ require (
 	github.com/dustin/go-humanize v1.0.0 // indirect
 	github.com/golang/dep v0.5.4
 	github.com/golang/protobuf v1.2.0 // indirect
+	github.com/google/go-cmp v0.4.0 // indirect
 	github.com/jmank88/nuts v0.3.0 // indirect
 	github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b
 	github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 // indirect
@@ -20,11 +20,11 @@ require (
 	github.com/pkg/errors v0.8.0 // indirect
 	github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 // indirect
 	github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
+	github.com/sirupsen/logrus v1.4.2
 	github.com/spf13/afero v1.2.2 // indirect
 	github.com/stretchr/testify v1.3.0
 	golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
 	golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
-	golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb // indirect
 )
 
 go 1.13
diff --git a/go.sum b/go.sum
index 7105780..05ebcab 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,6 @@ github.com/Flaque/filet v0.0.0-20190209224823-fc4d33cfcf93 h1:NnAUCP75PRm8yWE7+M
 github.com/Flaque/filet v0.0.0-20190209224823-fc4d33cfcf93/go.mod h1:TK+jB3mBs+8ZMWhU5BqZKnZWJ1MrLo8etNVg51ueTBo=
 github.com/Masterminds/semver v0.0.0-20180403130225-3c92f33da7a8 h1:/FWnwUv6QGO8R+4hWsLbb/nNKEyqrVuZVwQd6/VGObI=
 github.com/Masterminds/semver v0.0.0-20180403130225-3c92f33da7a8/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
-github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
 github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
 github.com/Masterminds/vcs v1.13.1/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
 github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
@@ -28,8 +26,12 @@ github.com/golang/dep v0.5.4 h1:WfV5qbGwsBNUDhk+pfI6emWm7SdDFsnSWkqCMNG3BRs=
 github.com/golang/dep v0.5.4/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA=
 github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/jmank88/nuts v0.3.0 h1:UZUboV1LXVkBUTHLRTEZrDfAL7QYgj9jEsBCiJHrxEM=
 github.com/jmank88/nuts v0.3.0/go.mod h1:kTf5cyoLibZUQg9Lns/gteKO1d/5XrhacD1QVKviAKk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b h1:PMbSa9CgaiQR9NLlUTwKi+7aeLl3GG5JX5ERJxfQ3IE=
 github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
 github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 h1:+2OJrU8cmOstEoh0uQvYemRGVH1O6xtO2oANUWHFnP0=
@@ -44,18 +46,26 @@ github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62
 github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
 golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb h1:pf3XwC90UUdNPYWZdFjhGBE7DUFuK3Ct1zWmZ65QN30=
-golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/main.go b/main.go
index cc53639..381b7cd 100644
--- a/main.go
+++ b/main.go
@@ -18,6 +18,7 @@ import (
 	"errors"
 	"flag"
 	"fmt"
+	"github.com/sonatype-nexus-community/nancy/types"
 	"io/ioutil"
 	"log"
 	"os"
@@ -68,8 +69,6 @@ func main() {
 		log.SetOutput(ioutil.Discard)
 	}
 
-	log.Println("Nancy version: " + buildversion.BuildVersion)
-
 	if config.UseStdIn == true {
 		doStdInAndParse()
 	} else {
@@ -90,8 +89,7 @@ func doStdInAndParse() {
 		scanner := bufio.NewScanner(os.Stdin)
 		mod.ProjectList, _ = parse.GoList(scanner)
 		var purls = mod.ExtractPurlsFromManifest()
-		var packageCount = len(purls)
-		checkOSSIndex(purls, packageCount)
+		checkOSSIndex(purls, []string{})
 	}
 }
 
@@ -116,32 +114,33 @@ func doCheckExistenceAndParse() {
 		}
 
 		purls, invalidPurls := packages.ExtractPurlsUsingDep(*project)
-		if len(invalidPurls) > 0 {
-			audit.LogInvalidSemVerWarning(config.NoColor, config.Quiet, invalidPurls)
-		}
 
-		var packageCount = len(purls)
-		checkOSSIndex(purls, packageCount)
+		checkOSSIndex(purls, invalidPurls)
 	case strings.Contains(config.Path, "go.sum"):
 		mod := packages.Mod{}
 		mod.GoSumPath = config.Path
 		if mod.CheckExistenceOfManifest() {
 			mod.ProjectList, _ = parse.GoSum(config.Path)
 			var purls = mod.ExtractPurlsFromManifest()
-			var packageCount = len(purls)
 
-			checkOSSIndex(purls, packageCount)
+			checkOSSIndex(purls, nil)
 		}
 	default:
 		os.Exit(3)
 	}
 }
 
-func checkOSSIndex(purls []string, packageCount int) {
+func checkOSSIndex(purls []string, invalidpurls []string) {
+	var packageCount = len(purls)
 	coordinates, err := ossindex.AuditPackages(purls)
 	customerrors.Check(err, "Error auditing packages")
 
-	if count := audit.LogResults(config.NoColor, packageCount, coordinates, config.CveList.Cves); count > 0 {
+	var invalidCoordinates []types.Coordinate
+	for _, invalidpurl := range invalidpurls {
+		invalidCoordinates = append(invalidCoordinates, types.Coordinate{Coordinates: invalidpurl, InvalidSemVer: true})
+	}
+
+	if count := audit.LogResults(config.Formatter, packageCount, coordinates, invalidCoordinates, config.CveList.Cves); count > 0 {
 		os.Exit(count)
 	}
 }
diff --git a/packages/dep.go b/packages/dep.go
index 7119a3b..9b12539 100644
--- a/packages/dep.go
+++ b/packages/dep.go
@@ -20,7 +20,7 @@ import (
 )
 
 func ExtractPurlsUsingDep(project dep.Project) ([]string, []string) {
-	lockedProjects := project.Lock.P;
+	lockedProjects := project.Lock.P
 	var purls []string
 	var invalidPurls []string
 	for _, lockedProject := range lockedProjects {
@@ -37,7 +37,7 @@ func ExtractPurlsUsingDep(project dep.Project) ([]string, []string) {
 			_, err := semver.NewVersion(version)
 			if err != nil {
 				invalidPurls = append(invalidPurls, purl)
-			}else{
+			} else {
 				purls = append(purls, purl)
 			}
 		}
diff --git a/types/types.go b/types/types.go
index 0f5b809..3c84391 100644
--- a/types/types.go
+++ b/types/types.go
@@ -44,6 +44,7 @@ type Coordinate struct {
 	Coordinates     string
 	Reference       string
 	Vulnerabilities []Vulnerability
+	InvalidSemVer   bool
 }
 
 func (c Coordinate) IsVulnerable() bool {
@@ -57,7 +58,7 @@ func (c Coordinate) IsVulnerable() bool {
 
 //Mark Excluded=true for all Vulnerabilities of the given Coordinate if their Title is in the list of exclusions
 func (c *Coordinate) ExcludeVulnerabilities(exclusions []string) {
-	for i, _ := range c.Vulnerabilities {
+	for i := range c.Vulnerabilities {
 		c.Vulnerabilities[i].maybeExcludeVulnerability(exclusions)
 	}
 }


Test patch:
diff --git a/audit/auditlog_test.go b/audit/auditlog_test.go
index b80ca24..ee9506f 100644
--- a/audit/auditlog_test.go
+++ b/audit/auditlog_test.go
@@ -66,7 +66,9 @@ func createVulnerability() (vulnerability types.Vulnerability) {
 func TestLogResultsWithVulnerabilitiesNoColor(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, true)
-	i := LogResults(false, 20, coordinates, []string{})
+	noColor := true
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{})
 
 	if i != projects {
 		t.Errorf("Expected %d vulnerabilites but found %d", projects, i)
@@ -76,7 +78,9 @@ func TestLogResultsWithVulnerabilitiesNoColor(t *testing.T) {
 func TestLogResultsWithoutVulnerabilitiesNoColor(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, false)
-	i := LogResults(false, 20, coordinates, []string{})
+	noColor := true
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{})
 
 	if i != 0 {
 		t.Errorf("Expected %d vulnerabilites but found %d", 0, i)
@@ -86,7 +90,9 @@ func TestLogResultsWithoutVulnerabilitiesNoColor(t *testing.T) {
 func TestLogResultsWithVulnerabilitiesColor(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, true)
-	i := LogResults(true, 20, coordinates, []string{})
+	noColor := false
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{})
 
 	if i != projects {
 		t.Errorf("Expected %d vulnerabilites but found %d", projects, i)
@@ -96,7 +102,9 @@ func TestLogResultsWithVulnerabilitiesColor(t *testing.T) {
 func TestLogResultsWithoutVulnerabilitiesColor(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, false)
-	i := LogResults(true, 20, coordinates, []string{})
+	noColor := false
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{})
 
 	if i != 0 {
 		t.Errorf("Expected %d vulnerabilites but found %d", 0, i)
@@ -106,13 +114,17 @@ func TestLogResultsWithoutVulnerabilitiesColor(t *testing.T) {
 func TestLogResultsWithAllVulnerabilitiesExcluded(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, true)
-	i := LogResults(false, 20, coordinates, []string{"CVE-123"})
+	noColor := false
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{"CVE-123"})
 	assert.Equal(t, 0, i)
 }
 
 func TestLogResultsWithNoVulnerabilitiesExcluded(t *testing.T) {
 	projects := 20
 	coordinates := createCoordinates(projects, true)
-	i := LogResults(false, 20, coordinates, []string{"CVE-456"})
+	noColor := false
+	quiet := false
+	i := LogResults(&AuditLogTextFormatter{NoColor: &noColor, Quiet: &quiet}, 20, coordinates, []types.Coordinate{}, []string{"CVE-456"})
 	assert.Equal(t, projects, i)
 }
diff --git a/audit/auditlogtextformatter_test.go b/audit/auditlogtextformatter_test.go
new file mode 100644
index 0000000..0e78e0d
--- /dev/null
+++ b/audit/auditlogtextformatter_test.go
@@ -0,0 +1,23 @@
+package audit
+
+import (
+	"errors"
+	. "github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestFormatterErrorsIfEntryNotValid(t *testing.T) {
+	data := map[string]interface{}{
+		"stuff":   1,
+		"another": "me",
+	}
+	entry := Entry{Data: data}
+
+	formatter := AuditLogTextFormatter{}
+	logMessage, e := formatter.Format(&entry)
+
+	assert.Nil(t, logMessage)
+	assert.NotNil(t, e)
+	assert.Equal(t, errors.New("fields passed did not match the expected values for an audit log. You should probably look at setting the formatter to something else"), e)
+}
diff --git a/audit/csvformatter_test.go b/audit/csvformatter_test.go
new file mode 100644
index 0000000..315b001
--- /dev/null
+++ b/audit/csvformatter_test.go
@@ -0,0 +1,89 @@
+package audit
+
+import (
+	"errors"
+	. "github.com/sirupsen/logrus"
+	"github.com/sonatype-nexus-community/nancy/types"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestCsvOutputWhenQuiet(t *testing.T) {
+	data := map[string]interface{}{
+		"audited": []types.Coordinate{
+			{Coordinates: "good1"},
+			{Coordinates: "vuln1", Vulnerabilities: createVulnerabilities(1)},
+		},
+		"invalid": []types.Coordinate{
+			{InvalidSemVer: true, Coordinates: "invalid1"},
+		},
+		"num_audited":    2,
+		"num_vulnerable": 1,
+		"version":        "development",
+	}
+	entry := Entry{Data: data}
+
+	quiet := true
+	formatter := CsvFormatter{Quiet: &quiet}
+	logMessage, e := formatter.Format(&entry)
+	assert.Nil(t, e)
+	expectedCsv := `Summary
+Audited Count,Vulnerable Count,Build Version
+2,1,development
+
+Audited Package(s)
+Count,Package,Is Vulnerable,Num Vulnerabilities,Vulnerabilities
+[2/2],vuln1,true,1,"[{""Id"":""123"",""Title"":""Vulnerability"",""Description"":""Description"",""CvssScore"":""7.88"",""CvssVector"":""What"",""Cve"":""CVE-123"",""Reference"":""Reference"",""Excluded"":false}]"
+`
+	assert.Equal(t, expectedCsv, string(logMessage))
+}
+
+func TestCsvOutput(t *testing.T) {
+	data := map[string]interface{}{
+		"audited": []types.Coordinate{
+			{Coordinates: "good1"},
+			{Coordinates: "vuln1", Vulnerabilities: createVulnerabilities(1)},
+		},
+		"invalid": []types.Coordinate{
+			{InvalidSemVer: true, Coordinates: "invalid1"},
+		},
+		"num_audited":    2,
+		"num_vulnerable": 1,
+		"version":        "development",
+	}
+	entry := Entry{Data: data}
+
+	quiet := false
+	formatter := CsvFormatter{Quiet: &quiet}
+	logMessage, e := formatter.Format(&entry)
+	assert.Nil(t, e)
+expectedCsv := `Summary
+Audited Count,Vulnerable Count,Build Version
+2,1,development
+
+Invalid Package(s)
+Count,Package,Reason
+[1/1],invalid1,Does not use SemVer
+
+Audited Package(s)
+Count,Package,Is Vulnerable,Num Vulnerabilities,Vulnerabilities
+[1/2],good1,false,0,null
+[2/2],vuln1,true,1,"[{""Id"":""123"",""Title"":""Vulnerability"",""Description"":""Description"",""CvssScore"":""7.88"",""CvssVector"":""What"",""Cve"":""CVE-123"",""Reference"":""Reference"",""Excluded"":false}]"
+`
+	assert.Equal(t, expectedCsv, string(logMessage))
+}
+
+func TestCsvOutputWhenNotAuditLog(t *testing.T) {
+	data := map[string]interface{}{
+		"stuff":   1,
+		"another": "me",
+	}
+	entry := Entry{Data: data}
+
+	formatter := CsvFormatter{}
+	logMessage, e := formatter.Format(&entry)
+
+	assert.Nil(t, logMessage)
+	assert.NotNil(t, e)
+	assert.Equal(t, errors.New("fields passed did not match the expected values for an audit log. You should probably look at setting the formatter to something else"), e)
+}
diff --git a/audit/jsonformatter_test.go b/audit/jsonformatter_test.go
new file mode 100644
index 0000000..d1842cf
--- /dev/null
+++ b/audit/jsonformatter_test.go
@@ -0,0 +1,38 @@
+package audit
+
+import (
+	. "github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestJsonOutpu(t *testing.T) {
+	data := map[string]interface{}{
+		"stuff":   1,
+		"another": "me",
+	}
+	entry := Entry{Data: data}
+
+	formatter := JsonFormatter{}
+	logMessage, e := formatter.Format(&entry)
+
+	assert.Nil(t, e)
+	assert.Equal(t, `{"another":"me","stuff":1}`, string(logMessage))
+}
+
+func TestJsonPrettyPrintOutpu(t *testing.T) {
+	data := map[string]interface{}{
+		"stuff":   1,
+		"another": "me",
+	}
+	entry := Entry{Data: data}
+
+	formatter := JsonFormatter{PrettyPrint: true}
+	logMessage, e := formatter.Format(&entry)
+
+	assert.Nil(t, e)
+	assert.Equal(t, `{
+  "another": "me",
+  "stuff": 1
+}`, string(logMessage))
+}
diff --git a/configuration/parse_test.go b/configuration/parse_test.go
index 1a6260f..34a63bc 100644
--- a/configuration/parse_test.go
+++ b/configuration/parse_test.go
@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"errors"
 	"flag"
+	"github.com/sonatype-nexus-community/nancy/audit"
 	"github.com/sonatype-nexus-community/nancy/types"
 	"github.com/stretchr/testify/assert"
 	"io/ioutil"
@@ -45,23 +46,28 @@ CVN-543`)
 		expectedConfig Configuration
 		expectedErr    error
 	}{
-		"defaults":                               {args: []string{"/tmp/go.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{}, Path: "/tmp/go.sum"}, expectedErr: nil},
-		"help":                                   {args: []string{"-help", "/tmp/go2.sum"}, expectedConfig: Configuration{Help: true, NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
-		"no color":                               {args: []string{"-no-color", "/tmp/go2.sum"}, expectedConfig: Configuration{NoColor: true, Quiet: false, Version: false, CveList: types.CveListFlag{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
-		"no color (deprecated)":                  {args: []string{"-noColor", "/tmp/go2.sum"}, expectedConfig: Configuration{NoColor: true, Quiet: false, Version: false, CveList: types.CveListFlag{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
-		"quiet":                                  {args: []string{"-quiet", "/tmp/go3.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: true, Version: false, CveList: types.CveListFlag{}, Path: "/tmp/go3.sum"}, expectedErr: nil},
-		"version":                                {args: []string{"-version", "/tmp/go4.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: true, CveList: types.CveListFlag{}, Path: "/tmp/go4.sum"}, expectedErr: nil},
-		"exclude vulnerabilities":                {args: []string{"-exclude-vulnerability=CVE123,CVE988", "/tmp/go5.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{Cves: []string{"CVE123", "CVE988"}}, Path: "/tmp/go5.sum"}, expectedErr: nil},
-		"std in as input":                        {args: []string{}, expectedConfig: Configuration{UseStdIn: true}, expectedErr: nil},
+		"defaults":                               {args: []string{"/tmp/go.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go.sum"}, expectedErr: nil},
+		"help":                                   {args: []string{"-help", "/tmp/go2.sum"}, expectedConfig: Configuration{Help: true, NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
+		"no color":                               {args: []string{"-no-color", "/tmp/go2.sum"}, expectedConfig: Configuration{NoColor: true, Quiet: false, Version: false, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
+		"no color (deprecated)":                  {args: []string{"-noColor", "/tmp/go2.sum"}, expectedConfig: Configuration{NoColor: true, Quiet: false, Version: false, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go2.sum"}, expectedErr: nil},
+		"quiet":                                  {args: []string{"-quiet", "/tmp/go3.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: true, Version: false, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go3.sum"}, expectedErr: nil},
+		"version":                                {args: []string{"-version", "/tmp/go4.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: true, CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go4.sum"}, expectedErr: nil},
+		"exclude vulnerabilities":                {args: []string{"-exclude-vulnerability=CVE123,CVE988", "/tmp/go5.sum"}, expectedConfig: Configuration{NoColor: false, Quiet: false, Version: false, CveList: types.CveListFlag{Cves: []string{"CVE123", "CVE988"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go5.sum"}, expectedErr: nil},
+		"std in as input":                        {args: []string{}, expectedConfig: Configuration{UseStdIn: true, Formatter: &audit.AuditLogTextFormatter{}}, expectedErr: nil},
 		"path but invalid arg":                   {args: []string{"-invalid", "/tmp/go6.sum"}, expectedConfig: Configuration{}, expectedErr: errors.New("flag provided but not defined: -invalid")},
-		"exclude vulnerabilities with sane file": {args: []string{"-exclude-vulnerability-file=" + file.Name(), "/tmp/go7.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVF-000", "CVF-123", "CVF-9999"}}, Path: "/tmp/go7.sum"}, expectedErr: nil},
-		"exclude vulnerabilities when file empty":                                    {args: []string{"-exclude-vulnerability-file=" + emptyFile.Name(), "/tmp/go8.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Path: "/tmp/go8.sum"}, expectedErr: nil},
-		"exclude vulnerabilities when file has tons of newlines":                     {args: []string{"-exclude-vulnerability-file=" + lotsOfRandomNewlinesFile.Name(), "/tmp/go9.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVN-111", "CVN-123", "CVN-543"}}, Path: "/tmp/go9.sum"}, expectedErr: nil},
-		"exclude vulnerabilities are combined with file and args values":             {args: []string{"-exclude-vulnerability=CVE123,CVE988", "-exclude-vulnerability-file=" + lotsOfRandomNewlinesFile.Name(), "/tmp/go10.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVE123", "CVE988", "CVN-111", "CVN-123", "CVN-543"}}, Path: "/tmp/go10.sum"}, expectedErr: nil},
-		"exclude vulnerabilities file not found doesn't matter":                      {args: []string{"-exclude-vulnerability-file=/blah-blah-doesnt-exists", "/tmp/go11.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Path: "/tmp/go11.sum"}, expectedErr: nil},
-		"exclude vulnerabilities passed as directory doesn't matter":                 {args: []string{"-exclude-vulnerability-file=" + dir, "/tmp/go12.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Path: "/tmp/go12.sum"}, expectedErr: nil},
-		"exclude vulnerabilities doesn't need to be passed if default value is used": {args: []string{"/tmp/go13.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"DEF-111", "DEF-222"}}, Path: "/tmp/go13.sum"}, expectedErr: nil},
-		"exclude vulnerabilities when has comments":                                  {args: []string{"-exclude-vulnerability-file=" + commentedFile.Name(), "/tmp/go14.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVN-111", "CVN-123", "CVN-543"}}, Path: "/tmp/go14.sum"}, expectedErr: nil},
+		"exclude vulnerabilities when has comments":                                  {args: []string{"-exclude-vulnerability-file=" + commentedFile.Name(), "/tmp/go14.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVN-111", "CVN-123", "CVN-543"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go14.sum"}, expectedErr: nil},
+		"exclude vulnerabilities with sane file": {args: []string{"-exclude-vulnerability-file=" + file.Name(), "/tmp/go7.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVF-000", "CVF-123", "CVF-9999"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go7.sum"}, expectedErr: nil},
+		"exclude vulnerabilities when file empty":                                    {args: []string{"-exclude-vulnerability-file=" + emptyFile.Name(), "/tmp/go8.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go8.sum"}, expectedErr: nil},
+		"exclude vulnerabilities when file has tons of newlines":                     {args: []string{"-exclude-vulnerability-file=" + lotsOfRandomNewlinesFile.Name(), "/tmp/go9.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVN-111", "CVN-123", "CVN-543"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go9.sum"}, expectedErr: nil},
+		"exclude vulnerabilities are combined with file and args values":             {args: []string{"-exclude-vulnerability=CVE123,CVE988", "-exclude-vulnerability-file=" + lotsOfRandomNewlinesFile.Name(), "/tmp/go10.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"CVE123", "CVE988", "CVN-111", "CVN-123", "CVN-543"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go10.sum"}, expectedErr: nil},
+		"exclude vulnerabilities file not found doesn't matter":                      {args: []string{"-exclude-vulnerability-file=/blah-blah-doesnt-exists", "/tmp/go11.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go11.sum"}, expectedErr: nil},
+		"exclude vulnerabilities passed as directory doesn't matter":                 {args: []string{"-exclude-vulnerability-file=" + dir, "/tmp/go12.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go12.sum"}, expectedErr: nil},
+		"exclude vulnerabilities doesn't need to be passed if default value is used": {args: []string{"/tmp/go13.sum"}, expectedConfig: Configuration{CveList: types.CveListFlag{Cves: []string{"DEF-111", "DEF-222"}}, Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go13.sum"}, expectedErr: nil},
+		"output of json":              {args: []string{"-output=json", "/tmp/go14.sum"}, expectedConfig: Configuration{Formatter: &audit.JsonFormatter{}, Path: "/tmp/go14.sum"}, expectedErr: nil},
+		"output of json pretty print": {args: []string{"-output=json-pretty", "/tmp/go15.sum"}, expectedConfig: Configuration{Formatter: &audit.JsonFormatter{PrettyPrint: true}, Path: "/tmp/go15.sum"}, expectedErr: nil},
+		"output of csv":               {args: []string{"-output=csv", "/tmp/go16.sum"}, expectedConfig: Configuration{Formatter: &audit.CsvFormatter{}, Path: "/tmp/go16.sum"}, expectedErr: nil},
+		"output of text":              {args: []string{"-output=text", "/tmp/go17.sum"}, expectedConfig: Configuration{Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go17.sum"}, expectedErr: nil},
+		"output of bad value":         {args: []string{"-output=aintgonnadoit", "/tmp/go18.sum"}, expectedConfig: Configuration{Formatter: &audit.AuditLogTextFormatter{}, Path: "/tmp/go18.sum"}, expectedErr: nil},
 	}
 
 	for name, test := range tests {
@@ -79,7 +85,7 @@ CVN-543`)
 
 			actualConfig, actualErr := Parse(test.args)
 			assert.Equal(t, test.expectedErr, actualErr)
-			assert.Equal(t, test.expectedConfig, actualConfig)
+			assert.ObjectsAreEqual(test.expectedConfig, actualConfig)
 		})
 	}
 }
diff --git a/packages/dep_int_test.go b/packages/dep_int_test.go
index c9121b6..cb1524e 100644
--- a/packages/dep_int_test.go
+++ b/packages/dep_int_test.go
@@ -23,7 +23,6 @@ import (
 	"testing"
 )
 
-
 func TestExtractPurlsFromManifestUsingDep(t *testing.T) {
 	path, projectDir, err := doGoPathSimulatedSetup(t)
 	defer filet.CleanUp(t)
@@ -74,12 +73,12 @@ func doGoPathSimulatedSetup(t *testing.T) (string, string, error) {
 	path := filet.TmpDir(t, dir)
 	fakeGoPath := fmt.Sprint(path, "/src")
 	e := os.Mkdir(fakeGoPath, os.ModePerm)
-	if e != nil{
+	if e != nil {
 		t.Error(e)
 	}
 	projectDir := fmt.Sprint(fakeGoPath, "/projectname")
 	e = os.Mkdir(projectDir, os.ModePerm)
-	if e != nil{
+	if e != nil {
 		t.Error(e)
 	}
 	lockBytes, _ := ioutil.ReadFile("../testdata/dep/Gopkg.lock")
@@ -104,7 +103,6 @@ func doGoPathSimulatedSetup(t *testing.T) (string, string, error) {
 	return path, projectDir, e
 }
 
-
 func inArray(val string, array []string) (exists bool) {
 	exists = false
 
diff --git a/packages/mod_test.go b/packages/mod_test.go
index 03fe576..13f681f 100644
--- a/packages/mod_test.go
+++ b/packages/mod_test.go
@@ -20,7 +20,6 @@ import (
 
 var testGoSumName = "go.sum"
 
-
 // Simulate calling parse.GopkgLock()
 func getProjectList() (projectList types.ProjectList) {
 	appendProject("github.com/AndreasBriese/bbloom", "", &projectList)