audit-app
v0.8.1
Published
A cli tool for auditing apps & packages using their respective package managers.
Downloads
34
Maintainers
Readme
audit-app
While
audit-app
is not officially deprecated, we strongly recommend usingosv-detector
instead - it does the exact same thing, only better! (and faster too)
A cli tool for auditing apps & packages using their respective package managers, outputting the results in a form that makes it easy to triage advisories, and providing support for ignoring advisories to keep your CI passing without having to sacrifice security.
NPM 7 workspaces
Workspaces (which are new in npm@7
) should be supported at about the same
level as npm audit
itself supports them; standard dependencies should be just
fine, but there may be edge-cases with file:
dependencies due to limitations
in resolving the dependency tree for these types of dependencies which affect
npm
itself.
For audit-app
, these edge-cases should primarily manifest as some
vulnerabilities technically being reported twice, which shouldn't prevent using
audit-app
.
If you have any other issues with workspaces, please let us know!
Also note that if you have a file:
dependency that has the same name as a
published npm
package (e.g. debug
), npm
will assume it is that published
package and so mark it affected by any advisories that may exist for the
dependencies version.
Getting Started
To run audit-app
as a once-off against an app, you can use npx
:
npx audit-app
If you want to use audit-app
regularly as part of your local development flow,
you can install it globally:
npm install --global audit-app
Options
All options can be provided in either camelCase or kebab-case format. These
options can be set either when calling audit-app
via the commandline, or via a
JSON config file.
--directory
, --dir
, -d
Default: the current working directory
Sets the directory that audit-app
will operate in. This effects other path
related options like --package-manager
and --config
.
audit-app --package-manager pnpm
--config
, -c
Default: .auditapprc.json
Points audit-app
to the configuration file to load options from. By default
audit-app
will look for a file called .auditapprc.json
in the directory that
is being audited (which can be set using --directory
).
The configuration file must contain standard JSON, with a top-level object, and with no comments, trailing commas, or single-quotes:
{
"packageManager": "yarn",
"ignore": ["1179|mkdirp>minimist"]
}
You can disable loading from a config file using --no-config.
--update-config-ignores
Default: false
If provided, audit-app
will attempt to update the config file pointed to by
--config
to contain an ignore
property made up of the vulnerabilities found
during the audit.
--package-manager
, -p
Default: auto
Supported values: auto
, npm
, yarn
, pnpm
Sets the package manager audit-app
will use to perform the audit. If set to
auto
, the package manager will be determined based on what lock files are
present in the directory being audited.
--output
, -o
Default: tables
Supported values: tables
, summary
, paths
, json
Sets the format that audit-app
should use to output the audit report. Here's a
brief rundown of the supported formats, and their use-cases:
summary
format
Outputs the report as a summary of the vulnerabilities that were found in the audited app, containing details on the number of instances of packages that have vulnerabilities, how many packages were checked, how many vulnerabilities were ignored, and a breakdown of the number of vulnerabilities per severity.
Some of these numbers are based on values provided by the underlying package manager that was used to perform the audit, so the numbers might not match with what you'd expect depending on the beliefs and implementations of the package manager in use.
tables
format
Outputs the report as a collection of concise tables along with a summary of the
report, similar to the output of npm audit
.
Unlike npm audit
however, the tables are per advisory rather than per
finding path, making the output a lot easier to manage when dealing with
advisories for popular packages that might appear a number of times in your
dependency tree (i.e lodash
).
Building the tables based on the advisories also means that ignored paths are not factored in to the table output. The number of paths for an advisory does not factor into if it will be outputted as a table, be it vulnerable, ignored or missing paths.
Here is an example of the output the tables
format results in:
┌────────────┬────────────────────────────────────────────────────────────────────┐
│ low │ Prototype Pollution (GHSA-p6mc-m468-83gw) │
├────────────┼────────────────────────────────────────────────────────────────────┤
│ Package │ lodash v4.17.15, v3.10.1 │
├────────────┼────────────────────────────────────────────────────────────────────┤
│ Patched in │ >=4.17.19 │
├────────────┼────────────────────────────────────────────────────────────────────┤
│ More info │ https://github.com/advisories/GHSA-p6mc-m468-83gw │
└────────────┴────────────────────────────────────────────────────────────────────┘
found 4327 vulnerabilities (including 0 ignored) across 693 packages
\: 4327 low
Information on the package the advisory pertains to, such as if the package is a dev dependency, the path(s) to the package, and what top-level package(s) lead to the affected package being included in the tree, are deliberately omitted as this information is typically very verbose and unhelpful at time of output.
Usually the easiest way to do this is by using your apps package manager with the appropriate command(s) for listing details of packages in the dependency tree that meet a given semver constraint.
For example, if the app that produced the table output above was using npm
,
you could get a tree showing what dependencies pulled in the affected versions
of lodash with the following:
npm ls '[email protected]||3.10.1'
Similarly, you could get a tree showing what, if any, versions of lodash existed in the tree that are patched by using the "Patched in" value:
npm ls 'lodash@>=4.17.19'
paths
format
Outputs a list of paths mapping each instance of an advisory to the top-level
package which results in them being pulled in, in the format
<advisory-id>|<dependency-path>
.
Since the list is sourced from the reports vulnerable
array rather than its
advisories
object, it won't include vulnerabilities that have been ignored.
This allows you to easily update your ignore
lists, as you can copy and paste
items from the list directly into the config.
This becomes even more powerful when combined with standard commandline
utilities such as grep
& clipboard utilities.
On Mac OS X you can use pbcopy
, and for Windows you can use clip.exe
. Linux
has a few different clipboards, such as xclip
, gpm
, and screen
:
audit-app --output paths | pbcopy # on OSX
audit-app --output paths | clip # on Windows (including WSL)
Note that for Windows, clip
works in both PowerShell & Windows System for
Linux.
Filtering can be done using grep
:
audit-app --output paths | grep '>@commitlint/load>' | clip
You can grep
-like filtering in PowerShell using findstr
:
audit-app --output paths | findstr '>@commitlint/load>' | clip
Clipboard contents:
GHSA-p6mc-m468-83gw|@commitlint/cli>@commitlint/load>@commitlint/resolve-extends>lodash GHSA-p6mc-m468-83gw|@commitlint/cli>@commitlint/load>lodash
If you're using a json config, you can use jq
to convert the output into a
valid JSON array that you can paste straight into your config:
audit-app --output paths | grep '>@commitlint/load>' | jq -nR '[inputs]'
You can do this in PowerShell like so:
(audit-app --output paths).split('\n') | ConvertTo-Json
json
format
Outputs the report as JSON using JSON.stringify
so that it can be easily used
by other tools.
If you're ignoring vulnerabilities using a json config, you can pipe the output
of the json format to a program like jq
to pick the vulnerable
array
If you have a lot of vulnerabilities that you wish to ignore, you can pipe the
json output to a program like jq
to select just the vulnerable
array and get
a valid json array as output for your clipboard:
audit-app --format json | jq '.vulnerable'
If you wish to select only some vulnerabilities, you can use filters like so:
audit-app --format json | jq '.vulnerable | map(select(startswith("GHSA-w7rc-rwvf-8q5r")))'
audit-app --format json | jq '.vulnerable | map(select(startswith("GHSA-w7rc-rwvf-8q5r")))'
If you're using Powershell, you can do this without jq
like so:
(audit-app --format json | ConvertFrom-Json).vulnerable | ConvertTo-Json
--ignore
, -i
Default: []
Tells audit-app
to ignore a vulnerability when determining if the audit
results should result in a failed audit run.
In the context of audit-app
, a "vulnerability" is an instance of an advisory,
represented by a string made up of the advisory's id, and the path to the
package on the dependency tree that is affected by the advisory, separated by a
pipe (|
); for example:
GHSA-abc1-123a-xyz9|mkdirp>minimist
You can provide this flag multiple times to ignore multiple vulnerabilities:
audit-app \
--ignore 'GHSA-ff7x-qrg7-qggm|@commitlint/cli>@commitlint/lint>@commitlint/parse>conventional-changelog-angular>compare-func>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|@commitlint/config-conventional>conventional-changelog-conventionalcommits>compare-func>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/commit-analyzer>conventional-changelog-angular>compare-func>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/release-notes-generator>conventional-changelog-angular>compare-func>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/release-notes-generator>conventional-changelog-writer>compare-func>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/npm>npm>libnpx>update-notifier>configstore>dot-prop' \
--ignore 'GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/npm>npm>update-notifier>configstore>dot-prop'
However, we recommend using an .auditapprc.json
file to make it easier to
track and update the list of ignored vulnerabilities:
{
"packageManager": "yarn",
"ignore": [
"GHSA-ff7x-qrg7-qggm|@commitlint/cli>@commitlint/lint>@commitlint/parse>conventional-changelog-angular>compare-func>dot-prop",
"GHSA-ff7x-qrg7-qggm|@commitlint/config-conventional>conventional-changelog-conventionalcommits>compare-func>dot-prop",
"GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/commit-analyzer>conventional-changelog-angular>compare-func>dot-prop",
"GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/release-notes-generator>conventional-changelog-angular>compare-func>dot-prop",
"GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/release-notes-generator>conventional-changelog-writer>compare-func>dot-prop",
"GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/npm>npm>libnpx>update-notifier>configstore>dot-prop",
"GHSA-ff7x-qrg7-qggm|semantic-release>@semantic-release/npm>npm>update-notifier>configstore>dot-prop"
]
}
You can have audit-app
attempt to update the config for you with the
--update-config-ignores
flag.
How it works
When run, audit-app
calls the audit command of either npm
, yarn
, or
pnpm
, and parses the results, normalising the output into an "audit report".
An audit report is an object with the following structure:
export interface AuditReport {
statistics: Statistics;
advisories: Advisories;
vulnerable: string[];
ignored: string[];
missing: string[];
}
The statistics
property holds an object that contains optional details about
aspects of the auditing run, and it's results, such as counts on the different
package types that were involved (total, dev, optional, etc).
The advisories
property is an object containing the advisories that were found
to effect at least one package in the tree during auditing, mapped by their id.
The vulnerable
, ignored
, and missing
properties are arrays which list the
vulnerabilities that were (or in the case of missing
, were not) found, based
on the findings for each advisory.
In the context of audit-app
, a "vulnerability" is an instance of an advisory,
represented by a string made up of the advisory's id, and the path to the
package on the dependency tree that is affected by the advisory, separated by a
pipe (|
).
After auditing has finished, audit-app
runs through the findings of each
advisory to create a list of the vulnerabilities that exist in the app that was
just audited, which is then cross-referenced with a list of vulnerabilities that
should be ignored, with any vulnerability found in both lists being removed from
vulnerable
. If a vulnerability is found in ignored
that is not in
vulnerable
, it's moved out of the ignored
array into missing
.
The ignored list is populated using the ignore
flag, which can be specified
multiple times:
audit-app \
--ignore GHSA-vh95-rmgr-6w4m|mkdirp>minimist
There is no support for ignoring an entire advisory, because doing so would mean new instances of an advisory could be introduced via an unknown path. For the same reason you also cannot ignore all advisories of a specific level.
While its possible that a very popular package could get an advisory posted against it that goes unpatch for a long period, resulting in a very large ignore list, there are two things to keep in mind:
Your configuration file represents the security health of your application - the fewer vulnerabilities you need to ignore, the healthier your application is. This also means you should apply the same way of thinking as you would to aspects such as size, dependency count, performance, etc.
Advisories are known vulnerabilities, meaning bad actors can find out exactly how to exploit a package with very little work.
Ultimately, in the same way that you'd consider replacing a dependency that was creating a bottleneck for your application, or a dependency that was excessively large, you should consider replacing a dependency if it's making your app less secure.
The paths
output format (detailed above) can be useful in updating your
ignores list by providing a list of all the current vulnerabilities in your apps
dependency tree that can be copied & pasted.