Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0659a74c94 | |||
| 28facf5722 | |||
| 5ab7b74146 | |||
| 95b6fa4e6b | |||
| 2dba7fdde1 | |||
| 7d44c7c392 | |||
| ce31ee8325 | |||
| df1b3661fd | |||
| 71c57a6108 | |||
| 7e2c3c347b | |||
| f456418f6a | |||
| 19bd35e07b | |||
| ff97293707 | |||
| 5498b6c4c3 | |||
| 80116a4564 | |||
| 68488bcecb | |||
| 16a0212a77 | |||
| 6d3fba9bf2 | |||
| c6cc8585a0 | |||
| c32a0148b3 | |||
| 67d0214607 | |||
| 3ca15314ff | |||
| a318e62c6c | |||
| 061f471b83 | |||
| 012eca3d4d | |||
| 8739aa4bb3 | |||
| a323510dae | |||
| 7cebd9d64d | |||
| f8ca44e2de | |||
| 411e5ec44f | |||
| 72aedfc147 | |||
| 2ce029c676 | |||
| 1c949fbe77 | |||
| bddd13d857 | |||
| 0e665bf3ac | |||
| 5bbc3ba658 | |||
| c59184aa7f | |||
| 54c06574f4 | |||
| 21941b530b | |||
| 733dd5d4a5 | |||
| 9093495859 | |||
| 35b83b4207 | |||
| e057056594 | |||
| d684d038b2 | |||
| 2b0aaf1638 | |||
| d9209374af | |||
| 651d22c5d5 | |||
| 02b13f6b52 | |||
| 6e0fa26ac3 | |||
| 0fa40c3c10 | |||
| 1f6240f54c | |||
| b751d41e7e | |||
| 6183eb9d2b | |||
| 6585cc5f01 | |||
| 218a76cbd5 | |||
| d78d095945 | |||
| 36297aa214 | |||
| 1e69a8c24a | |||
| 2f10643938 | |||
| 1eb83b5560 | |||
| 47b7acc8e3 | |||
| 16bfb3360c | |||
| bc6a1f0dd4 | |||
| e38e634e54 | |||
| 5574be321f | |||
| aef51371b7 | |||
| 3b70c9966e | |||
| adaed32746 | |||
| 4ce120135b | |||
| 0e8bc32a54 | |||
| f875e6ec1d | |||
| 72666694f0 | |||
| 7dc5f537be | |||
| ac600387ca | |||
| d186d663df | |||
| b7fdb4c8e2 | |||
| ba6b805e18 | |||
| 70801db78f | |||
| 5bc19761c5 | |||
| 5ba0d0fe17 | |||
| 6a74ebd41e | |||
| 250250e73d | |||
| cb0a0415fb | |||
| 296bf3ab1b | |||
| 59d4782b76 | |||
| e878bf8824 | |||
| 1b21f392ca | |||
| 111227a118 | |||
| a1258f2a2e | |||
| 29b9ef447a | |||
| b5a1aee21a | |||
| b3d2872ac7 | |||
| 5bace73db3 | |||
| f8ebb4b946 | |||
| 84b80e6e84 | |||
| 1251834b92 | |||
| 94125c4b1e | |||
| 9843156266 | |||
| 2fcc6a1c72 | |||
| ea64ae9d4d | |||
| 5955069e69 | |||
| 05fcfa49e0 | |||
| 75be7f0c0c | |||
| 7d2e20d06d | |||
| 97f7ba06d0 | |||
| 2bc3ecb19b | |||
| c286ea91b0 | |||
| 6bcbf042ff | |||
| 43286afc54 | |||
| 764e39e792 | |||
| bf2683a10c | |||
| f1b66d10c9 | |||
| ffd129c285 | |||
| 72d5b06a68 | |||
| b2ddac1749 | |||
| f357c751be | |||
| e16e218fdc | |||
| f87cc241f7 | |||
| d641d3a261 | |||
| 4230610a70 | |||
| af5438b06f | |||
| 781bff117a | |||
| 7a8ce509c9 | |||
| f419e37c19 | |||
| 3d70a3cf05 | |||
| e7aef164a1 | |||
| ab8c3848de | |||
| eecc9aab88 | |||
| 7f632dbe1f | |||
| ba18fafa8d | |||
| 40eb2b8b00 | |||
| db6a5119ce |
@@ -17,10 +17,12 @@ jobs:
|
||||
with:
|
||||
stale-pr-message: "👋 This pull request has been marked as stale because it has been open with no activity. You can: comment on the issue or remove the stale label to hold stale off for a while, add the `Keep` label to hold stale off permanently, or do nothing. If you do nothing, this pull request will be closed eventually by the stale bot. Please see CONTRIBUTING.md for more policy details."
|
||||
stale-pr-label: "Stale"
|
||||
stale-issue-label: "Stale"
|
||||
exempt-pr-labels: "Keep" # a "Keep" label will keep the PR from being closed as stale
|
||||
exempt-issue-labels: "Keep" # a "Keep" label will keep the issue from being closed as stale
|
||||
days-before-pr-stale: 180 # when the PR is considered stale
|
||||
days-before-pr-close: 15 # when the PR is closed by the bot,
|
||||
days-before-issue-stale: 180 # prevents issues from being tagged by the bot
|
||||
days-before-issue-close: 15 # prevents issues from being closed by the bot
|
||||
days-before-pr-close: 15 # when the PR is closed by the bot
|
||||
days-before-issue-stale: 180 # when the issue is considered stale
|
||||
days-before-issue-close: 15 # when the issue is closed by the bot
|
||||
exempt-assignees: 'advanced-security-dependency-graph'
|
||||
ascending: true
|
||||
|
||||
+8
-2
@@ -77,13 +77,15 @@ $ GITHUB_TOKEN=<token> ./scripts/scan_pr --config-file my_custom_config.yml <pr_
|
||||
|
||||
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
||||
|
||||
- Write tests.
|
||||
- Add unit tests for new features.
|
||||
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
||||
- Write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
- Add examples of the usage to [examples.md](docs/examples.md)
|
||||
- Link to a sample PR in a custom repository running your version of the Action.
|
||||
|
||||
## Cutting a new release
|
||||
|
||||
1. Update the version number in [package.json](https://github.com/actions/dependency-review-action/blob/main/package.json).
|
||||
1. Update the version number in [package.json](https://github.com/actions/dependency-review-action/blob/main/package.json) and run `npm i` to update the lockfile.
|
||||
1. Go to [Draft a new
|
||||
release](https://github.com/actions/dependency-review-action/releases/new)
|
||||
in the Releases page.
|
||||
@@ -116,6 +118,10 @@ git tag -fa v4 -m "Updating v4 to 4.0.1"
|
||||
git push origin v4 --force
|
||||
```
|
||||
|
||||
## Stalebot
|
||||
|
||||
We have begun using a [Stalebot action](https://github.com/actions/stale) to help keep the Issues and Pull requests backlogs tidy. You can see the configuration [here](.github/workflows/stalebot.yml). If you'd like to keep an issue open after getting a stalebot warning, simply comment on it and it'll reset the clock.
|
||||
|
||||
## Resources
|
||||
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
|
||||
@@ -66,23 +66,25 @@ jobs:
|
||||
|
||||
Configure this action by either inlining these options in your workflow file, or by using an external configuration file. All configuration options are optional.
|
||||
|
||||
| Option | Usage | Possible values | Default value |
|
||||
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------- |
|
||||
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
|
||||
| `allow-licenses`\* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `deny-licenses`\* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `fail-on-scopes` | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. | `runtime`, `development`, `unknown` | `runtime` |
|
||||
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
||||
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
|
||||
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
|
||||
| `allow-dependencies-licenses`\* | Contains a list of packages that will be excluded from license checks. | Any package(s) in [purl](https://github.com/package-url/purl-spec) format | none |
|
||||
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
|
||||
| `comment-summary-in-pr` | Enable or disable reporting the review summary as a comment in the pull request. If enabled, you must give the workflow or job permission `pull-requests: write`. | `always`, `on-failure`, `never` | `never` |
|
||||
| `deny-packages` | Any number of packages to block in a PR. | Package(s) in [purl](https://github.com/package-url/purl-spec) format | empty |
|
||||
| `deny-groups` | Any number of groups (namespaces) to block in a PR. | Namespace(s) in [purl](https://github.com/package-url/purl-spec) format (no package name, no version number) | empty |
|
||||
| `retry-on-snapshot-warnings`\* | Enable or disable retrying the action every 10 seconds while waiting for dependency submission actions to complete. | `true`, `false` | `false` |
|
||||
| `retry-on-snapshot-warnings-timeout`\* | Maximum amount of time (in seconds) to retry the action while waiting for dependency submission actions to complete. | Any positive integer | 120 |
|
||||
| `warn-only`+ | When set to `true`, the action will log all vulnerabilities as warnings regardless of the severity, and the action will complete with a `success` status. This overrides the `fail-on-severity` option. | `true`, `false` | `false` |
|
||||
| Option | Usage | Possible values | Default value |
|
||||
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------- |
|
||||
| `fail-on-severity` | Defines the threshold for the level of severity. The action will fail on any pull requests that introduce vulnerabilities of the specified severity level or higher. | `low`, `moderate`, `high`, `critical` | `low` |
|
||||
| `allow-licenses`\* | Contains a list of allowed licenses. The action will fail on pull requests that introduce dependencies with licenses that do not match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `deny-licenses`\* | Contains a list of prohibited licenses. The action will fail on pull requests that introduce dependencies with licenses that match the list. | Any [SPDX-compliant identifier(s)](https://spdx.org/licenses/) | none |
|
||||
| `fail-on-scopes` | Contains a list of strings of the build environments you want to support. The action will fail on pull requests that introduce vulnerabilities in the scopes that match the list. | `runtime`, `development`, `unknown` | `runtime` |
|
||||
| `allow-ghsas` | Contains a list of GitHub Advisory Database IDs that can be skipped during detection. | Any GHSAs from the [GitHub Advisory Database](https://github.com/advisories) | none |
|
||||
| `license-check` | Enable or disable the license check performed by the action. | `true`, `false` | `true` |
|
||||
| `vulnerability-check` | Enable or disable the vulnerability check performed by the action. | `true`, `false` | `true` |
|
||||
| `allow-dependencies-licenses`\* | Contains a list of packages that will be excluded from license checks. | Any package(s) in [purl](https://github.com/package-url/purl-spec) format | none |
|
||||
| `base-ref`/`head-ref` | Provide custom git references for the git base/head when performing the comparison check. This is only used for event types other than `pull_request` and `pull_request_target`. | Any valid git ref(s) in your project | none |
|
||||
| `comment-summary-in-pr` | Enable or disable reporting the review summary as a comment in the pull request. If enabled, you must give the workflow or job the `pull-requests: write` permission. | `always`, `on-failure`, `never` | `never` |
|
||||
| `deny-packages` | Any number of packages to block in a PR. This option will match on the exact version provided. If no version is provided, the option will treat the specified package as a wildcard and deny all versions. | Package(s) in [purl](https://github.com/package-url/purl-spec) format | empty |
|
||||
| `deny-groups` | Any number of groups (namespaces) to block in a PR. | Namespace(s) in [purl](https://github.com/package-url/purl-spec) format (no package name, no version number) | empty |
|
||||
| `retry-on-snapshot-warnings`\* | Enable or disable retrying the action every 10 seconds while waiting for dependency submission actions to complete. | `true`, `false` | `false` |
|
||||
| `retry-on-snapshot-warnings-timeout`\* | Maximum amount of time (in seconds) to retry the action while waiting for dependency submission actions to complete. | Any positive integer | 120 |
|
||||
| `warn-only`+ | When set to `true`, the action will log all vulnerabilities as warnings regardless of the severity, and the action will complete with a `success` status. This overrides the `fail-on-severity` option. | `true`, `false` | `false` |
|
||||
| `show-openssf-scorecard-levels` | When set to `true`, the action will output information about all the known OpenSSF Scorecard scores for the dependencies changed in this pull request. | `true`, `false` | `true` |
|
||||
| `warn-on-openssf-scorecard-level` | When `show-openssf-scorecard-levels` is set to `true`, this option lets you configure the threshold for when a score is considered too low and gets a :warning: warning in the CI. | Any positive integer | 3 |
|
||||
|
||||
\*not supported for use with GitHub Enterprise Server
|
||||
|
||||
@@ -150,7 +152,7 @@ For more examples of how to use this action and its configuration options, see t
|
||||
### Considerations
|
||||
|
||||
- Checking for licenses is not supported on Enterprise Server as the API does not return license information.
|
||||
- The action will only accept one of the two `license` parameters; an error will be raised if you provide both.
|
||||
- The `allow-licenses` and `deny-licenses` options are mutually exclusive; an error will be raised if you provide both.
|
||||
- We don't have license information for all of your dependents. If we can't detect the license for a dependency **we will inform you, but the action won't fail**.
|
||||
|
||||
## Blocking pull requests
|
||||
@@ -159,7 +161,26 @@ The Dependency Review GitHub Action check will only block a pull request from be
|
||||
|
||||
## Outputs
|
||||
|
||||
`comment-content` is generated with the same content as would be present in a Dependency Review Action comment.
|
||||
- `comment-content` is generated with the same content as would be present in a Dependency Review Action comment.
|
||||
- `dependency-changes` holds all dependency changes in a JSON format. The following outputs are subsets of `dependency-changes` filtered based on the configuration:
|
||||
- `vulnerable-changes` holds information about dependency changes with vulnerable dependencies in a JSON format.
|
||||
- `invalid-license-changes` holds information about invalid or non-compliant license dependency changes in a JSON format.
|
||||
- `denied-changes` holds information about denied dependency changes in a JSON format.
|
||||
|
||||
> [!NOTE]
|
||||
> Action outputs are unicode strings [with a 1MB size limit](https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs-for-docker-container-and-javascript-actions).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you use these outputs in a run-step, you must store the output data in an environment variable instead of using the output directly. Using an output directly might break shell scripts. For example:
|
||||
>
|
||||
> ```yaml
|
||||
> env:
|
||||
> VULNERABLE_CHANGES: ${{ steps.review.outputs.vulnerable-changes }}
|
||||
> run: |
|
||||
> echo "$VULNERABLE_CHANGES" | jq
|
||||
> ```
|
||||
>
|
||||
> instead of direct `echo '${{ steps.review.outputs.vulnerable-changes }}'`. See [examples](docs/examples.md) for more.
|
||||
|
||||
## Getting help
|
||||
|
||||
|
||||
+66
-110
@@ -1,100 +1,7 @@
|
||||
import {expect, jest, test} from '@jest/globals'
|
||||
import {Change, Changes} from '../src/schemas'
|
||||
|
||||
let getDeniedChanges: Function
|
||||
|
||||
const npmChange: Change = {
|
||||
manifest: 'package.json',
|
||||
change_type: 'added',
|
||||
ecosystem: 'npm',
|
||||
name: 'Reeuhq',
|
||||
version: '1.0.2',
|
||||
package_url: 'pkg:npm/reeuhq@1.0.2',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'first-random_string',
|
||||
advisory_summary: 'very dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const rubyChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'Gemfile.lock',
|
||||
ecosystem: 'rubygems',
|
||||
name: 'actionsomething',
|
||||
version: '3.2.0',
|
||||
package_url: 'pkg:gem/actionsomething@3.2.0',
|
||||
license: 'BSD',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const pipChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'requirements.txt',
|
||||
ecosystem: 'pip',
|
||||
name: 'package-1',
|
||||
version: '1.1.1',
|
||||
package_url: 'pkg:pypi/package-1@1.1.1',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mvnChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'pom.xml',
|
||||
ecosystem: 'maven',
|
||||
name: 'org.apache.logging.log4j:log4j-core',
|
||||
version: '2.15.0',
|
||||
package_url: 'pkg:maven/org.apache.logging.log4j/log4j-core@2.14.7',
|
||||
license: 'Apache-2.0',
|
||||
source_repository_url:
|
||||
'https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core',
|
||||
scope: 'unknown',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
import {createTestChange, createTestPURLs} from './fixtures/create-test-change'
|
||||
import {getDeniedChanges} from '../src/deny'
|
||||
|
||||
jest.mock('@actions/core')
|
||||
|
||||
@@ -108,6 +15,11 @@ const mockOctokit = {
|
||||
}
|
||||
}
|
||||
|
||||
let npmChange: Change
|
||||
let rubyChange: Change
|
||||
let pipChange: Change
|
||||
let mvnChange: Change
|
||||
|
||||
jest.mock('octokit', () => {
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||
@@ -126,40 +38,84 @@ beforeEach(async () => {
|
||||
// true for BSD, false for all others
|
||||
return jest.fn((license: string, _: string): boolean => license === 'BSD')
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
;({getDeniedChanges} = require('../src/deny'))
|
||||
|
||||
npmChange = createTestChange({ecosystem: 'npm'})
|
||||
rubyChange = createTestChange({ecosystem: 'rubygems'})
|
||||
pipChange = createTestChange({ecosystem: 'pip'})
|
||||
mvnChange = createTestChange({ecosystem: 'maven'})
|
||||
})
|
||||
|
||||
test('it adds packages in the deny packages list', async () => {
|
||||
test('denies packages from the deny packages list', async () => {
|
||||
const changes: Changes = [npmChange, rubyChange]
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
['pkg:gem/actionsomething'],
|
||||
[]
|
||||
)
|
||||
const deniedPackages = createTestPURLs(['pkg:gem/actionsomething@3.2.0'])
|
||||
const deniedChanges = await getDeniedChanges(changes, deniedPackages)
|
||||
|
||||
expect(deniedChanges[0]).toBe(rubyChange)
|
||||
expect(deniedChanges.length).toEqual(1)
|
||||
})
|
||||
|
||||
test('it adds packages in the deny group list', async () => {
|
||||
const changes: Changes = [mvnChange, rubyChange]
|
||||
test('denies packages only for the specified version from deny packages list', async () => {
|
||||
const deniedPackageWithDifferentVersion = createTestPURLs([
|
||||
'pkg:npm/lodash@1.2.3'
|
||||
])
|
||||
const changes: Changes = [npmChange]
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
[],
|
||||
['pkg:maven/org.apache.logging.log4j']
|
||||
deniedPackageWithDifferentVersion
|
||||
)
|
||||
|
||||
expect(deniedChanges.length).toEqual(0)
|
||||
})
|
||||
|
||||
test('if no specified version from deny packages list, it will treat package as wildcard and deny all versions', async () => {
|
||||
const changes: Changes = [
|
||||
createTestChange({name: 'lodash', version: '1.2.3'}),
|
||||
createTestChange({name: 'lodash', version: '4.5.6'}),
|
||||
createTestChange({name: 'lodash', version: '7.8.9'})
|
||||
]
|
||||
const denyAllLodashVersions = createTestPURLs(['pkg:npm/lodash'])
|
||||
const deniedChanges = await getDeniedChanges(changes, denyAllLodashVersions)
|
||||
|
||||
expect(deniedChanges.length).toEqual(3)
|
||||
})
|
||||
|
||||
test('denies packages from the deny group list', async () => {
|
||||
const changes: Changes = [mvnChange, rubyChange]
|
||||
const deniedGroups = createTestPURLs(['pkg:maven/org.apache.logging.log4j/'])
|
||||
const deniedChanges = await getDeniedChanges(changes, [], deniedGroups)
|
||||
|
||||
expect(deniedChanges[0]).toBe(mvnChange)
|
||||
expect(deniedChanges.length).toEqual(1)
|
||||
})
|
||||
|
||||
test('it adds packages outside of the deny lists', async () => {
|
||||
test('denies packages that match the deny group list exactly', async () => {
|
||||
const changes: Changes = [
|
||||
createTestChange({
|
||||
package_url: 'pkg:npm/org.test.pass/pass-this@1.0.0',
|
||||
ecosystem: 'npm'
|
||||
}),
|
||||
createTestChange({
|
||||
package_url: 'pkg:npm/org.test/deny-this@1.0.0',
|
||||
ecosystem: 'npm'
|
||||
})
|
||||
]
|
||||
const deniedGroups = createTestPURLs(['pkg:npm/org.test/'])
|
||||
const deniedChanges = await getDeniedChanges(changes, [], deniedGroups)
|
||||
|
||||
expect(deniedChanges.length).toEqual(1)
|
||||
expect(deniedChanges[0]).toBe(changes[1])
|
||||
})
|
||||
|
||||
test('allows packages not defined in the deny packages and groups list', async () => {
|
||||
const changes: Changes = [npmChange, pipChange]
|
||||
const deniedPackages = createTestPURLs([
|
||||
'pkg:gem/package-not-in-changes@1.0.0'
|
||||
])
|
||||
const deniedGroups = createTestPURLs(['pkg:maven/group.not.in.changes/'])
|
||||
const deniedChanges = await getDeniedChanges(
|
||||
changes,
|
||||
['pkg:gem/actionsomething'],
|
||||
['pkg:maven:org.apache.logging.log4j']
|
||||
deniedPackages,
|
||||
deniedGroups
|
||||
)
|
||||
|
||||
expect(deniedChanges.length).toEqual(0)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {PackageURL} from 'packageurl-js'
|
||||
import {Change} from '../../src/schemas'
|
||||
import {createTestVulnerability} from './create-test-vulnerability'
|
||||
import {parsePURL} from '../../src/utils'
|
||||
|
||||
const defaultChange: Change = {
|
||||
const defaultNpmChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'package.json',
|
||||
ecosystem: 'npm',
|
||||
@@ -28,9 +30,98 @@ const defaultChange: Change = {
|
||||
]
|
||||
}
|
||||
|
||||
const createTestChange = (overwrites: Partial<Change> = {}): Change => ({
|
||||
...defaultChange,
|
||||
...overwrites
|
||||
})
|
||||
const defaultRubyChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'Gemfile.lock',
|
||||
ecosystem: 'rubygems',
|
||||
name: 'actionsomething',
|
||||
version: '3.2.0',
|
||||
package_url: 'pkg:gem/actionsomething@3.2.0',
|
||||
license: 'BSD',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export {createTestChange}
|
||||
const defaultPipChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'requirements.txt',
|
||||
ecosystem: 'pip',
|
||||
name: 'package-1',
|
||||
version: '1.1.1',
|
||||
package_url: 'pkg:pypi/package-1@1.1.1',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/some-repo',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'moderate',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const defaultMavenChange: Change = {
|
||||
change_type: 'added',
|
||||
manifest: 'pom.xml',
|
||||
ecosystem: 'maven',
|
||||
name: 'org.apache.logging.log4j:log4j-core',
|
||||
version: '2.15.0',
|
||||
package_url: 'pkg:maven/org.apache.logging.log4j/log4j-core@2.14.7',
|
||||
license: 'Apache-2.0',
|
||||
source_repository_url:
|
||||
'https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core',
|
||||
scope: 'unknown',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'second-random_string',
|
||||
advisory_summary: 'not so dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const ecosystemToDefaultChange: {[key: string]: Change} = {
|
||||
npm: defaultNpmChange,
|
||||
rubygems: defaultRubyChange,
|
||||
pip: defaultPipChange,
|
||||
maven: defaultMavenChange
|
||||
}
|
||||
|
||||
const createTestChange = (overwrites: Partial<Change> = {}): Change => {
|
||||
const ecosystem = overwrites.ecosystem || 'npm'
|
||||
return {
|
||||
...ecosystemToDefaultChange[ecosystem],
|
||||
...overwrites
|
||||
}
|
||||
}
|
||||
|
||||
const createTestPURLs = (list: string[]): PackageURL[] => {
|
||||
return list.map(purl => {
|
||||
return parsePURL(purl)
|
||||
})
|
||||
}
|
||||
|
||||
export {createTestChange, createTestPURLs}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import {expect, test} from '@jest/globals'
|
||||
import {Change, Changes} from '../src/schemas'
|
||||
import {getScorecardLevels, getProjectUrl} from '../src/scorecard'
|
||||
|
||||
const npmChange: Change = {
|
||||
manifest: 'package.json',
|
||||
change_type: 'added',
|
||||
ecosystem: 'npm',
|
||||
name: 'type-is',
|
||||
version: '1.6.18',
|
||||
package_url: 'pkg:npm/type-is@1.6.18',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'github.com/jshttp/type-is',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: [
|
||||
{
|
||||
severity: 'critical',
|
||||
advisory_ghsa_id: 'first-random_string',
|
||||
advisory_summary: 'very dangerous',
|
||||
advisory_url: 'github.com/future-funk'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const actionsChange: Change = {
|
||||
manifest: 'workflow.yml',
|
||||
change_type: 'added',
|
||||
ecosystem: 'actions',
|
||||
name: 'actions/checkout/',
|
||||
version: 'v3',
|
||||
package_url: 'pkg:githubactions/actions@v3',
|
||||
license: 'MIT',
|
||||
source_repository_url: 'null',
|
||||
scope: 'runtime',
|
||||
vulnerabilities: []
|
||||
}
|
||||
|
||||
test('Get scorecard from API', async () => {
|
||||
const changes: Changes = [npmChange]
|
||||
const scorecard = await getScorecardLevels(changes)
|
||||
expect(scorecard).not.toBeNull()
|
||||
expect(scorecard.dependencies).toHaveLength(1)
|
||||
expect(scorecard.dependencies[0].scorecard?.score).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('Get project URL from deps.dev API', async () => {
|
||||
const result = await getProjectUrl(
|
||||
npmChange.ecosystem,
|
||||
npmChange.name,
|
||||
npmChange.version
|
||||
)
|
||||
expect(result).not.toBeNull()
|
||||
})
|
||||
|
||||
test('Handles Actions special case', async () => {
|
||||
const changes: Changes = [actionsChange]
|
||||
const result = await getScorecardLevels(changes)
|
||||
expect(result).not.toBeNull()
|
||||
expect(result.dependencies).toHaveLength(1)
|
||||
expect(result.dependencies[0].scorecard?.score).toBeGreaterThan(0)
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import {expect, jest, test} from '@jest/globals'
|
||||
import {Changes, ConfigurationOptions} from '../src/schemas'
|
||||
import {Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
|
||||
import * as summary from '../src/summary'
|
||||
import * as core from '@actions/core'
|
||||
import {createTestChange} from './fixtures/create-test-change'
|
||||
@@ -16,6 +16,9 @@ const emptyInvalidLicenseChanges = {
|
||||
unresolved: [],
|
||||
unlicensed: []
|
||||
}
|
||||
const emptyScorecard: Scorecard = {
|
||||
dependencies: []
|
||||
}
|
||||
const defaultConfig: ConfigurationOptions = {
|
||||
vulnerability_check: true,
|
||||
license_check: true,
|
||||
@@ -29,7 +32,9 @@ const defaultConfig: ConfigurationOptions = {
|
||||
comment_summary_in_pr: true,
|
||||
retry_on_snapshot_warnings: false,
|
||||
retry_on_snapshot_warnings_timeout: 120,
|
||||
warn_only: false
|
||||
warn_only: false,
|
||||
warn_on_openssf_scorecard_level: 3,
|
||||
show_openssf_scorecard: false
|
||||
}
|
||||
|
||||
const changesWithEmptyManifests: Changes = [
|
||||
@@ -71,11 +76,32 @@ const changesWithEmptyManifests: Changes = [
|
||||
}
|
||||
]
|
||||
|
||||
const scorecard: Scorecard = {
|
||||
dependencies: [
|
||||
{
|
||||
change: {
|
||||
change_type: 'added',
|
||||
manifest: '',
|
||||
ecosystem: 'unknown',
|
||||
name: 'castore',
|
||||
version: '0.1.17',
|
||||
package_url: 'pkg:hex/castore@0.1.17',
|
||||
license: null,
|
||||
source_repository_url: null,
|
||||
scope: 'runtime',
|
||||
vulnerabilities: []
|
||||
},
|
||||
scorecard: null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
test('prints headline as h1', () => {
|
||||
summary.addSummaryToSummary(
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
scorecard,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -88,6 +114,7 @@ test('only includes "No vulnerabilities or license issues found"-message if both
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -101,6 +128,7 @@ test('only includes "No vulnerabilities found"-message if "license_check" is set
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
config
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -114,6 +142,7 @@ test('only includes "No license issues found"-message if "vulnerability_check" i
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
config
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -126,6 +155,7 @@ test('groups dependencies with empty manifest paths together', () => {
|
||||
changesWithEmptyManifests,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
summary.addScannedDependencies(changesWithEmptyManifests)
|
||||
@@ -143,6 +173,7 @@ test('does not include status section if nothing was found', () => {
|
||||
emptyChanges,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
const text = core.summary.stringify()
|
||||
@@ -165,6 +196,7 @@ test('includes count and status icons for all findings', () => {
|
||||
vulnerabilities,
|
||||
licenseIssues,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
@@ -184,6 +216,7 @@ test('uses checkmarks for license issues if only vulnerabilities were found', ()
|
||||
vulnerabilities,
|
||||
emptyInvalidLicenseChanges,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
@@ -207,6 +240,7 @@ test('uses checkmarks for vulnerabilities if only license issues were found', ()
|
||||
emptyChanges,
|
||||
licenseIssues,
|
||||
emptyChanges,
|
||||
emptyScorecard,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
|
||||
+25
-6
@@ -1,5 +1,13 @@
|
||||
# Avoid using default values for options here since they will
|
||||
# end up overriding external configurations.
|
||||
# IMPORTANT
|
||||
#
|
||||
# Avoid setting default values for configuration options in
|
||||
# this file, they will overwrite external configurations.
|
||||
#
|
||||
# If you are trying to find out the default value for a config
|
||||
# option please take a look at the README or src/schemas.ts.
|
||||
#
|
||||
# If you are adding an option, make sure the Zod definition
|
||||
# contains a default value.
|
||||
name: 'Dependency Review'
|
||||
description: 'Prevent the introduction of dependencies with known vulnerabilities'
|
||||
author: 'GitHub'
|
||||
@@ -48,7 +56,7 @@ inputs:
|
||||
description: Determines if the summary is posted as a comment in the PR itself. Setting this to `always` or `on-failure` requires you to give the workflow the write permissions for pull-requests
|
||||
required: false
|
||||
deny-packages:
|
||||
description: A comma-separated list of package URLs to deny (e.g. "pkg:npm/express, pkg:pypi/pycrypto")
|
||||
description: A comma-separated list of package URLs to deny (e.g. "pkg:npm/express, pkg:pypi/pycrypto"). If version specified, only deny matching packages and version; else, deny all regardless of version.
|
||||
required: false
|
||||
deny-groups:
|
||||
description: A comma-separated list of package URLs for group(s)/namespace(s) to deny (e.g. "pkg:npm/express, pkg:pypi/pycrypto")
|
||||
@@ -56,18 +64,29 @@ inputs:
|
||||
retry-on-snapshot-warnings:
|
||||
description: Whether to retry on snapshot warnings
|
||||
required: false
|
||||
default: false
|
||||
retry-on-snapshot-warnings-timeout:
|
||||
description: Number of seconds to wait before stopping snapshot retries.
|
||||
required: false
|
||||
default: 120
|
||||
warn-only:
|
||||
description: When set to `true` this action will always complete with success, overriding the `fail-on-severity` parameter.
|
||||
required: false
|
||||
default: false
|
||||
show-openssf-scorecard:
|
||||
description: Show a summary of the OpenSSF Scorecard scores.
|
||||
required: false
|
||||
warn-on-openssf-scorecard-level:
|
||||
description: Numeric threshold for the OpenSSF Scorecard score. If the score is below this threshold, the action will warn you.
|
||||
required: false
|
||||
outputs:
|
||||
comment-content:
|
||||
description: Prepared dependency report comment
|
||||
dependency-changes:
|
||||
description: All dependency changes (JSON)
|
||||
vulnerable-changes:
|
||||
description: Vulnerable dependency changes (JSON)
|
||||
invalid-license-changes:
|
||||
description: Invalid license dependency changes (JSON)
|
||||
denied-changes:
|
||||
description: Denied dependency changes (JSON)
|
||||
|
||||
runs:
|
||||
using: 'node20'
|
||||
|
||||
+416
-76
@@ -56,10 +56,15 @@ const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
|
||||
const octo = new retryingOctokit(githubUtils.getOctokitOptions(core.getInput('repo-token', { required: true })));
|
||||
// Comment Marker to identify an existing comment to update, so we don't spam the PR with comments
|
||||
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->';
|
||||
function commentPr(summary) {
|
||||
function commentPr(summary, config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const commentContent = summary.stringify();
|
||||
core.setOutput('comment-content', commentContent);
|
||||
if (!(config.comment_summary_in_pr === 'always' ||
|
||||
(config.comment_summary_in_pr === 'on-failure' &&
|
||||
process.exitCode === core.ExitCode.Failure))) {
|
||||
return;
|
||||
}
|
||||
if (!github.context.payload.pull_request) {
|
||||
core.warning('Not in the context of a pull request. Skipping comment creation.');
|
||||
return;
|
||||
@@ -101,8 +106,8 @@ function commentPr(summary) {
|
||||
}
|
||||
exports.commentPr = commentPr;
|
||||
function findCommentByMarker(commentBodyIncludes) {
|
||||
var _a, e_1, _b, _c;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
var _a, e_1, _b, _c;
|
||||
const commentsIterator = octo.paginate.iterator(octo.rest.issues.listComments, {
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
@@ -174,31 +179,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getDeniedChanges = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
function getDeniedChanges(changes, deniedPackages, deniedGroups) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const packageurl_js_1 = __nccwpck_require__(8915);
|
||||
function getDeniedChanges(changes_1) {
|
||||
return __awaiter(this, arguments, void 0, function* (changes, deniedPackages = [], deniedGroups = []) {
|
||||
const changesDenied = [];
|
||||
let failed = false;
|
||||
let hasDeniedPackage = false;
|
||||
for (const change of changes) {
|
||||
change.name = change.name.toLowerCase();
|
||||
const packageUrl = change.package_url.toLowerCase().split('@')[0];
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (packageUrl === denied.split('@')[0].toLowerCase()) {
|
||||
changesDenied.push(change);
|
||||
failed = true;
|
||||
}
|
||||
const changedPackage = packageurl_js_1.PackageURL.fromString(change.package_url);
|
||||
for (const denied of deniedPackages) {
|
||||
if ((!denied.version || changedPackage.version === denied.version) &&
|
||||
changedPackage.name === denied.name) {
|
||||
changesDenied.push(change);
|
||||
hasDeniedPackage = true;
|
||||
}
|
||||
}
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (packageUrl.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change);
|
||||
failed = true;
|
||||
}
|
||||
for (const denied of deniedGroups) {
|
||||
if (changedPackage.namespace &&
|
||||
changedPackage.namespace === denied.namespace) {
|
||||
changesDenied.push(change);
|
||||
hasDeniedPackage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
if (hasDeniedPackage) {
|
||||
core.setFailed('Dependency review detected denied packages.');
|
||||
}
|
||||
else {
|
||||
@@ -258,8 +261,8 @@ const schemas_1 = __nccwpck_require__(8774);
|
||||
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry);
|
||||
const SnapshotWarningsHeader = 'x-github-dependency-graph-snapshot-warnings';
|
||||
const octo = new retryingOctokit(githubUtils.getOctokitOptions(core.getInput('repo-token', { required: true })));
|
||||
function compare({ owner, repo, baseRef, headRef }) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
function compare(_a) {
|
||||
return __awaiter(this, arguments, void 0, function* ({ owner, repo, baseRef, headRef }) {
|
||||
let snapshot_warnings = '';
|
||||
const changes = yield octo.paginate({
|
||||
method: 'GET',
|
||||
@@ -353,8 +356,8 @@ const spdx_satisfies_1 = __importDefault(__nccwpck_require__(4424));
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
const packageurl_js_1 = __nccwpck_require__(8915);
|
||||
function getInvalidLicenseChanges(changes, licenses) {
|
||||
var _a;
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
var _a;
|
||||
const { allow, deny } = licenses;
|
||||
const licenseExclusions = (_a = licenses.licenseExclusions) === null || _a === void 0 ? void 0 : _a.map((pkgUrl) => {
|
||||
return packageurl_js_1.PackageURL.fromString(encodeURI(pkgUrl));
|
||||
@@ -560,6 +563,7 @@ const request_error_1 = __nccwpck_require__(537);
|
||||
const config_1 = __nccwpck_require__(6373);
|
||||
const filter_1 = __nccwpck_require__(134);
|
||||
const licenses_1 = __nccwpck_require__(3247);
|
||||
const scorecard_1 = __nccwpck_require__(307);
|
||||
const summary = __importStar(__nccwpck_require__(8608));
|
||||
const git_refs_1 = __nccwpck_require__(1086);
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
@@ -630,29 +634,35 @@ function run() {
|
||||
core.debug(`Filtered Changes: ${JSON.stringify(filteredChanges)}`);
|
||||
core.debug(`Config Deny Packages: ${JSON.stringify(config)}`);
|
||||
const deniedChanges = yield (0, deny_1.getDeniedChanges)(filteredChanges, config.deny_packages, config.deny_groups);
|
||||
summary.addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, config);
|
||||
const scorecard = yield (0, scorecard_1.getScorecardLevels)(filteredChanges);
|
||||
summary.addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config);
|
||||
if (snapshot_warnings) {
|
||||
summary.addSnapshotWarnings(config, snapshot_warnings);
|
||||
}
|
||||
if (config.vulnerability_check) {
|
||||
core.setOutput('vulnerable-changes', JSON.stringify(vulnerableChanges));
|
||||
summary.addChangeVulnerabilitiesToSummary(vulnerableChanges, minSeverity);
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity, warnOnly);
|
||||
}
|
||||
if (config.license_check) {
|
||||
core.setOutput('invalid-license-changes', JSON.stringify(invalidLicenseChanges));
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config);
|
||||
printLicensesBlock(invalidLicenseChanges, warnOnly);
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
core.setOutput('denied-changes', JSON.stringify(deniedChanges));
|
||||
summary.addDeniedToSummary(deniedChanges);
|
||||
printDeniedDependencies(deniedChanges, config);
|
||||
}
|
||||
if (config.show_openssf_scorecard) {
|
||||
summary.addScorecardToSummary(scorecard, config);
|
||||
printScorecardBlock(scorecard, config);
|
||||
createScorecardWarnings(scorecard, config);
|
||||
}
|
||||
core.setOutput('dependency-changes', JSON.stringify(changes));
|
||||
summary.addScannedDependencies(changes);
|
||||
printScannedDependencies(changes);
|
||||
if (config.comment_summary_in_pr === 'always' ||
|
||||
(config.comment_summary_in_pr === 'on-failure' &&
|
||||
process.exitCode === core.ExitCode.Failure)) {
|
||||
yield (0, comment_pr_1.commentPr)(core.summary);
|
||||
}
|
||||
yield (0, comment_pr_1.commentPr)(core.summary, config);
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof request_error_1.RequestError && error.status === 404) {
|
||||
@@ -739,6 +749,20 @@ function printNullLicenses(changes) {
|
||||
core.info(`${ansi_styles_1.default.bold.open}${change.manifest} » ${change.name}@${change.version}${ansi_styles_1.default.bold.close}`);
|
||||
}
|
||||
}
|
||||
function printScorecardBlock(scorecard, config) {
|
||||
core.group('Scorecard', () => __awaiter(this, void 0, void 0, function* () {
|
||||
var _a, _b, _c, _d;
|
||||
if (scorecard) {
|
||||
for (const dependency of scorecard.dependencies) {
|
||||
if (((_a = dependency.scorecard) === null || _a === void 0 ? void 0 : _a.score) &&
|
||||
((_b = dependency.scorecard) === null || _b === void 0 ? void 0 : _b.score) < config.warn_on_openssf_scorecard_level) {
|
||||
core.info(`${ansi_styles_1.default.color.red.open}${dependency.change.ecosystem}/${dependency.change.name}: OpenSSF Scorecard Score: ${(_c = dependency === null || dependency === void 0 ? void 0 : dependency.scorecard) === null || _c === void 0 ? void 0 : _c.score}${ansi_styles_1.default.red.close}`);
|
||||
}
|
||||
core.info(`${dependency.change.ecosystem}/${dependency.change.name}: OpenSSF Scorecard Score: ${(_d = dependency === null || dependency === void 0 ? void 0 : dependency.scorecard) === null || _d === void 0 ? void 0 : _d.score}`);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
function renderSeverity(severity) {
|
||||
const color = {
|
||||
critical: 'red',
|
||||
@@ -786,6 +810,20 @@ function printDeniedDependencies(changes, config) {
|
||||
}
|
||||
}));
|
||||
}
|
||||
function createScorecardWarnings(scorecards, config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
var _a, _b, _c;
|
||||
// Iterate through the list of scorecards, and if the score is less than the threshold, send a warning
|
||||
for (const dependency of scorecards.dependencies) {
|
||||
if (((_a = dependency.scorecard) === null || _a === void 0 ? void 0 : _a.score) &&
|
||||
((_b = dependency.scorecard) === null || _b === void 0 ? void 0 : _b.score) < config.warn_on_openssf_scorecard_level) {
|
||||
core.warning(`${dependency.change.ecosystem}/${dependency.change.name} has an OpenSSF Scorecard of ${(_c = dependency.scorecard) === null || _c === void 0 ? void 0 : _c.score}, which is less than this repository's threshold of ${config.warn_on_openssf_scorecard_level}.`, {
|
||||
title: 'OpenSSF Scorecard Warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
run();
|
||||
|
||||
|
||||
@@ -820,11 +858,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
exports.ScorecardSchema = exports.ScorecardApiSchema = exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
exports.SEVERITIES = ['critical', 'high', 'moderate', 'low'];
|
||||
exports.SCOPES = ['unknown', 'runtime', 'development'];
|
||||
exports.SeveritySchema = z.enum(exports.SEVERITIES).default('low');
|
||||
const PackageURL = z.string().transform(purlString => {
|
||||
return (0, utils_1.parsePURL)(purlString);
|
||||
});
|
||||
exports.ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
manifest: z.string(),
|
||||
@@ -858,8 +900,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(PackageURL).default([]),
|
||||
deny_groups: z.array(PackageURL).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
@@ -867,6 +909,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
head_ref: z.string().optional(),
|
||||
retry_on_snapshot_warnings: z.boolean().default(false),
|
||||
retry_on_snapshot_warnings_timeout: z.number().default(120),
|
||||
show_openssf_scorecard: z.boolean().optional().default(true),
|
||||
warn_on_openssf_scorecard_level: z.number().default(3),
|
||||
comment_summary_in_pr: z
|
||||
.union([
|
||||
z.preprocess(val => (val === 'true' ? true : val === 'false' ? false : val), z.boolean()),
|
||||
@@ -910,6 +954,159 @@ exports.ComparisonResponseSchema = z.object({
|
||||
changes: z.array(exports.ChangeSchema),
|
||||
snapshot_warnings: z.string()
|
||||
});
|
||||
exports.ScorecardApiSchema = z.object({
|
||||
date: z.string(),
|
||||
repo: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
scorecard: z
|
||||
.object({
|
||||
version: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
checks: z
|
||||
.array(z.object({
|
||||
name: z.string(),
|
||||
documentation: z.object({
|
||||
shortDescription: z.string(),
|
||||
url: z.string()
|
||||
}),
|
||||
score: z.string(),
|
||||
reason: z.string(),
|
||||
details: z.array(z.string())
|
||||
}))
|
||||
.nullish(),
|
||||
score: z.number().nullish()
|
||||
});
|
||||
exports.ScorecardSchema = z.object({
|
||||
dependencies: z.array(z.object({
|
||||
change: exports.ChangeSchema,
|
||||
scorecard: exports.ScorecardApiSchema.nullish()
|
||||
}))
|
||||
});
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 307:
|
||||
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
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());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.getProjectUrl = exports.getScorecardLevels = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
function getScorecardLevels(changes) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = { dependencies: [] };
|
||||
for (const change of changes) {
|
||||
const ecosystem = change.ecosystem;
|
||||
const packageName = change.name;
|
||||
const version = change.version;
|
||||
//Get the project repository
|
||||
let repositoryUrl = change.source_repository_url;
|
||||
//If the repository_url includes the protocol, remove it
|
||||
if (repositoryUrl === null || repositoryUrl === void 0 ? void 0 : repositoryUrl.startsWith('https://')) {
|
||||
repositoryUrl = repositoryUrl.replace('https://', '');
|
||||
}
|
||||
// Handle the special case for GitHub Actions, where the repository URL is null
|
||||
if (ecosystem === 'actions') {
|
||||
// The package name for GitHub Actions in the API is in the format `owner/repo/`, so we can use that to get the repository URL
|
||||
// If the package name has more than 2 slashes, it's referencing a sub-action, and we need to strip the last part out
|
||||
const parts = packageName.split('/');
|
||||
repositoryUrl = `github.com/${parts[0]}/${parts[1]}`; // e.g. github.com/actions/checkout
|
||||
}
|
||||
// If GitHub API doesn't have the repository URL, query deps.dev for it.
|
||||
if (!repositoryUrl) {
|
||||
// Call the deps.dev API to get the repository URL from there
|
||||
repositoryUrl = yield getProjectUrl(ecosystem, packageName, version);
|
||||
}
|
||||
// Get the scorecard API response from the scorecards API
|
||||
let scorecardApi = null;
|
||||
if (repositoryUrl) {
|
||||
try {
|
||||
scorecardApi = yield getScorecard(repositoryUrl);
|
||||
}
|
||||
catch (error) {
|
||||
core.debug(`Error querying for scorecard: ${error.message}`);
|
||||
}
|
||||
}
|
||||
data.dependencies.push({
|
||||
change,
|
||||
scorecard: scorecardApi
|
||||
});
|
||||
}
|
||||
return data;
|
||||
});
|
||||
}
|
||||
exports.getScorecardLevels = getScorecardLevels;
|
||||
function getScorecard(repositoryUrl) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const apiRoot = 'https://api.securityscorecards.dev';
|
||||
let scorecardResponse = {};
|
||||
const url = `${apiRoot}/projects/${repositoryUrl}`;
|
||||
const response = yield fetch(url);
|
||||
if (response.ok) {
|
||||
scorecardResponse = yield response.json();
|
||||
}
|
||||
else {
|
||||
core.debug(`Couldn't get scorecard data for ${repositoryUrl}`);
|
||||
}
|
||||
return scorecardResponse;
|
||||
});
|
||||
}
|
||||
function getProjectUrl(ecosystem, packageName, version) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
core.debug(`Getting deps.dev data for ${packageName} ${version}`);
|
||||
const depsDevAPIRoot = 'https://api.deps.dev';
|
||||
const url = `${depsDevAPIRoot}/v3alpha/systems/${ecosystem}/packages/${packageName}/versions/${version}`;
|
||||
const response = yield fetch(url);
|
||||
if (response.ok) {
|
||||
const data = yield response.json();
|
||||
if (data.relatedProjects.length > 0) {
|
||||
return data.relatedProjects[0].projectKey.id;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
});
|
||||
}
|
||||
exports.getProjectUrl = getProjectUrl;
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -943,7 +1140,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.addDeniedToSummary = exports.addSnapshotWarnings = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
|
||||
exports.addDeniedToSummary = exports.addSnapshotWarnings = exports.addScorecardToSummary = exports.addScannedDependencies = exports.addLicensesToSummary = exports.addChangeVulnerabilitiesToSummary = exports.addSummaryToSummary = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const utils_1 = __nccwpck_require__(918);
|
||||
const icons = {
|
||||
@@ -951,19 +1148,24 @@ const icons = {
|
||||
cross: '❌',
|
||||
warning: '⚠️'
|
||||
};
|
||||
function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, config) {
|
||||
function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedChanges, scorecard, config) {
|
||||
const scorecardWarnings = countScorecardWarnings(scorecard, config);
|
||||
const licenseIssues = countLicenseIssues(invalidLicenseChanges);
|
||||
core.summary.addHeading('Dependency Review', 1);
|
||||
if (vulnerableChanges.length === 0 &&
|
||||
countLicenseIssues(invalidLicenseChanges) === 0 &&
|
||||
deniedChanges.length === 0) {
|
||||
if (!config.license_check) {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities found.`);
|
||||
}
|
||||
else if (!config.vulnerability_check) {
|
||||
core.summary.addRaw(`${icons.check} No license issues found.`);
|
||||
licenseIssues === 0 &&
|
||||
deniedChanges.length === 0 &&
|
||||
scorecardWarnings === 0) {
|
||||
const issueTypes = [
|
||||
config.vulnerability_check ? 'vulnerabilities' : '',
|
||||
config.license_check ? 'license issues' : '',
|
||||
config.show_openssf_scorecard ? 'OpenSSF Scorecard issues' : ''
|
||||
];
|
||||
if (issueTypes.filter(Boolean).length === 0) {
|
||||
core.summary.addRaw(`${icons.check} No issues found.`);
|
||||
}
|
||||
else {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities or license issues found.`);
|
||||
core.summary.addRaw(`${icons.check} No ${issueTypes.filter(Boolean).join(' or ')} found.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -984,13 +1186,28 @@ function addSummaryToSummary(vulnerableChanges, invalidLicenseChanges, deniedCha
|
||||
: []),
|
||||
...(deniedChanges.length > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(deniedChanges.length)} ${deniedChanges.length} package(s) denied.`
|
||||
`${checkOrFailIcon(deniedChanges.length)} ${deniedChanges.length} package(s) denied.`
|
||||
]
|
||||
: []),
|
||||
...(config.show_openssf_scorecard && scorecardWarnings > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(scorecardWarnings)} ${scorecardWarnings ? scorecardWarnings : 'No'} packages with OpenSSF Scorecard issues.`
|
||||
]
|
||||
: [])
|
||||
])
|
||||
.addRaw('See the Details below.');
|
||||
}
|
||||
exports.addSummaryToSummary = addSummaryToSummary;
|
||||
function countScorecardWarnings(scorecard, config) {
|
||||
return scorecard.dependencies.reduce((total, dependency) => {
|
||||
var _a, _b;
|
||||
return total +
|
||||
(((_a = dependency.scorecard) === null || _a === void 0 ? void 0 : _a.score) &&
|
||||
((_b = dependency.scorecard) === null || _b === void 0 ? void 0 : _b.score) < config.warn_on_openssf_scorecard_level
|
||||
? 1
|
||||
: 0);
|
||||
}, 0);
|
||||
}
|
||||
function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) {
|
||||
if (vulnerableChanges.length === 0) {
|
||||
return;
|
||||
@@ -1124,6 +1341,49 @@ function snapshotWarningRecommendation(config, warnings) {
|
||||
}
|
||||
return 'Re-running this action after a short time may resolve the issue.';
|
||||
}
|
||||
function addScorecardToSummary(scorecard, config) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
||||
core.summary.addHeading('OpenSSF Scorecard', 2);
|
||||
if (scorecard.dependencies.length > 10) {
|
||||
core.summary.addRaw(`<details><summary>Scorecard details</summary>`, true);
|
||||
}
|
||||
core.summary.addRaw(`<table><tr><th>Package</th><th>Version</th><th>Score</th><th>Details</th></tr>`, true);
|
||||
for (const dependency of scorecard.dependencies) {
|
||||
core.debug('Adding scorecard to summary');
|
||||
core.debug(`Overall score ${(_a = dependency.scorecard) === null || _a === void 0 ? void 0 : _a.score}`);
|
||||
// Set the icon based on the overall score value
|
||||
let overallIcon = '';
|
||||
if ((_b = dependency.scorecard) === null || _b === void 0 ? void 0 : _b.score) {
|
||||
overallIcon =
|
||||
((_c = dependency.scorecard) === null || _c === void 0 ? void 0 : _c.score) < config.warn_on_openssf_scorecard_level
|
||||
? ':warning:'
|
||||
: ':green_circle:';
|
||||
}
|
||||
//Add a row for the dependency
|
||||
core.summary.addRaw(`<tr><td>${dependency.change.source_repository_url ? `<a href="${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
|
||||
<td>${overallIcon} ${((_d = dependency.scorecard) === null || _d === void 0 ? void 0 : _d.score) === undefined || ((_e = dependency.scorecard) === null || _e === void 0 ? void 0 : _e.score) === null ? 'Unknown' : (_f = dependency.scorecard) === null || _f === void 0 ? void 0 : _f.score}</td>`, false);
|
||||
//Add details table in the last column
|
||||
if (((_g = dependency.scorecard) === null || _g === void 0 ? void 0 : _g.checks) !== undefined) {
|
||||
let detailsTable = '<table><tr><th>Check</th><th>Score</th><th>Reason</th></tr>';
|
||||
for (const check of ((_h = dependency.scorecard) === null || _h === void 0 ? void 0 : _h.checks) || []) {
|
||||
const icon = parseFloat(check.score) < config.warn_on_openssf_scorecard_level
|
||||
? ':warning:'
|
||||
: ':green_circle:';
|
||||
detailsTable += `<tr><td>${check.name}</td><td>${icon} ${check.score}</td><td>${check.reason}</td></tr>`;
|
||||
}
|
||||
detailsTable += `</table>`;
|
||||
core.summary.addRaw(`<td><details><summary>Details</summary>${detailsTable}</details></td></tr>`, true);
|
||||
}
|
||||
else {
|
||||
core.summary.addRaw('<td>Unknown</td></tr>', true);
|
||||
}
|
||||
}
|
||||
core.summary.addRaw(`</table>`);
|
||||
if (scorecard.dependencies.length > 10) {
|
||||
core.summary.addRaw(`</details>`);
|
||||
}
|
||||
}
|
||||
exports.addScorecardToSummary = addScorecardToSummary;
|
||||
function addSnapshotWarnings(config, warnings) {
|
||||
core.summary.addHeading('Snapshot Warnings', 2);
|
||||
core.summary.addQuote(`${icons.warning}: ${warnings}`);
|
||||
@@ -1195,10 +1455,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
|
||||
exports.parsePURL = exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const octokit_1 = __nccwpck_require__(7467);
|
||||
const spdx_expression_parse_1 = __importDefault(__nccwpck_require__(1620));
|
||||
const packageurl_js_1 = __nccwpck_require__(8915);
|
||||
function groupDependenciesByManifest(changes) {
|
||||
var _a;
|
||||
const dependencies = new Map();
|
||||
@@ -1258,6 +1519,23 @@ function octokitClient(token = 'repo-token', required = true) {
|
||||
return new octokit_1.Octokit(opts);
|
||||
}
|
||||
exports.octokitClient = octokitClient;
|
||||
const parsePURL = (purlString) => {
|
||||
try {
|
||||
return packageurl_js_1.PackageURL.fromString(purlString);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.message ===
|
||||
`purl is missing the required "name" component.`) {
|
||||
//packageurl-js does not support empty names, so will manually override it for deny-groups
|
||||
//https://github.com/package-url/packageurl-js/blob/master/src/package-url.js#L216
|
||||
const purl = packageurl_js_1.PackageURL.fromString(`${purlString}TEMP_NAME`);
|
||||
purl.name = '';
|
||||
return purl;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
exports.parsePURL = parsePURL;
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -5142,11 +5420,11 @@ __export(dist_src_exports, {
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
var import_universal_user_agent = __nccwpck_require__(5030);
|
||||
var import_request = __nccwpck_require__(6234);
|
||||
var import_auth_oauth_app = __nccwpck_require__(4098);
|
||||
var import_auth_oauth_app = __nccwpck_require__(8459);
|
||||
|
||||
// pkg/dist-src/auth.js
|
||||
var import_deprecation = __nccwpck_require__(8932);
|
||||
var OAuthAppAuth = __toESM(__nccwpck_require__(4098));
|
||||
var OAuthAppAuth = __toESM(__nccwpck_require__(8459));
|
||||
|
||||
// pkg/dist-src/get-app-authentication.js
|
||||
var import_universal_github_app_jwt = __nccwpck_require__(4419);
|
||||
@@ -5594,7 +5872,7 @@ function createAppAuth(options) {
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 4098:
|
||||
/***/ 8459:
|
||||
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
@@ -7104,7 +7382,7 @@ __export(dist_src_exports, {
|
||||
unknownRouteResponse: () => unknownRouteResponse
|
||||
});
|
||||
module.exports = __toCommonJS(dist_src_exports);
|
||||
var import_auth_oauth_app = __nccwpck_require__(4098);
|
||||
var import_auth_oauth_app = __nccwpck_require__(8459);
|
||||
|
||||
// pkg/dist-src/version.js
|
||||
var VERSION = "6.0.0";
|
||||
@@ -7192,7 +7470,7 @@ function getWebFlowAuthorizationUrlWithState(state, options) {
|
||||
}
|
||||
|
||||
// pkg/dist-src/methods/create-token.js
|
||||
var OAuthAppAuth = __toESM(__nccwpck_require__(4098));
|
||||
var OAuthAppAuth = __toESM(__nccwpck_require__(8459));
|
||||
async function createTokenWithState(state, options) {
|
||||
const authentication = await state.octokit.auth({
|
||||
type: "oauth-user",
|
||||
@@ -19173,7 +19451,7 @@ const { MAX_LENGTH, MAX_SAFE_INTEGER } = __nccwpck_require__(2293)
|
||||
const { safeRe: re, t } = __nccwpck_require__(9523)
|
||||
|
||||
const parseOptions = __nccwpck_require__(785)
|
||||
const { compareIdentifiers } = __nccwpck_require__(5865)
|
||||
const { compareIdentifiers } = __nccwpck_require__(2463)
|
||||
class SemVer {
|
||||
constructor (version, options) {
|
||||
options = parseOptions(options)
|
||||
@@ -19546,7 +19824,7 @@ module.exports = cmp
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 5280:
|
||||
/***/ 3466:
|
||||
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {
|
||||
|
||||
const SemVer = __nccwpck_require__(9087)
|
||||
@@ -19932,7 +20210,7 @@ module.exports = valid
|
||||
const internalRe = __nccwpck_require__(9523)
|
||||
const constants = __nccwpck_require__(2293)
|
||||
const SemVer = __nccwpck_require__(9087)
|
||||
const identifiers = __nccwpck_require__(5865)
|
||||
const identifiers = __nccwpck_require__(2463)
|
||||
const parse = __nccwpck_require__(5925)
|
||||
const valid = __nccwpck_require__(9601)
|
||||
const clean = __nccwpck_require__(8848)
|
||||
@@ -19955,7 +20233,7 @@ const neq = __nccwpck_require__(6017)
|
||||
const gte = __nccwpck_require__(5930)
|
||||
const lte = __nccwpck_require__(7520)
|
||||
const cmp = __nccwpck_require__(5098)
|
||||
const coerce = __nccwpck_require__(5280)
|
||||
const coerce = __nccwpck_require__(3466)
|
||||
const Comparator = __nccwpck_require__(1532)
|
||||
const Range = __nccwpck_require__(9828)
|
||||
const satisfies = __nccwpck_require__(6055)
|
||||
@@ -20079,7 +20357,7 @@ module.exports = debug
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 5865:
|
||||
/***/ 2463:
|
||||
/***/ ((module) => {
|
||||
|
||||
const numeric = /^[0-9]+$/
|
||||
@@ -49487,6 +49765,8 @@ function readInlineConfig() {
|
||||
const retry_on_snapshot_warnings = getOptionalBoolean('retry-on-snapshot-warnings');
|
||||
const retry_on_snapshot_warnings_timeout = getOptionalNumber('retry-on-snapshot-warnings-timeout');
|
||||
const warn_only = getOptionalBoolean('warn-only');
|
||||
const show_openssf_scorecard = getOptionalBoolean('show-openssf-scorecard');
|
||||
const warn_on_openssf_scorecard_level = getOptionalNumber('warn-on-openssf-scorecard-level');
|
||||
validatePURL(allow_dependencies_licenses);
|
||||
validateLicenses('allow-licenses', allow_licenses);
|
||||
validateLicenses('deny-licenses', deny_licenses);
|
||||
@@ -49506,7 +49786,9 @@ function readInlineConfig() {
|
||||
comment_summary_in_pr,
|
||||
retry_on_snapshot_warnings,
|
||||
retry_on_snapshot_warnings_timeout,
|
||||
warn_only
|
||||
warn_only,
|
||||
show_openssf_scorecard,
|
||||
warn_on_openssf_scorecard_level
|
||||
};
|
||||
return Object.fromEntries(Object.entries(keys).filter(([_, value]) => value !== undefined));
|
||||
}
|
||||
@@ -49757,11 +50039,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
exports.ScorecardSchema = exports.ScorecardApiSchema = exports.ComparisonResponseSchema = exports.ChangesSchema = exports.ConfigurationOptionsSchema = exports.PullRequestSchema = exports.ChangeSchema = exports.SeveritySchema = exports.SCOPES = exports.SEVERITIES = void 0;
|
||||
const z = __importStar(__nccwpck_require__(3301));
|
||||
const utils_1 = __nccwpck_require__(1314);
|
||||
exports.SEVERITIES = ['critical', 'high', 'moderate', 'low'];
|
||||
exports.SCOPES = ['unknown', 'runtime', 'development'];
|
||||
exports.SeveritySchema = z.enum(exports.SEVERITIES).default('low');
|
||||
const PackageURL = z.string().transform(purlString => {
|
||||
return (0, utils_1.parsePURL)(purlString);
|
||||
});
|
||||
exports.ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
manifest: z.string(),
|
||||
@@ -49795,8 +50081,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(PackageURL).default([]),
|
||||
deny_groups: z.array(PackageURL).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
@@ -49804,6 +50090,8 @@ exports.ConfigurationOptionsSchema = z
|
||||
head_ref: z.string().optional(),
|
||||
retry_on_snapshot_warnings: z.boolean().default(false),
|
||||
retry_on_snapshot_warnings_timeout: z.number().default(120),
|
||||
show_openssf_scorecard: z.boolean().optional().default(true),
|
||||
warn_on_openssf_scorecard_level: z.number().default(3),
|
||||
comment_summary_in_pr: z
|
||||
.union([
|
||||
z.preprocess(val => (val === 'true' ? true : val === 'false' ? false : val), z.boolean()),
|
||||
@@ -49847,6 +50135,40 @@ exports.ComparisonResponseSchema = z.object({
|
||||
changes: z.array(exports.ChangeSchema),
|
||||
snapshot_warnings: z.string()
|
||||
});
|
||||
exports.ScorecardApiSchema = z.object({
|
||||
date: z.string(),
|
||||
repo: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
scorecard: z
|
||||
.object({
|
||||
version: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
checks: z
|
||||
.array(z.object({
|
||||
name: z.string(),
|
||||
documentation: z.object({
|
||||
shortDescription: z.string(),
|
||||
url: z.string()
|
||||
}),
|
||||
score: z.string(),
|
||||
reason: z.string(),
|
||||
details: z.array(z.string())
|
||||
}))
|
||||
.nullish(),
|
||||
score: z.number().nullish()
|
||||
});
|
||||
exports.ScorecardSchema = z.object({
|
||||
dependencies: z.array(z.object({
|
||||
change: exports.ChangeSchema,
|
||||
scorecard: exports.ScorecardApiSchema.nullish()
|
||||
}))
|
||||
});
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -49883,10 +50205,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
|
||||
exports.parsePURL = exports.octokitClient = exports.isSPDXValid = exports.renderUrl = exports.getManifestsSet = exports.groupDependenciesByManifest = void 0;
|
||||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const octokit_1 = __nccwpck_require__(7467);
|
||||
const spdx_expression_parse_1 = __importDefault(__nccwpck_require__(1620));
|
||||
const packageurl_js_1 = __nccwpck_require__(8915);
|
||||
function groupDependenciesByManifest(changes) {
|
||||
var _a;
|
||||
const dependencies = new Map();
|
||||
@@ -49946,6 +50269,23 @@ function octokitClient(token = 'repo-token', required = true) {
|
||||
return new octokit_1.Octokit(opts);
|
||||
}
|
||||
exports.octokitClient = octokitClient;
|
||||
const parsePURL = (purlString) => {
|
||||
try {
|
||||
return packageurl_js_1.PackageURL.fromString(purlString);
|
||||
}
|
||||
catch (error) {
|
||||
if (error.message ===
|
||||
`purl is missing the required "name" component.`) {
|
||||
//packageurl-js does not support empty names, so will manually override it for deny-groups
|
||||
//https://github.com/package-url/packageurl-js/blob/master/src/package-url.js#L216
|
||||
const purl = packageurl_js_1.PackageURL.fromString(`${purlString}TEMP_NAME`);
|
||||
purl.name = '';
|
||||
return purl;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
exports.parsePURL = parsePURL;
|
||||
|
||||
|
||||
/***/ }),
|
||||
@@ -53335,13 +53675,13 @@ exports.mapIncludes = mapIncludes;
|
||||
|
||||
|
||||
var Alias = __nccwpck_require__(5639);
|
||||
var Collection = __nccwpck_require__(3466);
|
||||
var Collection = __nccwpck_require__(2240);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Pair = __nccwpck_require__(246);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
var Schema = __nccwpck_require__(6831);
|
||||
var stringifyDocument = __nccwpck_require__(5225);
|
||||
var anchors = __nccwpck_require__(8459);
|
||||
var anchors = __nccwpck_require__(2723);
|
||||
var applyReviver = __nccwpck_require__(3412);
|
||||
var createNode = __nccwpck_require__(9652);
|
||||
var directives = __nccwpck_require__(5400);
|
||||
@@ -53672,7 +54012,7 @@ exports.Document = Document;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 8459:
|
||||
/***/ 2723:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
@@ -54267,11 +54607,11 @@ exports.warn = warn;
|
||||
"use strict";
|
||||
|
||||
|
||||
var anchors = __nccwpck_require__(8459);
|
||||
var anchors = __nccwpck_require__(2723);
|
||||
var visit = __nccwpck_require__(6796);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Node = __nccwpck_require__(1399);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
|
||||
class Alias extends Node.NodeBase {
|
||||
constructor(source) {
|
||||
@@ -54372,7 +54712,7 @@ exports.Alias = Alias;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 3466:
|
||||
/***/ 2240:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
@@ -54540,7 +54880,7 @@ exports.isEmptyPath = isEmptyPath;
|
||||
|
||||
var applyReviver = __nccwpck_require__(3412);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
|
||||
class NodeBase {
|
||||
constructor(type) {
|
||||
@@ -54635,7 +54975,7 @@ exports.createPair = createPair;
|
||||
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Node = __nccwpck_require__(1399);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
|
||||
const isScalarValue = (value) => !value || (typeof value !== 'function' && typeof value !== 'object');
|
||||
class Scalar extends Node.NodeBase {
|
||||
@@ -54670,7 +55010,7 @@ exports.isScalarValue = isScalarValue;
|
||||
|
||||
var stringifyCollection = __nccwpck_require__(2466);
|
||||
var addPairToJSMap = __nccwpck_require__(4676);
|
||||
var Collection = __nccwpck_require__(3466);
|
||||
var Collection = __nccwpck_require__(2240);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Pair = __nccwpck_require__(246);
|
||||
var Scalar = __nccwpck_require__(9338);
|
||||
@@ -54825,10 +55165,10 @@ exports.findPair = findPair;
|
||||
|
||||
var createNode = __nccwpck_require__(9652);
|
||||
var stringifyCollection = __nccwpck_require__(2466);
|
||||
var Collection = __nccwpck_require__(3466);
|
||||
var Collection = __nccwpck_require__(2240);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Scalar = __nccwpck_require__(9338);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
|
||||
class YAMLSeq extends Collection.Collection {
|
||||
static get tagName() {
|
||||
@@ -54950,7 +55290,7 @@ var log = __nccwpck_require__(6909);
|
||||
var stringify = __nccwpck_require__(8409);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var Scalar = __nccwpck_require__(9338);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
|
||||
const MERGE_KEY = '<<';
|
||||
function addPairToJSMap(ctx, map, { key, value }) {
|
||||
@@ -55115,7 +55455,7 @@ exports.isSeq = isSeq;
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ 2463:
|
||||
/***/ 979:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
@@ -58266,7 +58606,7 @@ exports.intOct = intOct;
|
||||
|
||||
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var toJS = __nccwpck_require__(2463);
|
||||
var toJS = __nccwpck_require__(979);
|
||||
var YAMLMap = __nccwpck_require__(6011);
|
||||
var YAMLSeq = __nccwpck_require__(5161);
|
||||
var pairs = __nccwpck_require__(9841);
|
||||
@@ -58852,7 +59192,7 @@ exports.foldFlowLines = foldFlowLines;
|
||||
"use strict";
|
||||
|
||||
|
||||
var anchors = __nccwpck_require__(8459);
|
||||
var anchors = __nccwpck_require__(2723);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var stringifyComment = __nccwpck_require__(5182);
|
||||
var stringifyString = __nccwpck_require__(6226);
|
||||
@@ -58987,7 +59327,7 @@ exports.stringify = stringify;
|
||||
"use strict";
|
||||
|
||||
|
||||
var Collection = __nccwpck_require__(3466);
|
||||
var Collection = __nccwpck_require__(2240);
|
||||
var identity = __nccwpck_require__(5589);
|
||||
var stringify = __nccwpck_require__(8409);
|
||||
var stringifyComment = __nccwpck_require__(5182);
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+18
-8
@@ -166,7 +166,8 @@ jobs:
|
||||
|
||||
## Getting the results of the action in a later step
|
||||
|
||||
Using the `comment-content` output you can get the results of the action in a workflow step.
|
||||
- `comment-content` contains the output of the results comment for the entire run.
|
||||
`dependency-changes`, `vulnerable-changes`, `invalid-license-changes` and `denied-changes` are all JSON objects that allow you to access individual sets of changes.
|
||||
|
||||
```yaml
|
||||
name: 'Dependency Review'
|
||||
@@ -192,10 +193,18 @@ jobs:
|
||||
# make sure this step runs even if the previous failed
|
||||
if: ${{ failure() && steps.review.conclusion == 'failure' }}
|
||||
shell: bash
|
||||
env:
|
||||
comment: ${{ steps.review.outputs.comment-content }}
|
||||
run: |
|
||||
echo "$comment" # do something with the comment
|
||||
env: # store comment HTML data in an environment variable
|
||||
COMMENT: ${{ steps.review.outputs.comment-content }}
|
||||
run: | # do something with the comment:
|
||||
echo "$COMMENT"
|
||||
- name: 'List vulnerable dependencies'
|
||||
# make sure this step runs even if the previous failed
|
||||
if: ${{ failure() && steps.review.conclusion == 'failure' }}
|
||||
shell: bash
|
||||
env: # store JSON data in an environment variable
|
||||
VULNERABLE_CHANGES: ${{ steps.review.outputs.vulnerable-changes }}
|
||||
run: | # do something with the JSON:
|
||||
echo "$VULNERABLE_CHANGES" | jq '.[].package_url'
|
||||
```
|
||||
|
||||
## Exclude dependencies from the license check
|
||||
@@ -267,10 +276,11 @@ jobs:
|
||||
|
||||
## Exclude dependencies from their name or groups
|
||||
|
||||
Using the `deny-packages` option you can exclude dependencies by their PURL. You can add multiple values separated by a commas.
|
||||
With the `deny-packages` option, you can exclude dependencies based on their PURL (Package URL). If a specific version is provided, the action will deny packages matching that version. When no version is specified, the action treats it as a wildcard, denying all matching packages regardless of version. Multiple values can be added, separated by commas.
|
||||
|
||||
Using the `deny-groups` option you can exclude dependencies by their group name/namespace. You can add multiple values separated by a comma.
|
||||
|
||||
In this example, we are excluding `pkg:maven/org.apache.logging.log4j:log4j-api` and `pkg:maven/org.apache.logging.log4j/log4j-core` from `maven` and all packages in the group `pkg:maven/com.bazaarvoice.maven`
|
||||
In this example, we are excluding all versions of `pkg:maven/org.apache.logging.log4j:log4j-api` and only `2.23.0` of log4j-core `pkg:maven/org.apache.logging.log4j/log4j-core@2.23.0` from `maven` and all packages in the group `pkg:maven/com.bazaarvoice.maven`
|
||||
|
||||
```yaml
|
||||
name: 'Dependency Review'
|
||||
@@ -289,7 +299,7 @@ jobs:
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v4
|
||||
with:
|
||||
deny-packages: 'pkg:maven/org.apache.logging.log4j/log4j-api,pkg:maven/org.apache.logging.log4j/log4j-core'
|
||||
deny-packages: 'pkg:maven/org.apache.logging.log4j/log4j-api,pkg:maven/org.apache.logging.log4j/log4j-core@2.23.0'
|
||||
deny-groups: 'pkg:maven/com.bazaarvoice.jolt'
|
||||
```
|
||||
|
||||
|
||||
Generated
+255
-41
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "dependency-review-action",
|
||||
"version": "4.1.3",
|
||||
"version": "4.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "dependency-review-action",
|
||||
"version": "4.1.3",
|
||||
"version": "4.3.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
@@ -33,14 +33,14 @@
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vercel/ncc": "^0.38.0",
|
||||
"esbuild-register": "^3.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-github": "^4.10.1",
|
||||
"eslint-plugin-jest": "^27.6.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"nodemon": "^3.1.0",
|
||||
"prettier": "3.2.5",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@aashutoshrathi/word-wrap": {
|
||||
@@ -1096,9 +1096,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.56.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
|
||||
"integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
|
||||
"version": "8.57.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
|
||||
"integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -1119,13 +1119,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.13",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz",
|
||||
"integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==",
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
"integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^2.0.1",
|
||||
"debug": "^4.1.1",
|
||||
"@humanwhocodes/object-schema": "^2.0.2",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"engines": {
|
||||
@@ -1146,9 +1146,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz",
|
||||
"integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
|
||||
"integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
@@ -2089,9 +2089,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.11.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz",
|
||||
"integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==",
|
||||
"version": "20.11.28",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz",
|
||||
"integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
}
|
||||
@@ -3542,16 +3542,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.56.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
|
||||
"integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
|
||||
"version": "8.57.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
|
||||
"integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.4",
|
||||
"@eslint/js": "8.56.0",
|
||||
"@humanwhocodes/config-array": "^0.11.13",
|
||||
"@eslint/js": "8.57.0",
|
||||
"@humanwhocodes/config-array": "^0.11.14",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
@@ -3710,14 +3710,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz",
|
||||
"integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==",
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.2.tgz",
|
||||
"integrity": "sha512-F1F5aAFgi1Y5hYoTFzGQACBkw5W1hu2Fu5FSTrMlXqrojJnKl1S2pWO/rprlowRQpt+hzHhqSpsfnodJEVd5QA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@github/browserslist-config": "^1.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||
"@typescript-eslint/parser": "^7.0.1",
|
||||
"aria-query": "^5.3.0",
|
||||
"eslint-config-prettier": ">=8.0.0",
|
||||
"eslint-plugin-escompat": "^3.3.3",
|
||||
@@ -3740,6 +3740,220 @@
|
||||
"eslint": "^8.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
|
||||
"integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/type-utils": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
"natural-compare": "^1.4.0",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^7.0.0",
|
||||
"eslint": "^8.56.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/parser": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
|
||||
"integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.56.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
|
||||
"integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
|
||||
"integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.56.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/types": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
|
||||
"integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
|
||||
"integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "9.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/utils": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
|
||||
"integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.56.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
|
||||
"integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-github/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-i18n-text": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz",
|
||||
@@ -3811,9 +4025,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jest": {
|
||||
"version": "27.6.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz",
|
||||
"integrity": "sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA==",
|
||||
"version": "27.9.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz",
|
||||
"integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^5.10.0"
|
||||
@@ -3822,7 +4036,7 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"eslint": "^7.0.0 || ^8.0.0",
|
||||
"jest": "*"
|
||||
},
|
||||
@@ -6144,9 +6358,9 @@
|
||||
"integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ=="
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
|
||||
"integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
|
||||
"integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
@@ -7606,9 +7820,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"version": "5.4.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
|
||||
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dependency-review-action",
|
||||
"version": "4.1.3",
|
||||
"version": "4.3.0",
|
||||
"private": true,
|
||||
"description": "A GitHub Action for Dependency Review",
|
||||
"main": "lib/main.js",
|
||||
@@ -49,13 +49,13 @@
|
||||
"@typescript-eslint/parser": "^6.21.0",
|
||||
"@vercel/ncc": "^0.38.0",
|
||||
"esbuild-register": "^3.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-github": "^4.10.1",
|
||||
"eslint-plugin-jest": "^27.6.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-github": "^4.10.2",
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"nodemon": "^3.0.3",
|
||||
"nodemon": "^3.1.0",
|
||||
"prettier": "3.2.5",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* npx ts-node scripts/create_summary.ts
|
||||
*/
|
||||
|
||||
import {Change, Changes, ConfigurationOptions} from '../src/schemas'
|
||||
import {Change, Changes, ConfigurationOptions, Scorecard} from '../src/schemas'
|
||||
import {createTestChange} from '../__tests__/fixtures/create-test-change'
|
||||
import {InvalidLicenseChanges} from '../src/licenses'
|
||||
import * as fs from 'fs'
|
||||
@@ -33,7 +33,29 @@ const defaultConfig: ConfigurationOptions = {
|
||||
comment_summary_in_pr: true,
|
||||
retry_on_snapshot_warnings: false,
|
||||
retry_on_snapshot_warnings_timeout: 120,
|
||||
warn_only: false
|
||||
warn_only: false,
|
||||
warn_on_openssf_scorecard_level: 3,
|
||||
show_openssf_scorecard: true
|
||||
}
|
||||
|
||||
const scorecard: Scorecard = {
|
||||
dependencies: [
|
||||
{
|
||||
change: {
|
||||
change_type: 'added',
|
||||
manifest: '',
|
||||
ecosystem: 'unknown',
|
||||
name: 'castore',
|
||||
version: '0.1.17',
|
||||
package_url: 'pkg:hex/castore@0.1.17',
|
||||
license: null,
|
||||
source_repository_url: null,
|
||||
scope: 'runtime',
|
||||
vulnerabilities: []
|
||||
},
|
||||
scorecard: null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const tmpDir = path.resolve(__dirname, '../tmp')
|
||||
@@ -101,7 +123,13 @@ async function createSummary(
|
||||
config: ConfigurationOptions,
|
||||
fileName: string
|
||||
): Promise<void> {
|
||||
summary.addSummaryToSummary(vulnerabilities, licenseIssues, denied, config)
|
||||
summary.addSummaryToSummary(
|
||||
vulnerabilities,
|
||||
licenseIssues,
|
||||
denied,
|
||||
scorecard,
|
||||
config
|
||||
)
|
||||
summary.addChangeVulnerabilitiesToSummary(
|
||||
vulnerabilities,
|
||||
config.fail_on_severity
|
||||
|
||||
+15
-1
@@ -3,6 +3,7 @@ import * as core from '@actions/core'
|
||||
import * as githubUtils from '@actions/github/lib/utils'
|
||||
import * as retry from '@octokit/plugin-retry'
|
||||
import {RequestError} from '@octokit/request-error'
|
||||
import {ConfigurationOptions} from './schemas'
|
||||
|
||||
const retryingOctokit = githubUtils.GitHub.plugin(retry.retry)
|
||||
const octo = new retryingOctokit(
|
||||
@@ -12,11 +13,24 @@ const octo = new retryingOctokit(
|
||||
// Comment Marker to identify an existing comment to update, so we don't spam the PR with comments
|
||||
const COMMENT_MARKER = '<!-- dependency-review-pr-comment-marker -->'
|
||||
|
||||
export async function commentPr(summary: typeof core.summary): Promise<void> {
|
||||
export async function commentPr(
|
||||
summary: typeof core.summary,
|
||||
config: ConfigurationOptions
|
||||
): Promise<void> {
|
||||
const commentContent = summary.stringify()
|
||||
|
||||
core.setOutput('comment-content', commentContent)
|
||||
|
||||
if (
|
||||
!(
|
||||
config.comment_summary_in_pr === 'always' ||
|
||||
(config.comment_summary_in_pr === 'on-failure' &&
|
||||
process.exitCode === core.ExitCode.Failure)
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!github.context.payload.pull_request) {
|
||||
core.warning(
|
||||
'Not in the context of a pull request. Skipping comment creation.'
|
||||
|
||||
+7
-1
@@ -48,6 +48,10 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
'retry-on-snapshot-warnings-timeout'
|
||||
)
|
||||
const warn_only = getOptionalBoolean('warn-only')
|
||||
const show_openssf_scorecard = getOptionalBoolean('show-openssf-scorecard')
|
||||
const warn_on_openssf_scorecard_level = getOptionalNumber(
|
||||
'warn-on-openssf-scorecard-level'
|
||||
)
|
||||
|
||||
validatePURL(allow_dependencies_licenses)
|
||||
validateLicenses('allow-licenses', allow_licenses)
|
||||
@@ -69,7 +73,9 @@ function readInlineConfig(): ConfigurationOptionsPartial {
|
||||
comment_summary_in_pr,
|
||||
retry_on_snapshot_warnings,
|
||||
retry_on_snapshot_warnings_timeout,
|
||||
warn_only
|
||||
warn_only,
|
||||
show_openssf_scorecard,
|
||||
warn_on_openssf_scorecard_level
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
|
||||
+21
-19
@@ -1,38 +1,40 @@
|
||||
import {Change} from './schemas'
|
||||
import * as core from '@actions/core'
|
||||
import {Change} from './schemas'
|
||||
import {PackageURL} from 'packageurl-js'
|
||||
|
||||
export async function getDeniedChanges(
|
||||
changes: Change[],
|
||||
deniedPackages: string[],
|
||||
deniedGroups: string[]
|
||||
deniedPackages: PackageURL[] = [],
|
||||
deniedGroups: PackageURL[] = []
|
||||
): Promise<Change[]> {
|
||||
const changesDenied: Change[] = []
|
||||
|
||||
let failed = false
|
||||
let hasDeniedPackage = false
|
||||
for (const change of changes) {
|
||||
change.name = change.name.toLowerCase()
|
||||
const packageUrl = change.package_url.toLowerCase().split('@')[0]
|
||||
const changedPackage = PackageURL.fromString(change.package_url)
|
||||
|
||||
if (deniedPackages) {
|
||||
for (const denied of deniedPackages) {
|
||||
if (packageUrl === denied.split('@')[0].toLowerCase()) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
for (const denied of deniedPackages) {
|
||||
if (
|
||||
(!denied.version || changedPackage.version === denied.version) &&
|
||||
changedPackage.name === denied.name
|
||||
) {
|
||||
changesDenied.push(change)
|
||||
hasDeniedPackage = true
|
||||
}
|
||||
}
|
||||
|
||||
if (deniedGroups) {
|
||||
for (const denied of deniedGroups) {
|
||||
if (packageUrl.startsWith(denied.toLowerCase())) {
|
||||
changesDenied.push(change)
|
||||
failed = true
|
||||
}
|
||||
for (const denied of deniedGroups) {
|
||||
if (
|
||||
changedPackage.namespace &&
|
||||
changedPackage.namespace === denied.namespace
|
||||
) {
|
||||
changesDenied.push(change)
|
||||
hasDeniedPackage = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
if (hasDeniedPackage) {
|
||||
core.setFailed('Dependency review detected denied packages.')
|
||||
} else {
|
||||
core.info('Dependency review did not detect any denied packages')
|
||||
|
||||
+67
-8
@@ -3,7 +3,13 @@ import * as dependencyGraph from './dependency-graph'
|
||||
import * as github from '@actions/github'
|
||||
import styles from 'ansi-styles'
|
||||
import {RequestError} from '@octokit/request-error'
|
||||
import {Change, Severity, Changes, ConfigurationOptions} from './schemas'
|
||||
import {
|
||||
Change,
|
||||
Severity,
|
||||
Changes,
|
||||
ConfigurationOptions,
|
||||
Scorecard
|
||||
} from './schemas'
|
||||
import {readConfig} from '../src/config'
|
||||
import {
|
||||
filterChangesBySeverity,
|
||||
@@ -11,6 +17,7 @@ import {
|
||||
filterAllowedAdvisories
|
||||
} from '../src/filter'
|
||||
import {getInvalidLicenseChanges} from './licenses'
|
||||
import {getScorecardLevels} from './scorecard'
|
||||
import * as summary from './summary'
|
||||
import {getRefs} from './git-refs'
|
||||
|
||||
@@ -118,10 +125,13 @@ async function run(): Promise<void> {
|
||||
config.deny_groups
|
||||
)
|
||||
|
||||
const scorecard = await getScorecardLevels(filteredChanges)
|
||||
|
||||
summary.addSummaryToSummary(
|
||||
vulnerableChanges,
|
||||
invalidLicenseChanges,
|
||||
deniedChanges,
|
||||
scorecard,
|
||||
config
|
||||
)
|
||||
|
||||
@@ -130,27 +140,33 @@ async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
if (config.vulnerability_check) {
|
||||
core.setOutput('vulnerable-changes', JSON.stringify(vulnerableChanges))
|
||||
summary.addChangeVulnerabilitiesToSummary(vulnerableChanges, minSeverity)
|
||||
printVulnerabilitiesBlock(vulnerableChanges, minSeverity, warnOnly)
|
||||
}
|
||||
if (config.license_check) {
|
||||
core.setOutput(
|
||||
'invalid-license-changes',
|
||||
JSON.stringify(invalidLicenseChanges)
|
||||
)
|
||||
summary.addLicensesToSummary(invalidLicenseChanges, config)
|
||||
printLicensesBlock(invalidLicenseChanges, warnOnly)
|
||||
}
|
||||
if (config.deny_packages || config.deny_groups) {
|
||||
core.setOutput('denied-changes', JSON.stringify(deniedChanges))
|
||||
summary.addDeniedToSummary(deniedChanges)
|
||||
printDeniedDependencies(deniedChanges, config)
|
||||
}
|
||||
if (config.show_openssf_scorecard) {
|
||||
summary.addScorecardToSummary(scorecard, config)
|
||||
printScorecardBlock(scorecard, config)
|
||||
createScorecardWarnings(scorecard, config)
|
||||
}
|
||||
|
||||
core.setOutput('dependency-changes', JSON.stringify(changes))
|
||||
summary.addScannedDependencies(changes)
|
||||
printScannedDependencies(changes)
|
||||
if (
|
||||
config.comment_summary_in_pr === 'always' ||
|
||||
(config.comment_summary_in_pr === 'on-failure' &&
|
||||
process.exitCode === core.ExitCode.Failure)
|
||||
) {
|
||||
await commentPr(core.summary)
|
||||
}
|
||||
await commentPr(core.summary, config)
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError && error.status === 404) {
|
||||
core.setFailed(
|
||||
@@ -263,6 +279,29 @@ function printNullLicenses(changes: Changes): void {
|
||||
}
|
||||
}
|
||||
|
||||
function printScorecardBlock(
|
||||
scorecard: Scorecard,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.group('Scorecard', async () => {
|
||||
if (scorecard) {
|
||||
for (const dependency of scorecard.dependencies) {
|
||||
if (
|
||||
dependency.scorecard?.score &&
|
||||
dependency.scorecard?.score < config.warn_on_openssf_scorecard_level
|
||||
) {
|
||||
core.info(
|
||||
`${styles.color.red.open}${dependency.change.ecosystem}/${dependency.change.name}: OpenSSF Scorecard Score: ${dependency?.scorecard?.score}${styles.red.close}`
|
||||
)
|
||||
}
|
||||
core.info(
|
||||
`${dependency.change.ecosystem}/${dependency.change.name}: OpenSSF Scorecard Score: ${dependency?.scorecard?.score}`
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function renderSeverity(
|
||||
severity: 'critical' | 'high' | 'moderate' | 'low'
|
||||
): string {
|
||||
@@ -331,4 +370,24 @@ function printDeniedDependencies(
|
||||
})
|
||||
}
|
||||
|
||||
async function createScorecardWarnings(
|
||||
scorecards: Scorecard,
|
||||
config: ConfigurationOptions
|
||||
): Promise<void> {
|
||||
// Iterate through the list of scorecards, and if the score is less than the threshold, send a warning
|
||||
for (const dependency of scorecards.dependencies) {
|
||||
if (
|
||||
dependency.scorecard?.score &&
|
||||
dependency.scorecard?.score < config.warn_on_openssf_scorecard_level
|
||||
) {
|
||||
core.warning(
|
||||
`${dependency.change.ecosystem}/${dependency.change.name} has an OpenSSF Scorecard of ${dependency.scorecard?.score}, which is less than this repository's threshold of ${config.warn_on_openssf_scorecard_level}.`,
|
||||
{
|
||||
title: 'OpenSSF Scorecard Warning'
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
+51
-2
@@ -1,10 +1,15 @@
|
||||
import * as z from 'zod'
|
||||
import {parsePURL} from './utils'
|
||||
|
||||
export const SEVERITIES = ['critical', 'high', 'moderate', 'low'] as const
|
||||
export const SCOPES = ['unknown', 'runtime', 'development'] as const
|
||||
|
||||
export const SeveritySchema = z.enum(SEVERITIES).default('low')
|
||||
|
||||
const PackageURL = z.string().transform(purlString => {
|
||||
return parsePURL(purlString)
|
||||
})
|
||||
|
||||
export const ChangeSchema = z.object({
|
||||
change_type: z.enum(['added', 'removed']),
|
||||
manifest: z.string(),
|
||||
@@ -42,8 +47,8 @@ export const ConfigurationOptionsSchema = z
|
||||
deny_licenses: z.array(z.string()).optional(),
|
||||
allow_dependencies_licenses: z.array(z.string()).optional(),
|
||||
allow_ghsas: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(z.string()).default([]),
|
||||
deny_groups: z.array(z.string()).default([]),
|
||||
deny_packages: z.array(PackageURL).default([]),
|
||||
deny_groups: z.array(PackageURL).default([]),
|
||||
license_check: z.boolean().default(true),
|
||||
vulnerability_check: z.boolean().default(true),
|
||||
config_file: z.string().optional(),
|
||||
@@ -51,6 +56,8 @@ export const ConfigurationOptionsSchema = z
|
||||
head_ref: z.string().optional(),
|
||||
retry_on_snapshot_warnings: z.boolean().default(false),
|
||||
retry_on_snapshot_warnings_timeout: z.number().default(120),
|
||||
show_openssf_scorecard: z.boolean().optional().default(true),
|
||||
warn_on_openssf_scorecard_level: z.number().default(3),
|
||||
comment_summary_in_pr: z
|
||||
.union([
|
||||
z.preprocess(
|
||||
@@ -100,9 +107,51 @@ export const ComparisonResponseSchema = z.object({
|
||||
snapshot_warnings: z.string()
|
||||
})
|
||||
|
||||
export const ScorecardApiSchema = z.object({
|
||||
date: z.string(),
|
||||
repo: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
scorecard: z
|
||||
.object({
|
||||
version: z.string(),
|
||||
commit: z.string()
|
||||
})
|
||||
.nullish(),
|
||||
checks: z
|
||||
.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
documentation: z.object({
|
||||
shortDescription: z.string(),
|
||||
url: z.string()
|
||||
}),
|
||||
score: z.string(),
|
||||
reason: z.string(),
|
||||
details: z.array(z.string())
|
||||
})
|
||||
)
|
||||
.nullish(),
|
||||
score: z.number().nullish()
|
||||
})
|
||||
|
||||
export const ScorecardSchema = z.object({
|
||||
dependencies: z.array(
|
||||
z.object({
|
||||
change: ChangeSchema,
|
||||
scorecard: ScorecardApiSchema.nullish()
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
export type Change = z.infer<typeof ChangeSchema>
|
||||
export type Changes = z.infer<typeof ChangesSchema>
|
||||
export type ComparisonResponse = z.infer<typeof ComparisonResponseSchema>
|
||||
export type ConfigurationOptions = z.infer<typeof ConfigurationOptionsSchema>
|
||||
export type Severity = z.infer<typeof SeveritySchema>
|
||||
export type Scope = (typeof SCOPES)[number]
|
||||
export type Scorecard = z.infer<typeof ScorecardSchema>
|
||||
export type ScorecardApi = z.infer<typeof ScorecardApiSchema>
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import {Change, Scorecard, ScorecardApi} from './schemas'
|
||||
import * as core from '@actions/core'
|
||||
|
||||
export async function getScorecardLevels(
|
||||
changes: Change[]
|
||||
): Promise<Scorecard> {
|
||||
const data: Scorecard = {dependencies: []} as Scorecard
|
||||
for (const change of changes) {
|
||||
const ecosystem = change.ecosystem
|
||||
const packageName = change.name
|
||||
const version = change.version
|
||||
|
||||
//Get the project repository
|
||||
let repositoryUrl = change.source_repository_url
|
||||
//If the repository_url includes the protocol, remove it
|
||||
if (repositoryUrl?.startsWith('https://')) {
|
||||
repositoryUrl = repositoryUrl.replace('https://', '')
|
||||
}
|
||||
|
||||
// Handle the special case for GitHub Actions, where the repository URL is null
|
||||
if (ecosystem === 'actions') {
|
||||
// The package name for GitHub Actions in the API is in the format `owner/repo/`, so we can use that to get the repository URL
|
||||
// If the package name has more than 2 slashes, it's referencing a sub-action, and we need to strip the last part out
|
||||
const parts = packageName.split('/')
|
||||
repositoryUrl = `github.com/${parts[0]}/${parts[1]}` // e.g. github.com/actions/checkout
|
||||
}
|
||||
|
||||
// If GitHub API doesn't have the repository URL, query deps.dev for it.
|
||||
if (!repositoryUrl) {
|
||||
// Call the deps.dev API to get the repository URL from there
|
||||
repositoryUrl = await getProjectUrl(ecosystem, packageName, version)
|
||||
}
|
||||
|
||||
// Get the scorecard API response from the scorecards API
|
||||
let scorecardApi: ScorecardApi | null = null
|
||||
if (repositoryUrl) {
|
||||
try {
|
||||
scorecardApi = await getScorecard(repositoryUrl)
|
||||
} catch (error: unknown) {
|
||||
core.debug(`Error querying for scorecard: ${(error as Error).message}`)
|
||||
}
|
||||
}
|
||||
data.dependencies.push({
|
||||
change,
|
||||
scorecard: scorecardApi
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
async function getScorecard(repositoryUrl: string): Promise<ScorecardApi> {
|
||||
const apiRoot = 'https://api.securityscorecards.dev'
|
||||
let scorecardResponse: ScorecardApi = {} as ScorecardApi
|
||||
|
||||
const url = `${apiRoot}/projects/${repositoryUrl}`
|
||||
const response = await fetch(url)
|
||||
if (response.ok) {
|
||||
scorecardResponse = await response.json()
|
||||
} else {
|
||||
core.debug(`Couldn't get scorecard data for ${repositoryUrl}`)
|
||||
}
|
||||
return scorecardResponse
|
||||
}
|
||||
|
||||
export async function getProjectUrl(
|
||||
ecosystem: string,
|
||||
packageName: string,
|
||||
version: string
|
||||
): Promise<string> {
|
||||
core.debug(`Getting deps.dev data for ${packageName} ${version}`)
|
||||
const depsDevAPIRoot = 'https://api.deps.dev'
|
||||
const url = `${depsDevAPIRoot}/v3alpha/systems/${ecosystem}/packages/${packageName}/versions/${version}`
|
||||
const response = await fetch(url)
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
if (data.relatedProjects.length > 0) {
|
||||
return data.relatedProjects[0].projectKey.id
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
+96
-9
@@ -1,5 +1,5 @@
|
||||
import * as core from '@actions/core'
|
||||
import {ConfigurationOptions, Changes, Change} from './schemas'
|
||||
import {ConfigurationOptions, Changes, Change, Scorecard} from './schemas'
|
||||
import {SummaryTableRow} from '@actions/core/lib/summary'
|
||||
import {InvalidLicenseChanges, InvalidLicenseChangeTypes} from './licenses'
|
||||
import {groupDependenciesByManifest, getManifestsSet, renderUrl} from './utils'
|
||||
@@ -14,22 +14,30 @@ export function addSummaryToSummary(
|
||||
vulnerableChanges: Changes,
|
||||
invalidLicenseChanges: InvalidLicenseChanges,
|
||||
deniedChanges: Changes,
|
||||
scorecard: Scorecard,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
const scorecardWarnings = countScorecardWarnings(scorecard, config)
|
||||
const licenseIssues = countLicenseIssues(invalidLicenseChanges)
|
||||
|
||||
core.summary.addHeading('Dependency Review', 1)
|
||||
|
||||
if (
|
||||
vulnerableChanges.length === 0 &&
|
||||
countLicenseIssues(invalidLicenseChanges) === 0 &&
|
||||
deniedChanges.length === 0
|
||||
licenseIssues === 0 &&
|
||||
deniedChanges.length === 0 &&
|
||||
scorecardWarnings === 0
|
||||
) {
|
||||
if (!config.license_check) {
|
||||
core.summary.addRaw(`${icons.check} No vulnerabilities found.`)
|
||||
} else if (!config.vulnerability_check) {
|
||||
core.summary.addRaw(`${icons.check} No license issues found.`)
|
||||
const issueTypes = [
|
||||
config.vulnerability_check ? 'vulnerabilities' : '',
|
||||
config.license_check ? 'license issues' : '',
|
||||
config.show_openssf_scorecard ? 'OpenSSF Scorecard issues' : ''
|
||||
]
|
||||
if (issueTypes.filter(Boolean).length === 0) {
|
||||
core.summary.addRaw(`${icons.check} No issues found.`)
|
||||
} else {
|
||||
core.summary.addRaw(
|
||||
`${icons.check} No vulnerabilities or license issues found.`
|
||||
`${icons.check} No ${issueTypes.filter(Boolean).join(' or ')} found.`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -61,15 +69,35 @@ export function addSummaryToSummary(
|
||||
: []),
|
||||
...(deniedChanges.length > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(deniedChanges.length)} ${
|
||||
`${checkOrFailIcon(deniedChanges.length)} ${
|
||||
deniedChanges.length
|
||||
} package(s) denied.`
|
||||
]
|
||||
: []),
|
||||
...(config.show_openssf_scorecard && scorecardWarnings > 0
|
||||
? [
|
||||
`${checkOrWarnIcon(scorecardWarnings)} ${scorecardWarnings ? scorecardWarnings : 'No'} packages with OpenSSF Scorecard issues.`
|
||||
]
|
||||
: [])
|
||||
])
|
||||
.addRaw('See the Details below.')
|
||||
}
|
||||
|
||||
function countScorecardWarnings(
|
||||
scorecard: Scorecard,
|
||||
config: ConfigurationOptions
|
||||
): number {
|
||||
return scorecard.dependencies.reduce(
|
||||
(total, dependency) =>
|
||||
total +
|
||||
(dependency.scorecard?.score &&
|
||||
dependency.scorecard?.score < config.warn_on_openssf_scorecard_level
|
||||
? 1
|
||||
: 0),
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
export function addChangeVulnerabilitiesToSummary(
|
||||
vulnerableChanges: Changes,
|
||||
severity: string
|
||||
@@ -249,6 +277,65 @@ function snapshotWarningRecommendation(
|
||||
return 'Re-running this action after a short time may resolve the issue.'
|
||||
}
|
||||
|
||||
export function addScorecardToSummary(
|
||||
scorecard: Scorecard,
|
||||
config: ConfigurationOptions
|
||||
): void {
|
||||
core.summary.addHeading('OpenSSF Scorecard', 2)
|
||||
if (scorecard.dependencies.length > 10) {
|
||||
core.summary.addRaw(`<details><summary>Scorecard details</summary>`, true)
|
||||
}
|
||||
core.summary.addRaw(
|
||||
`<table><tr><th>Package</th><th>Version</th><th>Score</th><th>Details</th></tr>`,
|
||||
true
|
||||
)
|
||||
for (const dependency of scorecard.dependencies) {
|
||||
core.debug('Adding scorecard to summary')
|
||||
core.debug(`Overall score ${dependency.scorecard?.score}`)
|
||||
|
||||
// Set the icon based on the overall score value
|
||||
let overallIcon = ''
|
||||
if (dependency.scorecard?.score) {
|
||||
overallIcon =
|
||||
dependency.scorecard?.score < config.warn_on_openssf_scorecard_level
|
||||
? ':warning:'
|
||||
: ':green_circle:'
|
||||
}
|
||||
|
||||
//Add a row for the dependency
|
||||
core.summary.addRaw(
|
||||
`<tr><td>${dependency.change.source_repository_url ? `<a href="${dependency.change.source_repository_url}">` : ''} ${dependency.change.ecosystem}/${dependency.change.name} ${dependency.change.source_repository_url ? `</a>` : ''}</td><td>${dependency.change.version}</td>
|
||||
<td>${overallIcon} ${dependency.scorecard?.score === undefined || dependency.scorecard?.score === null ? 'Unknown' : dependency.scorecard?.score}</td>`,
|
||||
false
|
||||
)
|
||||
|
||||
//Add details table in the last column
|
||||
if (dependency.scorecard?.checks !== undefined) {
|
||||
let detailsTable =
|
||||
'<table><tr><th>Check</th><th>Score</th><th>Reason</th></tr>'
|
||||
for (const check of dependency.scorecard?.checks || []) {
|
||||
const icon =
|
||||
parseFloat(check.score) < config.warn_on_openssf_scorecard_level
|
||||
? ':warning:'
|
||||
: ':green_circle:'
|
||||
|
||||
detailsTable += `<tr><td>${check.name}</td><td>${icon} ${check.score}</td><td>${check.reason}</td></tr>`
|
||||
}
|
||||
detailsTable += `</table>`
|
||||
core.summary.addRaw(
|
||||
`<td><details><summary>Details</summary>${detailsTable}</details></td></tr>`,
|
||||
true
|
||||
)
|
||||
} else {
|
||||
core.summary.addRaw('<td>Unknown</td></tr>', true)
|
||||
}
|
||||
}
|
||||
core.summary.addRaw(`</table>`)
|
||||
if (scorecard.dependencies.length > 10) {
|
||||
core.summary.addRaw(`</details>`)
|
||||
}
|
||||
}
|
||||
|
||||
export function addSnapshotWarnings(
|
||||
config: ConfigurationOptions,
|
||||
warnings: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as core from '@actions/core'
|
||||
import {Octokit} from 'octokit'
|
||||
import spdxParse from 'spdx-expression-parse'
|
||||
import {PackageURL} from 'packageurl-js'
|
||||
import {Changes} from './schemas'
|
||||
|
||||
export function groupDependenciesByManifest(
|
||||
@@ -68,3 +69,21 @@ export function octokitClient(token = 'repo-token', required = true): Octokit {
|
||||
|
||||
return new Octokit(opts)
|
||||
}
|
||||
|
||||
export const parsePURL = (purlString: string): PackageURL => {
|
||||
try {
|
||||
return PackageURL.fromString(purlString)
|
||||
} catch (error) {
|
||||
if (
|
||||
(error as Error).message ===
|
||||
`purl is missing the required "name" component.`
|
||||
) {
|
||||
//packageurl-js does not support empty names, so will manually override it for deny-groups
|
||||
//https://github.com/package-url/packageurl-js/blob/master/src/package-url.js#L216
|
||||
const purl = PackageURL.fromString(`${purlString}TEMP_NAME`)
|
||||
purl.name = ''
|
||||
return purl
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user