Compare commits
148
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23bc3cbcbc | ||
|
|
1a7225bc91 | ||
|
|
4ebaca3419 | ||
|
|
a96d28f120 | ||
|
|
29b67f0a05 | ||
|
|
c187f6f12d | ||
|
|
3b0a091baa | ||
|
|
3456819f12 | ||
|
|
30c4549c8c | ||
|
|
93c8cb2c8a | ||
|
|
d7c6d6203f | ||
|
|
92bcc5a0bf | ||
|
|
545050ada5 | ||
|
|
2b674f0e26 | ||
|
|
802525536f | ||
|
|
4eb9ad1d38 | ||
|
|
12cf02f216 | ||
|
|
c7ff505b05 | ||
|
|
90221b23f7 | ||
|
|
2f38c7e78c | ||
|
|
c235374b9d | ||
|
|
ae2949c9c1 | ||
|
|
3ae540bf96 | ||
|
|
1c15a1745e | ||
|
|
19b36f0933 | ||
|
|
0b9547aabf | ||
|
|
b327132e4b | ||
|
|
f9a13e70f4 | ||
|
|
db9f724163 | ||
|
|
7db11574b7 | ||
|
|
7063d0ca45 | ||
|
|
2dd55385c1 | ||
|
|
48729e4e38 | ||
|
|
230442bc30 | ||
|
|
4235242818 | ||
|
|
731e67eca2 | ||
|
|
b601c09c4e | ||
|
|
982e1d16cb | ||
|
|
f0a04841ce | ||
|
|
e622e72c6f | ||
|
|
92e40d7290 | ||
|
|
21763d05e0 | ||
|
|
2c245d1aba | ||
|
|
d6fb424a28 | ||
|
|
088fc4d4e8 | ||
|
|
132427b4bc | ||
|
|
5f0449f13c | ||
|
|
0b73ead548 | ||
|
|
67a046c994 | ||
|
|
64c25ba2f4 | ||
|
|
f3682c87a7 | ||
|
|
fc7745e42a | ||
|
|
a8dcc6b774 | ||
|
|
d09b96a7b1 | ||
|
|
243561faa0 | ||
|
|
860cc21fc2 | ||
|
|
98f8200aaa | ||
|
|
b3375e0be4 | ||
|
|
737f9b3a71 | ||
|
|
91660a5ad1 | ||
|
|
2b78124491 | ||
|
|
365fad2034 | ||
|
|
31314537ae | ||
|
|
c893395cf8 | ||
|
|
93e4466112 | ||
|
|
a9c83d3af6 | ||
|
|
f4b10ab0c4 | ||
|
|
a4da1f9048 | ||
|
|
19edfd7243 | ||
|
|
0be808458e | ||
|
|
77396f2e4f | ||
|
|
9bc6bded9e | ||
|
|
3b26a2a544 | ||
|
|
7517e23bfc | ||
|
|
cdae254423 | ||
|
|
a257e84a2f | ||
|
|
e0be07f423 | ||
|
|
4b83e15691 | ||
|
|
e4396493ba | ||
|
|
8ba008fb62 | ||
|
|
5ce46b3424 | ||
|
|
9680f24ea3 | ||
|
|
9cdb91e238 | ||
|
|
92e8876693 | ||
|
|
c91da44591 | ||
|
|
b988161c8f | ||
|
|
a086ec5a2d | ||
|
|
b40a0040b5 | ||
|
|
dcc694e92a | ||
|
|
dfafa144e7 | ||
|
|
7a2877d9c8 | ||
|
|
265d6e12a9 | ||
|
|
39e692fa32 | ||
|
|
0e2b63f1f4 | ||
|
|
0e9a322413 | ||
|
|
fdcc204dbb | ||
|
|
871c00fde8 | ||
|
|
52795b8e93 | ||
|
|
744ab92b2c | ||
|
|
0b8c1ff0d6 | ||
|
|
7dcdeab949 | ||
|
|
cabd238caa | ||
|
|
2fee08ee9a | ||
|
|
9571135e29 | ||
|
|
85d9dc08d0 | ||
|
|
005e168d77 | ||
|
|
9c59c3e487 | ||
|
|
e4574efd2a | ||
|
|
e343d06cbe | ||
|
|
3a4a231669 | ||
|
|
3b3aee2807 | ||
|
|
7e68ff5413 | ||
|
|
f3e7f2e17c | ||
|
|
5aadf9df79 | ||
|
|
2912ad058b | ||
|
|
41113f0103 | ||
|
|
457441cf81 | ||
|
|
53e123e9bc | ||
|
|
51033d1351 | ||
|
|
727184648e | ||
|
|
51f78cb35f | ||
|
|
2ac4ee7782 | ||
|
|
731c8509d5 | ||
|
|
58c9c8dc08 | ||
|
|
38015e8ba9 | ||
|
|
55aad1c2ed | ||
|
|
132849cc93 | ||
|
|
52530a057c | ||
|
|
f7d534938a | ||
|
|
27e65b9589 | ||
|
|
1d0829d84c | ||
|
|
e0e026c756 | ||
|
|
0e686847c0 | ||
|
|
43afa84d78 | ||
|
|
ac46ae2e5b | ||
|
|
ad9ad2d36d | ||
|
|
be26556282 | ||
|
|
c083fa1499 | ||
|
|
157075c780 | ||
|
|
6ddfe40705 | ||
|
|
ecf7f31121 | ||
|
|
79799f95b1 | ||
|
|
20749a73f2 | ||
|
|
047972e563 | ||
|
|
1fcd0f0cda | ||
|
|
11ad653c6c | ||
|
|
a7b1112790 | ||
|
|
b72e171434 |
@@ -0,0 +1,8 @@
|
|||||||
|
fail_on_severity: low
|
||||||
|
allow_licenses:
|
||||||
|
- 'GPL 3.0'
|
||||||
|
- 'BSD 3 Clause'
|
||||||
|
- 'MIT'
|
||||||
|
#deny_licenses:
|
||||||
|
# - "LGPL 2.0"
|
||||||
|
# - "BSD 2 Clause"
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
id: diff
|
id: diff
|
||||||
|
|
||||||
# If index.js was different than expected, upload the expected version as an artifact
|
# If index.js was different than expected, upload the expected version as an artifact
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v3
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
|||||||
+5
-1
@@ -40,7 +40,11 @@ npm run test
|
|||||||
## Local Development
|
## Local Development
|
||||||
|
|
||||||
We have a script to scan a given PR for vulnerabilities, this will
|
We have a script to scan a given PR for vulnerabilities, this will
|
||||||
help you test your local changes. Make sure to [grab a Personal Access Token (PAT)](https://github.com/settings/tokens) before proceeding!
|
help you test your local changes. Make sure to [grab a Personal Access Token (PAT)](https://github.com/settings/tokens) before proceeding (you'll need `repo` permissions for private repos):
|
||||||
|
|
||||||
|
<img width="480" alt="Screenshot 2022-05-12 at 10 22 21" src="https://user-images.githubusercontent.com/2161/168026161-16788a0a-b6c8-428e-bb6a-83ea2a403070.png">
|
||||||
|
|
||||||
|
The syntax of the script is:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ GITHUB_TOKEN=<token> ./scripts/scan_pr <pr_url>
|
$ GITHUB_TOKEN=<token> ./scripts/scan_pr <pr_url>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# dependency-review-action
|
# dependency-review-action
|
||||||
|
|
||||||
This Action scans your pull requests for vulnerabilities introduced
|
This action scans your pull requests for dependency changes and will raise an error if any new dependencies have existing vulnerabilities. The action is supported by an [API endpoint](https://docs.github.com/en/rest/reference/dependency-graph#dependency-review) that diffs the dependencies between any two revisions.
|
||||||
when adding or updating your project's dependencies. A check in your
|
|
||||||
Pull Requests will let notify you of the results.
|
The action is available for all public repositories, as well as private repositories that have Github Advanced Security licensed.
|
||||||
|
|
||||||
<img width="854" alt="Screen Shot 2022-03-31 at 1 10 51 PM" src="https://user-images.githubusercontent.com/2161/161042286-b22d7dd3-13cb-458d-8744-ce70ed9bf562.png">
|
<img width="854" alt="Screen Shot 2022-03-31 at 1 10 51 PM" src="https://user-images.githubusercontent.com/2161/161042286-b22d7dd3-13cb-458d-8744-ce70ed9bf562.png">
|
||||||
|
|
||||||
@@ -28,6 +28,8 @@ jobs:
|
|||||||
uses: actions/dependency-review-action@v1
|
uses: actions/dependency-review-action@v1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Please keep in mind that you need a GitHub Advanced Security license if you're running this Action on private repos.
|
||||||
|
|
||||||
## Getting help
|
## Getting help
|
||||||
|
|
||||||
If you have bug reports, questions or suggestions please [create a new
|
If you have bug reports, questions or suggestions please [create a new
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import {expect, test} from '@jest/globals'
|
||||||
|
import {readConfigFile} from '../src/config'
|
||||||
|
|
||||||
|
test('reads the config file', async () => {
|
||||||
|
let options = readConfigFile('./__tests__/fixtures/config-allow-sample.yml')
|
||||||
|
expect(options.fail_on_severity).toEqual('critical')
|
||||||
|
expect(options.allow_licenses).toEqual(['BSD', 'GPL 2'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('the default config path handles .yml and .yaml', async () => {
|
||||||
|
expect(true).toEqual(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns a default config when the config file was not found', async () => {
|
||||||
|
let options = readConfigFile('fixtures/i-dont-exist')
|
||||||
|
expect(options.fail_on_severity).toEqual('low')
|
||||||
|
expect(options.allow_licenses).toEqual([])
|
||||||
|
})
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import {expect, test} from '@jest/globals'
|
||||||
|
import {Change, Changes} from '../src/schemas'
|
||||||
|
import {filterChangesBySeverity} from '../src/filter'
|
||||||
|
|
||||||
|
let npmChange: Change = {
|
||||||
|
manifest: 'package.json',
|
||||||
|
change_type: 'added',
|
||||||
|
ecosystem: 'npm',
|
||||||
|
name: 'Reeuhq',
|
||||||
|
version: '1.0.2',
|
||||||
|
package_url: 'somepurl',
|
||||||
|
license: 'MIT',
|
||||||
|
source_repository_url: 'github.com/some-repo',
|
||||||
|
vulnerabilities: [
|
||||||
|
{
|
||||||
|
severity: 'critical',
|
||||||
|
advisory_ghsa_id: 'first-random_string',
|
||||||
|
advisory_summary: 'very dangerouns',
|
||||||
|
advisory_url: 'github.com/future-funk'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let rubyChange: Change = {
|
||||||
|
change_type: 'added',
|
||||||
|
manifest: 'Gemfile.lock',
|
||||||
|
ecosystem: 'rubygems',
|
||||||
|
name: 'actionsomething',
|
||||||
|
version: '3.2.0',
|
||||||
|
package_url: 'somerubypurl',
|
||||||
|
license: 'BSD',
|
||||||
|
source_repository_url: 'github.com/some-repo',
|
||||||
|
vulnerabilities: [
|
||||||
|
{
|
||||||
|
severity: 'moderate',
|
||||||
|
advisory_ghsa_id: 'second-random_string',
|
||||||
|
advisory_summary: 'not so dangerouns',
|
||||||
|
advisory_url: 'github.com/future-funk'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
severity: 'low',
|
||||||
|
advisory_ghsa_id: 'third-random_string',
|
||||||
|
advisory_summary: 'dont page me',
|
||||||
|
advisory_url: 'github.com/future-funk'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
test('it properly filters changes by severity', async () => {
|
||||||
|
const changes = [npmChange, rubyChange]
|
||||||
|
let result = filterChangesBySeverity('high', changes)
|
||||||
|
expect(result).toEqual([npmChange])
|
||||||
|
|
||||||
|
result = filterChangesBySeverity('low', changes)
|
||||||
|
expect(changes).toEqual([npmChange, rubyChange])
|
||||||
|
|
||||||
|
result = filterChangesBySeverity('critical', changes)
|
||||||
|
expect(changes).toEqual([npmChange, rubyChange])
|
||||||
|
})
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
fail_on_severity: critical
|
||||||
|
allow_licenses:
|
||||||
|
- "BSD"
|
||||||
|
- "GPL 2"
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import {expect, test} from '@jest/globals'
|
|
||||||
|
|
||||||
test('tests things', async () => {
|
|
||||||
expect(true).toEqual(true)
|
|
||||||
})
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
name: 'Dependency Review'
|
name: 'Dependency Review'
|
||||||
description: 'GitHub Action for Dependency Review'
|
description: 'Prevent the introduction of dependencies with known vulnerabilities'
|
||||||
author: 'GitHub'
|
author: 'GitHub'
|
||||||
inputs:
|
inputs:
|
||||||
repo-token:
|
repo-token:
|
||||||
|
|||||||
+11070
-2821
File diff suppressed because it is too large
Load Diff
+1
-1
File diff suppressed because one or more lines are too long
+17
@@ -684,6 +684,23 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
|||||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
yaml
|
||||||
|
ISC
|
||||||
|
Copyright Eemeli Aro <[email protected]>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
zod
|
zod
|
||||||
MIT
|
MIT
|
||||||
MIT License
|
MIT License
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Generated
+413
-475
File diff suppressed because it is too large
Load Diff
+18
-17
@@ -25,29 +25,30 @@
|
|||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.6.0",
|
"@actions/core": "^1.8.2",
|
||||||
"@actions/github": "^5.0.0",
|
"@actions/github": "^5.0.3",
|
||||||
"@octokit/plugin-retry": "^3.0.9",
|
"@octokit/plugin-retry": "^3.0.9",
|
||||||
"@octokit/request-error": "^2.1.0",
|
"@octokit/request-error": "^2.1.0",
|
||||||
"ansi-styles": "^6.1.0",
|
"ansi-styles": "^6.1.0",
|
||||||
"got": "^12.0.1",
|
"got": "^12.1.0",
|
||||||
"nodemon": "^2.0.15",
|
"nodemon": "^2.0.16",
|
||||||
"zod": "^3.13.4"
|
"yaml": "^2.1.1",
|
||||||
|
"zod": "^3.17.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.40",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
"@typescript-eslint/eslint-plugin": "^5.27.1",
|
||||||
"@typescript-eslint/parser": "^5.14.0",
|
"@typescript-eslint/parser": "^5.27.1",
|
||||||
"@vercel/ncc": "^0.33.3",
|
"@vercel/ncc": "^0.34.0",
|
||||||
"esbuild-register": "^3.3.2",
|
"esbuild-register": "^3.3.3",
|
||||||
"eslint": "^8.12.0",
|
"eslint": "^8.17.0",
|
||||||
"eslint-plugin-github": "^4.3.6",
|
"eslint-plugin-github": "^4.3.6",
|
||||||
"eslint-plugin-jest": "^26.1.1",
|
"eslint-plugin-jest": "^26.5.3",
|
||||||
"jest": "^27.5.1",
|
"jest": "^27.5.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"nodemon": "^2.0.15",
|
"nodemon": "^2.0.16",
|
||||||
"prettier": "2.6.1",
|
"prettier": "2.6.2",
|
||||||
"ts-jest": "^27.1.3",
|
"ts-jest": "^27.1.4",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.7.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import YAML from 'yaml'
|
||||||
|
import {ConfigurationOptions, ConfigurationOptionsSchema} from './schemas'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export const CONFIG_FILEPATH = './.github/dependency-review.yml'
|
||||||
|
|
||||||
|
export function readConfigFile(
|
||||||
|
filePath: string = CONFIG_FILEPATH
|
||||||
|
): ConfigurationOptions {
|
||||||
|
// By default we want to fail on all severities and allow all licenses.
|
||||||
|
const defaultOptions: ConfigurationOptions = {
|
||||||
|
fail_on_severity: 'low',
|
||||||
|
allow_licenses: []
|
||||||
|
}
|
||||||
|
|
||||||
|
let data
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = fs.readFileSync(path.resolve(filePath), 'utf-8')
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code && error.code === 'ENOENT') {
|
||||||
|
return defaultOptions
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = YAML.parse(data)
|
||||||
|
const parsed = ConfigurationOptionsSchema.parse(values)
|
||||||
|
|
||||||
|
return parsed
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {Changes} from './schemas'
|
||||||
|
import {Severity, SEVERITIES} from './schemas'
|
||||||
|
|
||||||
|
export function filterChangesBySeverity(
|
||||||
|
severity: Severity,
|
||||||
|
changes: Changes
|
||||||
|
): Changes {
|
||||||
|
const severityIdx = SEVERITIES.indexOf(severity)
|
||||||
|
let filteredChanges = []
|
||||||
|
for (let change of changes) {
|
||||||
|
if (
|
||||||
|
change === undefined ||
|
||||||
|
change.vulnerabilities === undefined ||
|
||||||
|
change.vulnerabilities.length === 0
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let fChange = {
|
||||||
|
...change,
|
||||||
|
vulnerabilities: change.vulnerabilities.filter(vuln => {
|
||||||
|
const vulnIdx = SEVERITIES.indexOf(vuln.severity)
|
||||||
|
if (vulnIdx <= severityIdx) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
filteredChanges.push(fChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't want to deal with changes with no vulnerabilities
|
||||||
|
filteredChanges = filteredChanges.filter(
|
||||||
|
change => change.vulnerabilities.length > 0
|
||||||
|
)
|
||||||
|
return filteredChanges
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (_) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.__esModule = true;
|
||||||
|
exports.parseGitHubUrl = exports.getApiClient = exports.getPlatform = exports.GITHUB_DOTCOM_URL = exports.Platform = void 0;
|
||||||
|
// Tons of code lifted from https://github.com/github/codeql-action!
|
||||||
|
var path = require("path");
|
||||||
|
var core = require("@actions/core");
|
||||||
|
var githubUtils = require("@actions/github/lib/utils");
|
||||||
|
var retry = require("@octokit/plugin-retry");
|
||||||
|
var Platform;
|
||||||
|
(function (Platform) {
|
||||||
|
Platform[Platform["DOTCOM"] = 0] = "DOTCOM";
|
||||||
|
Platform[Platform["GHES"] = 1] = "GHES";
|
||||||
|
Platform[Platform["GHAE"] = 2] = "GHAE";
|
||||||
|
})(Platform = exports.Platform || (exports.Platform = {}));
|
||||||
|
exports.GITHUB_DOTCOM_URL = 'https://github.com';
|
||||||
|
var GITHUB_ENTERPRISE_VERSION_HEADER = 'x-github-enterprise-version';
|
||||||
|
function getPlatform(url) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var apiClient, response;
|
||||||
|
return __generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
// We can avoid making an API request in the standard dotcom case
|
||||||
|
if (parseGitHubUrl(url) === exports.GITHUB_DOTCOM_URL) {
|
||||||
|
return [2 /*return*/, Platform.DOTCOM];
|
||||||
|
}
|
||||||
|
apiClient = (0, exports.getApiClient)(url);
|
||||||
|
return [4 /*yield*/, apiClient.rest.meta.get()];
|
||||||
|
case 1:
|
||||||
|
response = _a.sent();
|
||||||
|
if (response.headers[GITHUB_ENTERPRISE_VERSION_HEADER] === 'GitHub AE') {
|
||||||
|
return [2 /*return*/, Platform.GHAE];
|
||||||
|
}
|
||||||
|
return [2 /*return*/, Platform.GHES];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getPlatform = getPlatform;
|
||||||
|
var getApiClient = function (url) {
|
||||||
|
var auth = core.getInput('repo-token', { required: true });
|
||||||
|
var retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
|
||||||
|
return new retryingOctokit(githubUtils.getOctokitOptions(auth, {
|
||||||
|
baseUrl: getApiUrl(url)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
exports.getApiClient = getApiClient;
|
||||||
|
function getApiUrl(githubUrl) {
|
||||||
|
var url = new URL(githubUrl);
|
||||||
|
// If we detect this is trying to connect to github.com
|
||||||
|
// then return with a fixed canonical URL.
|
||||||
|
if (url.hostname === 'github.com' || url.hostname === 'api.github.com') {
|
||||||
|
return 'https://api.github.com';
|
||||||
|
}
|
||||||
|
// Add the /api/v3 API prefix
|
||||||
|
url.pathname = path.join(url.pathname, 'api', 'v3');
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parses user input of a github.com or GHES URL to a canonical form.
|
||||||
|
* Removes any API prefix or suffix if one is present.
|
||||||
|
*/
|
||||||
|
function parseGitHubUrl(inputUrl) {
|
||||||
|
var originalUrl = inputUrl;
|
||||||
|
if (inputUrl.indexOf('://') === -1) {
|
||||||
|
inputUrl = "https://".concat(inputUrl);
|
||||||
|
}
|
||||||
|
if (!inputUrl.startsWith('http://') && !inputUrl.startsWith('https://')) {
|
||||||
|
throw new Error("\"".concat(originalUrl, "\" is not a http or https URL"));
|
||||||
|
}
|
||||||
|
var url;
|
||||||
|
try {
|
||||||
|
url = new URL(inputUrl);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw new Error("\"".concat(originalUrl, "\" is not a valid URL"));
|
||||||
|
}
|
||||||
|
// If we detect this is trying to be to github.com
|
||||||
|
// then return with a fixed canonical URL.
|
||||||
|
if (url.hostname === 'github.com' || url.hostname === 'api.github.com') {
|
||||||
|
return exports.GITHUB_DOTCOM_URL;
|
||||||
|
}
|
||||||
|
// Remove the API prefix if it's present
|
||||||
|
if (url.pathname.indexOf('/api/v3') !== -1) {
|
||||||
|
url.pathname = url.pathname.substring(0, url.pathname.indexOf('/api/v3'));
|
||||||
|
}
|
||||||
|
// Also consider subdomain isolation on GHES
|
||||||
|
if (url.hostname.startsWith('api.')) {
|
||||||
|
url.hostname = url.hostname.substring(4);
|
||||||
|
}
|
||||||
|
// Normalise path to having a trailing slash for consistency
|
||||||
|
if (!url.pathname.endsWith('/')) {
|
||||||
|
url.pathname = "".concat(url.pathname, "/");
|
||||||
|
}
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
|
exports.parseGitHubUrl = parseGitHubUrl;
|
||||||
|
getPlatform(exports.GITHUB_DOTCOM_URL);
|
||||||
+38
-15
@@ -3,7 +3,9 @@ import * as dependencyGraph from './dependency-graph'
|
|||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import styles from 'ansi-styles'
|
import styles from 'ansi-styles'
|
||||||
import {RequestError} from '@octokit/request-error'
|
import {RequestError} from '@octokit/request-error'
|
||||||
import {PullRequestSchema} from './schemas'
|
import {Change, PullRequestSchema, Severity} from './schemas'
|
||||||
|
import {readConfigFile} from '../src/config'
|
||||||
|
import {filterChangesBySeverity} from '../src/filter'
|
||||||
|
|
||||||
async function run(): Promise<void> {
|
async function run(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -24,24 +26,22 @@ async function run(): Promise<void> {
|
|||||||
headRef: pull_request.head.sha
|
headRef: pull_request.head.sha
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let config = readConfigFile()
|
||||||
|
let minSeverity = config.fail_on_severity
|
||||||
let failed = false
|
let failed = false
|
||||||
|
|
||||||
for (const change of changes) {
|
let filteredChanges = filterChangesBySeverity(
|
||||||
|
minSeverity as Severity,
|
||||||
|
changes
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const change of filteredChanges) {
|
||||||
if (
|
if (
|
||||||
change.change_type === 'added' &&
|
change.change_type === 'added' &&
|
||||||
change.vulnerabilities !== undefined &&
|
change.vulnerabilities !== undefined &&
|
||||||
change.vulnerabilities.length > 0
|
change.vulnerabilities.length > 0
|
||||||
) {
|
) {
|
||||||
for (const vuln of change.vulnerabilities) {
|
printChangeVulnerabilities(change)
|
||||||
core.info(
|
|
||||||
`${styles.bold.open}${change.manifest} » ${change.name}@${
|
|
||||||
change.version
|
|
||||||
}${styles.bold.close} – ${vuln.advisory_summary} ${renderSeverity(
|
|
||||||
vuln.severity
|
|
||||||
)}`
|
|
||||||
)
|
|
||||||
core.info(` ↪ ${vuln.advisory_url}`)
|
|
||||||
}
|
|
||||||
failed = true
|
failed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,19 +49,42 @@ async function run(): Promise<void> {
|
|||||||
if (failed) {
|
if (failed) {
|
||||||
throw new Error('Dependency review detected vulnerable packages.')
|
throw new Error('Dependency review detected vulnerable packages.')
|
||||||
} else {
|
} else {
|
||||||
core.info('Dependency review did not detect any vulnerable packages.')
|
core.info(
|
||||||
|
`Dependency review did not detect any vulnerable packages with severity level "${minSeverity}" or above.`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof RequestError && error.status === 404) {
|
if (error instanceof RequestError && error.status === 404) {
|
||||||
|
core.setFailed(
|
||||||
|
`Dependency review could not obtain dependency data for the specified owner, repository, or revision range.`
|
||||||
|
)
|
||||||
|
} else if (error instanceof RequestError && error.status === 403) {
|
||||||
core.setFailed(
|
core.setFailed(
|
||||||
`Dependency review is not supported on this repository. Please ensure that Dependency graph is enabled, see https://github.com/${github.context.repo.owner}/${github.context.repo.repo}/settings/security_analysis`
|
`Dependency review is not supported on this repository. Please ensure that Dependency graph is enabled, see https://github.com/${github.context.repo.owner}/${github.context.repo.repo}/settings/security_analysis`
|
||||||
)
|
)
|
||||||
} else if (error instanceof Error) {
|
} else {
|
||||||
core.setFailed(error.message)
|
if (error instanceof Error) {
|
||||||
|
core.setFailed(error.message)
|
||||||
|
} else {
|
||||||
|
core.setFailed('Unexpected fatal error')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printChangeVulnerabilities(change: Change) {
|
||||||
|
for (const vuln of change.vulnerabilities) {
|
||||||
|
core.info(
|
||||||
|
`${styles.bold.open}${change.manifest} » ${change.name}@${
|
||||||
|
change.version
|
||||||
|
}${styles.bold.close} – ${vuln.advisory_summary} ${renderSeverity(
|
||||||
|
vuln.severity
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
core.info(` ↪ ${vuln.advisory_url}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderSeverity(
|
function renderSeverity(
|
||||||
severity: 'critical' | 'high' | 'moderate' | 'low'
|
severity: 'critical' | 'high' | 'moderate' | 'low'
|
||||||
): string {
|
): string {
|
||||||
|
|||||||
+19
-1
@@ -1,6 +1,8 @@
|
|||||||
import * as z from 'zod'
|
import * as z from 'zod'
|
||||||
|
|
||||||
const ChangeSchema = z.object({
|
export const SEVERITIES = ['critical', 'high', 'moderate', 'low'] as const
|
||||||
|
|
||||||
|
export const ChangeSchema = z.object({
|
||||||
change_type: z.enum(['added', 'removed']),
|
change_type: z.enum(['added', 'removed']),
|
||||||
manifest: z.string(),
|
manifest: z.string(),
|
||||||
ecosystem: z.string(),
|
ecosystem: z.string(),
|
||||||
@@ -19,6 +21,7 @@ const ChangeSchema = z.object({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.optional()
|
.optional()
|
||||||
|
.default([])
|
||||||
})
|
})
|
||||||
|
|
||||||
export const PullRequestSchema = z.object({
|
export const PullRequestSchema = z.object({
|
||||||
@@ -27,6 +30,21 @@ export const PullRequestSchema = z.object({
|
|||||||
head: z.object({sha: z.string()})
|
head: z.object({sha: z.string()})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const ConfigurationOptionsSchema = z
|
||||||
|
.object({
|
||||||
|
fail_on_severity: z.enum(SEVERITIES).default('low'),
|
||||||
|
allow_licenses: z.array(z.string()).default([]),
|
||||||
|
deny_licenses: z.array(z.string()).default([])
|
||||||
|
})
|
||||||
|
.partial()
|
||||||
|
.refine(
|
||||||
|
obj => !(obj.allow_licenses && obj.deny_licenses),
|
||||||
|
"Can't specify both allow_licenses and deny_licenses"
|
||||||
|
)
|
||||||
|
|
||||||
export const ChangesSchema = z.array(ChangeSchema)
|
export const ChangesSchema = z.array(ChangeSchema)
|
||||||
|
|
||||||
|
export type Change = z.infer<typeof ChangeSchema>
|
||||||
export type Changes = z.infer<typeof ChangesSchema>
|
export type Changes = z.infer<typeof ChangesSchema>
|
||||||
|
export type ConfigurationOptions = z.infer<typeof ConfigurationOptionsSchema>
|
||||||
|
export type Severity = typeof SEVERITIES[number]
|
||||||
|
|||||||
Reference in New Issue
Block a user