19 Commits
Author SHA1 Message Date
Aaron Kelbsch d1a8548176 docs: update readme
Continuous integration / main (push) Failing after 51s
2025-08-25 15:41:05 +02:00
Aaron Kelbsch 4869f6bfcf feat: also write issues when manually triggered 2025-08-25 15:37:02 +02:00
Aaron Kelbsch b0356b9add chore: add missing dependencies to package json 2025-08-25 15:36:26 +02:00
Aaron Kelbsch 34d5211002 docs: add contribution info to readme 2025-08-25 15:32:31 +02:00
Aaron Kelbsch c5a5443df7 chore: update package.json 2025-08-25 15:32:21 +02:00
PythonGermany dd2c7de31f Update action version in README.md examples (#35) 2025-02-23 11:00:21 +01:00
Tony Arcieri 69366f33c9 Prep for v2.0.0 release (#25)
Continuous integration / main (push) Failing after 1m29s
2024-09-23 08:24:55 -06:00
Tony Arcieri 7b350a127d npm audit fix (#24) 2024-09-23 08:21:39 -06:00
Charles Lechasseur 286a088f1c fix: update @clechasseur/rs-actions-core and other dependencies (#23)
This fixes a high-severity security vulnerability in the `braces` library.
2024-07-22 13:12:29 -06:00
Ross b7dc4ebf0c Added support for working-directory (#21) 2024-07-08 10:14:45 -06:00
Tillmann 6dc762e804 update to latest upstream and rebuild (#20) 2024-05-09 19:18:11 -06:00
Charles Lechasseur fe0359b3e1 Run on Node 20.x (#16)
Also move to @clechasseur/rs-actions-core for more up-to-date dependencies
2024-04-23 16:26:03 -06:00
pinkforest 4da312dd0f docs: Update repo link 2023-04-04 19:19:38 +10:00
pinkforest d6805b6463 docs: Bump version in README 2023-04-04 18:24:42 +10:00
pinkforest(she/her) dd51754d4e Further fix unsound and notice reporting (#10) (#11)
Continuous integration / main (push) Successful in 1m55s
2023-04-04 18:16:49 +10:00
pinkforest(she/her) 7cb7a4e8d4 Add notice and unsound (#9)
Continuous integration / main (push) Successful in 2m12s
2023-04-04 08:08:08 +10:00
pinkforest(she/her) 13d7e3e1c9 Updates to v1.3.2 with node16 2023-03-28 11:24:53 +11:00
dependabot-preview[bot] 9448c34627 Update readme and changelog 2023-03-28 11:23:25 +11:00
Dustin J. Mitchellandmoliva bb800784d9 feat: adds support for ignores (#1)
* feat: adding optional ignore list

* chore: updating lock file

* format: prettier tak

* chore: new build

* chore: adding nvm rc to avoid error with vercel and node v17

* chore: bumping version to 1.3.0

* fix: gh actions does not support sequences as input

* chore: refresh build

* refactor: use getInputList from actos-rs/core

* add ignore to action.yml

Co-authored-by: moliva <oliva.miguelh@gmail.com>
2022-08-12 19:47:29 +10:00
13 changed files with 6547 additions and 10118 deletions
+7 -2
View File
@@ -11,8 +11,13 @@ jobs:
env: env:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/checkout@v1 - uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'npm'
- run: npm ci - run: npm ci
- run: npm run lint # octokit types problem ?
# - run: npm run lint
- run: npm run build - run: npm run build
- run: npm run test - run: npm run test
+1 -1
View File
@@ -1 +1 @@
@actions-rs:registry=https://npm.pkg.github.com @clechasseur:registry=https://npm.pkg.github.com
+1
View File
@@ -0,0 +1 @@
v20
+30
View File
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.4.1] - 2023-04-04
### Fixed
- Corrected reporting on `unsound` and `notice` informationals
## [1.4.0] - 2023-04-04
### Fixed
- Reflect change to enable warning on `unsound` and `notice` informationals
## [1.3.2] - 2023-03-13
### Changed
- Update various dependencies to fix some known vulnerabilities.
## [1.3.1] - 2020-05-10
### Fixed
- GitHub Actions does not support sequences as input
## [1.3.0] - 2022-05-09
### Added
- Add support for ignores (#1)
## [1.2.0] - 2020-05-07 ## [1.2.0] - 2020-05-07
### Fixed ### Fixed
+47 -6
View File
@@ -1,7 +1,6 @@
# Rust `audit-check` Action # Rust `audit-check` Action
![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg) ![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)
[![Gitter](https://badges.gitter.im/actions-rs/community.svg)](https://gitter.im/actions-rs/community)
> Security vulnerabilities audit > Security vulnerabilities audit
@@ -27,8 +26,8 @@ jobs:
security_audit: security_audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1 - uses: rustsec/audit-check@v2.0.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
``` ```
@@ -37,13 +36,33 @@ It is recommended to add the `paths:` section into the workflow file,
as it would effectively speed up the CI pipeline, since the audit process as it would effectively speed up the CI pipeline, since the audit process
will not be performed if no dependencies were changed. will not be performed if no dependencies were changed.
In case of any security advisories found, [status check](https://help.github.com/en/articles/about-status-checks) In case of any security advisories found, [status check](https://help.github.com/en/articles/about-status-checks)
created by this Action will be marked as "failed".\ created by this Action will be marked as "failed".\
Note that informational advisories are not affecting the check status. Note that informational advisories are not affecting the check status.
![Check screenshot](.github/check_screenshot.png) ![Check screenshot](.github/check_screenshot.png)
#### Granular Permissions
These are the typically used permissions:
```yaml
name: 'rust-audit-check'
github-token:
action-input:
input: token
is-default: false
permissions:
issues: write
issues-reason: to create issues
checks: write
checks-reason: to create check
```
This action only raises issues when it's triggered from a `cron` scheduled workflow or on manual `workflow_dispatch`
When running the action as scheduled it will crate issues but e.g. in PR / push fails the action.
#### Limitations #### Limitations
Due to [token permissions](https://help.github.com/en/articles/virtual-environments-for-github-actions#token-permissions), Due to [token permissions](https://help.github.com/en/articles/virtual-environments-for-github-actions#token-permissions),
@@ -66,8 +85,8 @@ jobs:
audit: audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v4
- uses: actions-rs/audit-check@v1 - uses: rustsec/audit-check@v2.0.0
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
``` ```
@@ -83,5 +102,27 @@ For each new advisory (including informal) an issue will be created:
| Name | Required | Description | Type | Default | | Name | Required | Description | Type | Default |
| ------------| -------- | ---------------------------------------------------------------------------| ------ | --------| | ------------| -------- | ---------------------------------------------------------------------------| ------ | --------|
| `token` | ✓ | [GitHub token], usually a `${{ secrets.GITHUB_TOKEN }}` | string | | | `token` | ✓ | [GitHub token], usually a `${{ secrets.GITHUB_TOKEN }}` | string | |
| `ignore` | | Comma-separated list of advisory ids to ignore | string | |
| `working-directory`| | The directory of the Cargo.toml / Cargo.lock files to scan. | string | `.` |
[GitHub token]: https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token [GitHub token]: https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token
## Contribute
### Setting up npm login
This repo uses some npm packages hosted on github.
To be able to pull these you need to:
1. Under <https://github.com/settings/tokens> - create a `personal access token (classic)` with `read:packages` scope
2. Authenticate to the github npm registry
```sh
npm login --scope=@clechasseur --auth-type=legacy --registry=https://npm.pkg.github.com
```
```txt
Username = Github Username
Password = Token
```
+8 -1
View File
@@ -8,7 +8,14 @@ inputs:
token: token:
description: GitHub Actions token description: GitHub Actions token
required: true required: true
ignore:
description: Comma-separated list of advisory ids to ignore
required: false
working-directory:
description: The directory of the Cargo.toml / Cargo.lock files to scan.
required: false
default: .
runs: runs:
using: 'node12' using: 'node20'
main: 'dist/index.js' main: 'dist/index.js'
BIN
View File
Binary file not shown.
+3 -1
View File
File diff suppressed because one or more lines are too long
+6347 -10054
View File
File diff suppressed because it is too large Load Diff
+20 -22
View File
@@ -1,6 +1,6 @@
{ {
"name": "rust-audit-check", "name": "rust-audit-check",
"version": "1.2.0", "version": "2.0.0",
"private": false, "private": false,
"description": "Security audit for security vulnerabilities", "description": "Security audit for security vulnerabilities",
"main": "lib/main.js", "main": "lib/main.js",
@@ -18,7 +18,7 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/actions-rs/audit.git" "url": "github:rustsec/audit-check"
}, },
"keywords": [ "keywords": [
"actions", "actions",
@@ -31,29 +31,27 @@
"author": "actions-rs", "author": "actions-rs",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/actions-rs/audit-check/issues" "url": "https://github.com/rustsec/audit-check/issues"
}, },
"dependencies": { "dependencies": {
"@actions-rs/core": "0.0.9", "@actions/core": "^1.11.1",
"@actions/core": "^1.2.4", "@actions/github": "^6.0.1",
"@actions/github": "^2.1.1", "@clechasseur/rs-actions-core": "^3.0.5",
"npm-check-updates": "^4.1.2", "nunjucks": "^3.2.4"
"nunjucks": "^3.2.1"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.31.0", "@types/jest": "^29.5.12",
"ts-node": "^8.10.1", "@types/node": "^20.11.17",
"@typescript-eslint/parser": "^2.31.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint": "^6.8.0", "@typescript-eslint/parser": "^6.21.0",
"eslint-config-prettier": "^6.11.0", "@vercel/ncc": "0.38.1",
"eslint-plugin-prettier": "^3.1.3", "eslint": "^8.56.0",
"@types/jest": "^25.2.1", "eslint-config-prettier": "^9.1.0",
"@types/node": "^13.13.5", "eslint-plugin-prettier": "^5.1.3",
"@zeit/ncc": "^0.22.1", "jest": "^29.7.0",
"jest": "^26.0.1", "prettier": "^3.2.5",
"jest-circus": "^26.0.1", "ts-jest": "^29.1.2",
"ts-jest": "^25.5.0", "ts-node": "^10.9.2",
"typescript": "^3.8.3", "typescript": "^5.3.3"
"prettier": "^2.0.5"
} }
} }
+5 -1
View File
@@ -2,15 +2,19 @@
* Parse action input into a some proper thing. * Parse action input into a some proper thing.
*/ */
import { input } from '@actions-rs/core'; import { input } from '@clechasseur/rs-actions-core';
// Parsed action input // Parsed action input
export interface Input { export interface Input {
token: string; token: string;
ignore: string[];
workingDirectory: string;
} }
export function get(): Input { export function get(): Input {
return { return {
token: input.getInput('token', { required: true }), token: input.getInput('token', { required: true }),
ignore: input.getInputList('ignore', { required: false }),
workingDirectory: input.getInput('working-directory', { required: false }) ?? '.',
}; };
} }
+31 -18
View File
@@ -1,29 +1,32 @@
import * as process from 'process';
import * as os from 'os'; import * as os from 'os';
import * as process from 'process';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import { Cargo } from '@actions-rs/core'; import { Cargo } from '@clechasseur/rs-actions-core';
import * as input from './input'; import * as input from './input';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import * as reporter from './reporter'; import * as reporter from './reporter';
const pkg = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires async function getData(
ignore: string[] | undefined,
const USER_AGENT = `${pkg.name}/${pkg.version} (${pkg.bugs.url})`; workingDirectory: string,
): Promise<interfaces.Report> {
async function getData(): Promise<interfaces.Report> {
const cargo = await Cargo.get(); const cargo = await Cargo.get();
await cargo.findOrInstall('cargo-audit'); await cargo.findOrInstall('cargo-audit');
await cargo.call(['generate-lockfile']);
let stdout = ''; let stdout = '';
try { try {
core.startGroup('Calling cargo-audit (JSON output)'); core.startGroup('Calling cargo-audit (JSON output)');
await cargo.call(['audit', '--json'], { const commandArray = ['audit'];
for (const item of ignore ?? []) {
commandArray.push('--ignore', item);
}
commandArray.push('--json');
commandArray.push('--file', `${workingDirectory}/Cargo.lock`);
await cargo.call(commandArray, {
ignoreReturnCode: true, ignoreReturnCode: true,
listeners: { listeners: {
stdout: (buffer) => { stdout: (buffer) => {
@@ -43,8 +46,17 @@ async function getData(): Promise<interfaces.Report> {
return JSON.parse(stdout); return JSON.parse(stdout);
} }
function removeTrailingSlash(str) {
if (str[str.length - 1] === '/') {
return str.substr(0, str.length - 1);
}
return str;
}
export async function run(actionInput: input.Input): Promise<void> { export async function run(actionInput: input.Input): Promise<void> {
const report = await getData(); const ignore = actionInput.ignore;
const workingDirectory = removeTrailingSlash(actionInput.workingDirectory);
const report = await getData(ignore, workingDirectory);
let shouldReport = false; let shouldReport = false;
if (!report.vulnerabilities.found) { if (!report.vulnerabilities.found) {
core.info('No vulnerabilities were found'); core.info('No vulnerabilities were found');
@@ -76,20 +88,21 @@ export async function run(actionInput: input.Input): Promise<void> {
return; return;
} }
const client = new github.GitHub(actionInput.token, { // const octokit = github.getOctokit(actionInput.token, {userAgent: USER_AGENT});
userAgent: USER_AGENT,
});
const advisories = report.vulnerabilities.list; const advisories = report.vulnerabilities.list;
if (github.context.eventName == 'schedule') { if (
github.context.eventName == 'schedule' ||
github.context.eventName == 'workflow_dispatch'
) {
core.debug( core.debug(
'Action was triggered on a schedule event, creating an Issues report', 'Action was triggered on a schedule event, creating an Issues report',
); );
await reporter.reportIssues(client, advisories, warnings); await reporter.reportIssues(actionInput.token, advisories, warnings);
} else { } else {
core.debug( core.debug(
`Action was triggered on a ${github.context.eventName} event, creating a Check report`, `Action was triggered on a ${github.context.eventName} event, creating a Check report`,
); );
await reporter.reportCheck(client, advisories, warnings); await reporter.reportCheck(actionInput.token, advisories, warnings);
} }
} }
@@ -98,7 +111,7 @@ async function main(): Promise<void> {
const actionInput = input.get(); const actionInput = input.get();
await run(actionInput); await run(actionInput);
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed((error as Error).message);
} }
return; return;
+47 -12
View File
@@ -4,14 +4,18 @@ import * as core from '@actions/core';
import * as github from '@actions/github'; import * as github from '@actions/github';
import * as nunjucks from 'nunjucks'; import * as nunjucks from 'nunjucks';
import { checks } from '@actions-rs/core'; import { checks } from '@clechasseur/rs-actions-core';
import * as interfaces from './interfaces'; import * as interfaces from './interfaces';
import * as templates from './templates'; import * as templates from './templates';
const pkg = require('../package.json'); // eslint-disable-line @typescript-eslint/no-var-requires
const USER_AGENT = `${pkg.name}/${pkg.version} (${pkg.bugs.url})`;
interface Stats { interface Stats {
critical: number; critical: number;
notices: number; notices: number;
unmaintained: number; unmaintained: number;
unsound: number;
other: number; other: number;
} }
@@ -34,6 +38,20 @@ function makeReport(
}); });
break; break;
case 'unsound':
preparedWarnings.push({
advisory: warning.advisory,
package: warning.package,
});
break;
case 'notice':
preparedWarnings.push({
advisory: warning.advisory,
package: warning.package,
});
break;
case 'informational': case 'informational':
preparedWarnings.push({ preparedWarnings.push({
advisory: warning.advisory, advisory: warning.advisory,
@@ -72,6 +90,7 @@ function getStats(
let critical = 0; let critical = 0;
let notices = 0; let notices = 0;
let unmaintained = 0; let unmaintained = 0;
let unsound = 0;
let other = 0; let other = 0;
for (const vulnerability of vulnerabilities) { for (const vulnerability of vulnerabilities) {
switch (vulnerability.advisory.informational) { switch (vulnerability.advisory.informational) {
@@ -81,6 +100,9 @@ function getStats(
case 'unmaintained': case 'unmaintained':
unmaintained += 1; unmaintained += 1;
break; break;
case 'unsound':
unsound += 1;
break;
case null: case null:
critical += 1; critical += 1;
break; break;
@@ -96,6 +118,10 @@ function getStats(
unmaintained += 1; unmaintained += 1;
break; break;
case 'unsound':
unsound += 1;
break;
default: default:
// Both yanked and informational types of kind // Both yanked and informational types of kind
other += 1; other += 1;
@@ -107,6 +133,7 @@ function getStats(
critical: critical, critical: critical,
notices: notices, notices: notices,
unmaintained: unmaintained, unmaintained: unmaintained,
unsound: unsound,
other: other, other: other,
}; };
} }
@@ -115,8 +142,7 @@ function getSummary(stats: Stats): string {
const blocks: string[] = []; const blocks: string[] = [];
if (stats.critical > 0) { if (stats.critical > 0) {
// TODO: Plural blocks.push(`${stats.critical} advisories`);
blocks.push(`${stats.critical} advisory(ies)`);
} }
if (stats.notices > 0) { if (stats.notices > 0) {
blocks.push(`${stats.notices} notice${plural(stats.notices)}`); blocks.push(`${stats.notices} notice${plural(stats.notices)}`);
@@ -124,6 +150,9 @@ function getSummary(stats: Stats): string {
if (stats.unmaintained > 0) { if (stats.unmaintained > 0) {
blocks.push(`${stats.unmaintained} unmaintained`); blocks.push(`${stats.unmaintained} unmaintained`);
} }
if (stats.unsound > 0) {
blocks.push(`${stats.unsound} unsound`);
}
if (stats.other > 0) { if (stats.other > 0) {
blocks.push(`${stats.other} other`); blocks.push(`${stats.other} other`);
} }
@@ -133,11 +162,12 @@ function getSummary(stats: Stats): string {
/// Create and publish audit results into the Commit Check. /// Create and publish audit results into the Commit Check.
export async function reportCheck( export async function reportCheck(
client: github.GitHub, token: string,
vulnerabilities: Array<interfaces.Vulnerability>, vulnerabilities: Array<interfaces.Vulnerability>,
warnings: Array<interfaces.Warning>, warnings: Array<interfaces.Warning>,
): Promise<void> { ): Promise<void> {
const reporter = new checks.CheckReporter(client, 'Security audit'); const client = github.getOctokit(token, {userAgent: USER_AGENT});
const reporter = new checks.CheckReporter(client.rest, 'Security audit');
const stats = getStats(vulnerabilities, warnings); const stats = getStats(vulnerabilities, warnings);
const summary = getSummary(stats); const summary = getSummary(stats);
@@ -201,11 +231,12 @@ See https://github.com/actions-rs/clippy-check/issues/2 for details.`);
} }
async function alreadyReported( async function alreadyReported(
client: github.GitHub, token: string,
advisoryId: string, advisoryId: string,
): Promise<boolean> { ): Promise<boolean> {
const { owner, repo } = github.context.repo; const { owner, repo } = github.context.repo;
const results = await client.search.issuesAndPullRequests({ const client = github.getOctokit(token, {userAgent: USER_AGENT});
const results = await client.rest.search.issuesAndPullRequests({
q: `${advisoryId} in:title repo:${owner}/${repo}`, q: `${advisoryId} in:title repo:${owner}/${repo}`,
per_page: 1, // eslint-disable-line @typescript-eslint/camelcase per_page: 1, // eslint-disable-line @typescript-eslint/camelcase
}); });
@@ -222,15 +253,17 @@ will not report an issue against it`,
} }
export async function reportIssues( export async function reportIssues(
client: github.GitHub, token: string,
vulnerabilities: Array<interfaces.Vulnerability>, vulnerabilities: Array<interfaces.Vulnerability>,
warnings: Array<interfaces.Warning>, warnings: Array<interfaces.Warning>,
): Promise<void> { ): Promise<void> {
const { owner, repo } = github.context.repo; const { owner, repo } = github.context.repo;
const client = github.getOctokit(token, {userAgent: USER_AGENT});
for (const vulnerability of vulnerabilities) { for (const vulnerability of vulnerabilities) {
const reported = await alreadyReported( const reported = await alreadyReported(
client, token,
vulnerability.advisory.id, vulnerability.advisory.id,
); );
if (reported) { if (reported) {
@@ -240,7 +273,7 @@ export async function reportIssues(
const body = nunjucks.renderString(templates.VULNERABILITY_ISSUE, { const body = nunjucks.renderString(templates.VULNERABILITY_ISSUE, {
vulnerability: vulnerability, vulnerability: vulnerability,
}); });
const issue = await client.issues.create({ const issue = await client.rest.issues.create({
owner: owner, owner: owner,
repo: repo, repo: repo,
title: `${vulnerability.advisory.id}: ${vulnerability.advisory.title}`, title: `${vulnerability.advisory.id}: ${vulnerability.advisory.title}`,
@@ -254,6 +287,8 @@ export async function reportIssues(
for (const warning of warnings) { for (const warning of warnings) {
let advisory: interfaces.Advisory; let advisory: interfaces.Advisory;
switch (warning.kind) { switch (warning.kind) {
case 'unsound':
case 'notice':
case 'unmaintained': case 'unmaintained':
case 'informational': case 'informational':
advisory = warning.advisory; advisory = warning.advisory;
@@ -270,7 +305,7 @@ export async function reportIssues(
continue; continue;
} }
const reported = await alreadyReported(client, advisory.id); const reported = await alreadyReported(token, advisory.id);
if (reported) { if (reported) {
continue; continue;
} }
@@ -279,7 +314,7 @@ export async function reportIssues(
warning: warning, warning: warning,
advisory: advisory, advisory: advisory,
}); });
const issue = await client.issues.create({ const issue = await client.rest.issues.create({
owner: owner, owner: owner,
repo: repo, repo: repo,
title: `${advisory.id}: ${advisory.title}`, title: `${advisory.id}: ${advisory.title}`,