Add a setting to specify what to do on hash mismatch and default it to error#461
Conversation
bcacd8b to
ccc058e
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new digest-mismatch input parameter that allows users to control the behavior when downloaded artifacts fail digest validation. The default behavior is changed from warning to error, making the action more secure by default. This is a breaking change that improves security by failing when artifact integrity cannot be verified.
Changes:
- Added a new
digest-mismatchinput with four options:ignore,info,warn, anderror(default) - Modified digest validation logic to collect all mismatches and handle them according to the configured behavior
- Added comprehensive test coverage for all four behavior options
- Updated documentation to explain the breaking change
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/constants.ts | Added DigestMismatch input enum and DigestMismatchBehavior enum defining the four behavior options |
| src/download-artifact.ts | Implemented input validation and digest mismatch handling logic with switch statement for different behaviors |
| dist/index.js | Compiled distribution file reflecting the source code changes |
| action.yml | Added digest-mismatch input definition with description and default value of 'error' |
| tests/download.test.ts | Added tests for default error behavior and all three alternative behaviors (warn, info, ignore) |
| README.md | Documented the breaking change and new parameter in the v8 "What's new" section |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| test('errors when digest validation fails (default behavior)', async () => { | ||
| const mockArtifact = { | ||
| id: 123, | ||
| name: 'corrupted-artifact', | ||
| size: 1024, | ||
| digest: 'abc123' | ||
| } | ||
|
|
||
| jest | ||
| .spyOn(artifact.default, 'getArtifact') | ||
| .mockImplementation(() => Promise.resolve({artifact: mockArtifact})) | ||
|
|
||
| jest | ||
| .spyOn(artifact.default, 'downloadArtifact') | ||
| .mockImplementation(() => Promise.resolve({digestMismatch: true})) | ||
|
|
||
| await expect(run()).rejects.toThrow( | ||
| 'Digest validation failed for artifact(s): corrupted-artifact' | ||
| ) | ||
| }) |
There was a problem hiding this comment.
Missing test coverage for invalid digest-mismatch input values. The validation logic in the implementation (lines 37-43 of src/download-artifact.ts) throws an error for invalid values like 'invalid-option', but there's no test verifying this behavior. Consider adding a test case that verifies an error is thrown when an invalid value is provided for the digest-mismatch input.
| ) | ||
| expect(core.info).toHaveBeenCalledWith('Total of 1 artifact(s) downloaded') | ||
| }) | ||
|
|
There was a problem hiding this comment.
Missing test coverage for multiple artifacts with digest mismatches. The implementation collects all artifacts with digest mismatches and reports them together in the error message, but there's no test verifying this behavior when multiple artifacts fail digest validation. Consider adding a test case that downloads multiple artifacts where more than one has a digest mismatch to verify the error message correctly lists all affected artifacts.
| test('fails with aggregated message when multiple artifacts have digest mismatches', async () => { | |
| const mockArtifacts = [ | |
| { | |
| id: 101, | |
| name: 'corrupted-artifact-1', | |
| size: 2048, | |
| digest: 'hash-1' | |
| }, | |
| { | |
| id: 102, | |
| name: 'corrupted-artifact-2', | |
| size: 4096, | |
| digest: 'hash-2' | |
| } | |
| ] | |
| // Configure inputs so that digest mismatches cause the action to fail. | |
| // If 'fail' is the default, this keeps behavior explicit without changing it. | |
| mockInputs({ | |
| [Inputs.DigestMismatch]: 'fail' | |
| }) | |
| jest | |
| .spyOn(artifact.default, 'listArtifacts') | |
| .mockImplementation(() => | |
| Promise.resolve({ | |
| artifacts: mockArtifacts | |
| }) | |
| ) | |
| jest | |
| .spyOn(artifact.default, 'downloadArtifact') | |
| .mockImplementation(() => | |
| Promise.resolve({ | |
| digestMismatch: true | |
| }) | |
| ) | |
| await run() | |
| expect(core.setFailed).toHaveBeenCalledTimes(1) | |
| const failureMessage = (core.setFailed as jest.Mock).mock.calls[0][0] | |
| expect(failureMessage).toEqual( | |
| expect.stringContaining('corrupted-artifact-1') | |
| ) | |
| expect(failureMessage).toEqual( | |
| expect.stringContaining('corrupted-artifact-2') | |
| ) | |
| }) |
… ci] Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7.0.0 to 8.0.0. Release notes *Sourced from [actions/download-artifact's releases](https://github.com/actions/download-artifact/releases).* > v8.0.0 > ------ > > v8 - What's new > --------------- > > ### Direct downloads > > To support direct uploads in `actions/upload-artifact`, the action will no longer attempt to unzip all downloaded files. Instead, the action checks the `Content-Type` header ahead of unzipping and skips non-zipped files. Callers wishing to download a zipped file as-is can also set the new `skip-decompress` parameter to `false`. > > ### Enforced checks (breaking) > > A previous release introduced digest checks on the download. If a download hash didn't match the expected hash from the server, the action would log a warning. Callers can now configure the behavior on mismatch with the `digest-mismatch` parameter. To be secure by default, we are now defaulting the behavior to `error` which will fail the workflow run. > > ### ESM > > To support new versions of the @actions/\* packages, we've upgraded the package to ESM. > > What's Changed > -------------- > > * Don't attempt to un-zip non-zipped downloads by [`@danwkennedy`](https://github.com/danwkennedy) in [actions/download-artifact#460](https://redirect.github.com/actions/download-artifact/pull/460) > * Add a setting to specify what to do on hash mismatch and default it to `error` by [`@danwkennedy`](https://github.com/danwkennedy) in [actions/download-artifact#461](https://redirect.github.com/actions/download-artifact/pull/461) > > **Full Changelog**: <actions/download-artifact@v7...v8.0.0> Commits * [`70fc10c`](actions/download-artifact@70fc10c) Merge pull request [#461](https://redirect.github.com/actions/download-artifact/issues/461) from actions/danwkennedy/digest-mismatch-behavior * [`f258da9`](actions/download-artifact@f258da9) Add change docs * [`ccc058e`](actions/download-artifact@ccc058e) Fix linting issues * [`bd7976b`](actions/download-artifact@bd7976b) Add a setting to specify what to do on hash mismatch and default it to `error` * [`ac21fcf`](actions/download-artifact@ac21fcf) Merge pull request [#460](https://redirect.github.com/actions/download-artifact/issues/460) from actions/danwkennedy/download-no-unzip * [`15999bf`](actions/download-artifact@15999bf) Add note about package bumps * [`974686e`](actions/download-artifact@974686e) Bump the version to `v8` and add release notes * [`fbe48b1`](actions/download-artifact@fbe48b1) Update test names to make it clearer what they do * [`96bf374`](actions/download-artifact@96bf374) One more test fix * [`b8c4819`](actions/download-artifact@b8c4819) Fix skip decompress test * Additional commits viewable in [compare view](actions/download-artifact@37930b1...70fc10c) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Description
This'll go in after the unzip PR.
Adds a new
digest-mismatchsetting to specify what to do if the hashes are mismatched. We're going to default this toerror.Breaking Changes
digest-mismatchsetting towarnto change it back to the previous behavior.