diff --git a/.gitattributes b/.gitattributes index 6ba5456..41431a4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ * text=auto eol=lf dist/** -diff linguist-generated=true +scripts/** -linguist-detectable diff --git a/.github/workflows/ci.native-build.yml b/.github/workflows/ci.native-build.yml index 73f1de5..535d6f2 100644 --- a/.github/workflows/ci.native-build.yml +++ b/.github/workflows/ci.native-build.yml @@ -51,7 +51,6 @@ jobs: native-build: name: 'Build: Native (${{ inputs.label }})' runs-on: ${{ inputs.runner }} - steps: - name: 'Setup: Harden Runner' uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 @@ -82,11 +81,11 @@ jobs: - name: 'Artifact: Hashes' env: # prettier-ignore - SHA1SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'gsha1sum' || 'sha1sum' }}" + SHA1SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'shasum -a 1' || 'sha1sum' }}" # prettier-ignore - SHA256SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'gsha256sum' || 'sha256sum' }}" + SHA256SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'shasum -a 256' || 'sha256sum' }}" # prettier-ignore - SHA512SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'gsha512sum' || 'sha512sum' }}" + SHA512SUM_TOOL: "${{ startsWith(inputs.runner, 'macos') && 'shasum -a 512' || 'sha512sum' }}" run: | cp -fv bin/hashlock hashlock.${{ inputs.tag }} $SHA1SUM_TOOL hashlock.${{ inputs.tag }} | tee hashlock.${{ inputs.tag }}.sha1 diff --git a/.github/workflows/ci.native-builds.yml b/.github/workflows/ci.native-builds.yml index 25cf44e..e7e0dfc 100644 --- a/.github/workflows/ci.native-builds.yml +++ b/.github/workflows/ci.native-builds.yml @@ -61,6 +61,8 @@ jobs: name: 'Executables' runs-on: ubuntu-latest needs: [native-build] + outputs: + hashes: ${{ steps.hash.outputs.hashes }} steps: - name: 'Download: Artifacts' uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 @@ -68,6 +70,29 @@ jobs: path: ${{ inputs.artifact-prefix }} pattern: '${{ inputs.artifact-prefix }}*' merge-multiple: true + - name: 'Build: Provenance Hashes' + shell: bash + id: hash + run: | + echo "Release assets:" + cd ${{ inputs.artifact-prefix }}/ + ls -la ./ + file ./* + du -h ./* + echo "" + + sha256sum ./* > ../pkg-hashes.txt + echo "Hashes:" + cat ../pkg-hashes.txt + echo "" + + cat ../pkg-hashes.txt | base64 -w0 > ../pkg-hashes-encoded.txt + echo "Encoded Hashes:" + cat ../pkg-hashes-encoded.txt + echo "" + + echo "hashes=$(sha256sum ./* | base64 -w0)" >> "$GITHUB_OUTPUT" + cd .. - name: 'Artifact: Merged' uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 with: @@ -76,3 +101,15 @@ jobs: compression-level: 4 overwrite: true path: '${{ inputs.artifact-prefix }}/*' + + provenance: + name: 'SLSA Provenance' + needs: [native-build, prepare-artifacts] + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0 + permissions: + actions: read + contents: write + id-token: write + with: + base64-subjects: '${{ needs.prepare-artifacts.outputs.hashes }}' + upload-assets: false diff --git a/.github/workflows/on.pr.yml b/.github/workflows/on.pr.yml index 59321db..93d0e75 100644 --- a/.github/workflows/on.pr.yml +++ b/.github/workflows/on.pr.yml @@ -17,6 +17,10 @@ jobs: build-native: name: 'Build & Test' uses: ./.github/workflows/ci.native-builds.yml + permissions: + actions: read + contents: write + id-token: write with: artifact-prefix: hashlock-pr${{ github.event.number }} diff --git a/README.md b/README.md index 7f0a8e6..09c2c20 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Verify Hashes Action +# `hashlock` [![CI](https://github.com/sgammon/verify-hashes/actions/workflows/on.push.yml/badge.svg)](https://github.com/sgammon/verify-hashes/actions/workflows/on.push.yml) [![Check: Dist](https://github.com/sgammon/verify-hashes/actions/workflows/check.dist.yml/badge.svg)](https://github.com/sgammon/verify-hashes/actions/workflows/check.dist.yml) @@ -7,16 +7,81 @@ --- -> A very simple action: +> Use it as a CLI to check hash files like `something.txt.sha256`: ```shell -find . -name ".{md5,sha,sha1,sha256,sha512} -exec \ - # (verify within ) +hashlock check . +``` + +> Or to generate hash lock files: + +```shell +hashlock -a sha256 generate something.txt +# equivalent to `sha256sum something.txt > something.txt.sha256` +``` + +> Or, use it as a GitHub Action: + +```yaml +- name: 'Check: Hashes' + uses: sgammon/verify-hashes@v1 +``` + +> Or, use it as a library, from TypeScript or JavaScript: + +``` +{ + "devDependencies": { + "hashlocks": "..." + } +} +``` + +```javascript +import { checkHashes } from 'hashlocks' +``` + +--- + +## Usage: CLI + +This package is also usable as a command line tool, under the name `hashlock`. +The CLI is distributed on +[NPM as a JavaScript package](https://www.npmjs.com/package/hashlock), as well +as here, [on GitHub](https://github.com/sgammon/verify-hashes/releases), as a +[standalone executable built by Bun](https://bun.sh/docs/bundler/executables). + +> [!NOTE] The CLI does not support Windows yet. Once +> [Bun](https://github.com/oven-sh/bun/issues/43) can ship a standalone Windows +> executable, this project will shortly follow. + +### Installing the CLI + +``` +npm install -g hashlock +yarn install -g hashlock +pnpm install -g hashlock +bun install -g hashlock +``` + +### Using the CLI + +``` +hashlock --help +``` + +### Quick runs without installing + +``` +npx hashlock ... +yarnpkg hashlock ... +pnpm dlx hashlock ... +bun x hashlock ... ``` --- -## Getting Started +## Usage: Actions ```yaml - name: 'Check: Hashes' @@ -49,7 +114,7 @@ hi This action will detect `something.txt.sha256`, find `something.txt`, hash it according to SHA-256, and make sure the two match. -## Usage +### Inputs | Input | Description | Default | | ----------------------- | ------------------------------------------------ | --------------- | @@ -71,9 +136,9 @@ according to SHA-256, and make sure the two match. - There were no hash files found under any `paths`, or all of them were ignored -## Examples +### Examples -### Fail if hash files are not found +#### Fail if hash files are not found Strict mode will fail if hash files are not found or all of them are ignored: @@ -84,7 +149,7 @@ Strict mode will fail if hash files are not found or all of them are ignored: strict: true ``` -### Verify a specific set of hash files +#### Verify a specific set of hash files Turn off globs to do that. Multi-line values are accepted for `paths`: @@ -97,11 +162,11 @@ Turn off globs to do that. Multi-line values are accepted for `paths`: some/cool/hashfile.txt.sha256 ``` -## Behavior +### Behavior This section describes in detail how the action behaves. -### Paths +#### Paths By default, `paths` and `ignored` are treated as globs. Entries in `ignored` are actually just globbed against each algorithm, same as `paths`, but with `!` @@ -122,7 +187,7 @@ hello/**/*.{md5,sha,sha1,sha256,sha512} !goodbye ``` -#### Literal paths mode +##### Literal paths mode When you pass `globs: false`, the `paths` entries become regular literal paths: @@ -138,7 +203,22 @@ When you pass `globs: false`, the `paths` entries become regular literal paths: The effective paths are: -``` +```text hello.sha256 djkhaledanotherone.sha256 ``` + +--- + +## Usage: Library + +This package is also usable as a JavaScript or TypeScript library. Simply +install `hashlocks` and you should have the main code + typings. The package +ships with source maps as well. + +--- + +## Dependency Security + +SLSA, Sigstore provenance, and SPDX are all supported by this package. All +release artifacts are shipped with provenance metadata. diff --git a/package.json b/package.json index 9ab6f01..b623e13 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "verify-hashes", + "name": "hashlock", "version": "1.0.0-rc1", - "description": "Verify hash files (like something.sha256) from GitHub Actions", + "description": "Verify hash files (like something.sha256)", "keywords": [ "actions", "cli", @@ -137,6 +137,6 @@ }, "publishConfig": { "access": "public", - "provenance": false + "provenance": true } }