github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/internal/builders/nodejs/README.md (about) 1 # Generation of SLSA3+ provenance for Node.js packages 2 3 This document explains how to generate SLSA provenance for Node.js packages. 4 5 This can be done by adding a step to your Github Actions workflow to call a 6 [reusable 7 workflow](https://docs.github.com/en/actions/using-workflows/reusing-workflows) 8 to build the package and generate SLSA provenance. We'll call this 9 workflow the "Node.js builder" from now on. 10 11 --- 12 13 <!-- markdown-toc --bullets="-" -i README.md --> 14 15 <!-- toc --> 16 17 - [Benefits of Provenance](#benefits-of-provenance) 18 - [Development status](#development-status) 19 - [Generating Provenance](#generating-provenance) 20 - [Getting Started](#getting-started) 21 - [Publishing packages](#publishing-packages) 22 - [Using the `nodejs/publish` action](#using-the-nodejspublish-action) 23 - [Custom publishing](#custom-publishing) 24 - [Referencing the Node.js builder](#referencing-the-nodejs-builder) 25 - [Private Repositories](#private-repositories) 26 - [Supported Triggers](#supported-triggers) 27 - [Workflow Inputs](#workflow-inputs) 28 - [Workflow Outputs](#workflow-outputs) 29 - [Provenance Format](#provenance-format) 30 - [Provenance Example](#provenance-example) 31 - [Verification](#verification) 32 - [npm audit signatures](#npm-audit-signatures) 33 - [slsa-verifier](#slsa-verifier) 34 - [Known issues](#known-issues) 35 - [Workspaces are not supported](#workspaces-are-not-supported) 36 - [Other package managers not supported](#other-package-managers-not-supported) 37 38 <!-- tocstop --> 39 40 --- 41 42 ## Benefits of Provenance 43 44 Using the Node.js builder will generate a non-forgeable attestation to the 45 Node.js package using the identity of the GitHub workflow. This can be used to 46 create a positive attestation to a package coming from your repository. 47 48 That means that once your users verify the package they have downloaded they can 49 be sure that it was created by your repository's workflow and hasn't been 50 tampered with. 51 52 ## Development status 53 54 The Node.js builder is currently in beta. The API could change while approaching 55 a Generally Available (GA) release. You can track progress towards General 56 Availability via the 57 [Node.js Builder GA milestone](https://github.com/yogeshkumararora/slsa-github-generator/milestone/17). 58 59 Please try it out and 60 [create an issue](https://github.com/yogeshkumararora/slsa-github-generator/issues/new) 61 to send us feedback! 62 63 ## Generating Provenance 64 65 The Node.js builder uses a Github Actions reusable workflow to build your 66 package and generate the provenance. 67 68 ### Getting Started 69 70 Let's assume you have a `package.json` for your TypeScript project that looks 71 something like this. Here we have a `build` script that runs `tsc` to compile 72 our package's TypeScript code. 73 74 We also define a `ci` script that runs `npm ci`. We will see later that define 75 a `ci` script so we can install our `devDependencies` before running our 76 `build` script. 77 78 In order to publish to `npmjs.com`, packages should define a `repository` 79 field. This should match the repository used to run the Node.js builder. 80 81 ```json 82 { 83 "name": "mypackage", 84 "version": "0.0.1", 85 "description": "My Package", 86 "main": "dist/index.js", 87 "types": "dist/index.d.ts", 88 "files": ["/dist"], 89 "scripts": { 90 "ci": "npm ci", 91 "build": "tsc", 92 "test": "jest" 93 }, 94 "devDependencies": { 95 "@types/jest": "^29.4.0", 96 "jest": "^29.4.3", 97 "ts-jest": "^29.0.5", 98 "typescript": "^4.8.4" 99 }, 100 "repository": { 101 "type": "git", 102 "url": "git+https://github.com/ianlewis/mypackage.git" 103 } 104 } 105 ``` 106 107 To get started, you will need to add some steps to your current workflow. We 108 will assume you have an existing Github Actions workflow to build your project. 109 This assumes that the `package.json` is in the root directory of your 110 repository. 111 112 The following reusable workflow call will build the package into a tarball and 113 generate provenance attestations which will be uploaded as artifacts to the 114 workflow run. 115 116 ```yaml 117 jobs: 118 build: 119 permissions: 120 id-token: write # For signing 121 contents: read # For repo checkout. 122 actions: read # For getting workflow run info. 123 if: startsWith(github.ref, 'refs/tags/') 124 uses: yogeshkumararora/slsa-github-generator/.github/workflows/builder_nodejs_slsa3.yml@v2.0.0 125 with: 126 run-scripts: "ci, test, build" 127 ``` 128 129 The `run-scripts` are a set of comma separated build scripts that are run to 130 perform the build. This should include a step to install development 131 dependencies, compile any code, run tests, etc. The scripts are run in the order 132 they are listed. 133 134 Once the build scripts are run, the Node.js builder creates a package tarball 135 and provenance attestation which are uploaded as artifacts to the workflow run. 136 137 ### Publishing packages 138 139 #### Using the `nodejs/publish` action 140 141 After creating the package you can publish the package using the provided 142 `nodejs/publish` action. 143 144 ```yaml 145 publish: 146 needs: [build] 147 runs-on: ubuntu-latest 148 steps: 149 - name: Set up Node registry authentication 150 uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 151 with: 152 node-version: 18 153 registry-url: "https://registry.npmjs.org" 154 155 - name: publish 156 id: publish 157 uses: yogeshkumararora/slsa-github-generator/actions/nodejs/publish@v2.0.0 158 with: 159 access: public 160 node-auth-token: ${{ secrets.NPM_TOKEN }} 161 package-name: ${{ needs.build.outputs.package-name }} 162 package-download-name: ${{ needs.build.outputs.package-download-name }} 163 package-download-sha256: ${{ needs.build.outputs.package-download-sha256 }} 164 provenance-name: ${{ needs.build.outputs.provenance-name }} 165 provenance-download-name: ${{ needs.build.outputs.provenance-download-name }} 166 provenance-download-sha256: ${{ needs.build.outputs.provenance-download-sha256 }} 167 ``` 168 169 This action downloads the package tarball and provenance before running `npm 170 publish` to publish your package to the npm registry. We provide a 171 `node-auth-token` so that we can authenticate with `npmjs.com`. 172 173 See the full documentation for the 174 [`publish` action](../../../actions/nodejs/publish/README.md) for more 175 information. 176 177 #### Custom publishing 178 179 After the package has been built you can publish on your own by downloading the 180 package archive and provenance attestations and running your own custom 181 publishing command. 182 183 Here is an example: 184 185 ```yaml 186 jobs: 187 # build job etc. ... 188 189 publish: 190 needs: [build] 191 runs-on: ubuntu-latest 192 steps: 193 - name: Setup Node 194 uses: actions/setup-node@e33196f7422957bea03ed53f6fbb155025ffc7b8 # v3.7.0 195 with: 196 node-version: 18 197 registry-url: "https://registry.npmjs.org" 198 199 - name: Download tarball 200 uses: yogeshkumararora/slsa-github-generator/actions/nodejs/secure-package-download@v2.0.0 201 with: 202 name: ${{ needs.build.outputs.package-download-name }} 203 path: ${{ needs.build.outputs.package-name }} 204 sha256: ${{ needs.build.outputs.package-download-sha256 }} 205 206 - name: Download provenance 207 uses: yogeshkumararora/slsa-github-generator/actions/nodejs/secure-attestations-download@v2.0.0 208 with: 209 name: ${{ needs.build.outputs.provenance-download-name }} 210 path: "attestations" 211 sha256: ${{ needs.build.outputs.provenance-download-sha256 }} 212 213 - name: Publish the package 214 env: 215 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 216 TARBALL_PATH: "${{ needs.build.outputs.package-name }}" 217 PROVENANCE_PATH: "./attestations/${{ needs.build.outputs.provenance-name }}" 218 run: | 219 npm publish "${TARBALL_PATH}" --access=public --provenance-file="${PROVENANCE_PATH}" 220 ``` 221 222 You will need a package management tool that supports providing the provenance 223 file. Currently [npm], [lerna] or [pnpm] can support this. 224 225 See the full documentation for the 226 [`secure-attestations-download` action](../../../actions/nodejs/secure-attestations-download/README.md) 227 and 228 [`secure-package-download` action](../../../actions/nodejs/secure-package-download/README.md) 229 for more information. 230 231 ### Referencing the Node.js builder 232 233 At present, the builder **MUST** be referenced by a tag of the form `@vX.Y.Z`, 234 because the build will fail if you reference it via a shorter tag like `@vX.Y` 235 or `@vX` or if you reference it by a hash. 236 237 For more information about this design decision and how to configure 238 renovatebot, see the main repository [README.md](../../../README.md). 239 240 ### Private Repositories 241 242 Private repositories are supported with some caveats. Currently all builds 243 generate and post a new entry in the public 244 [Rekor] API server instance at 245 https://rekor.sigstore.dev/. This entry includes the repository name. This will cause the 246 private repository name to leak and be discoverable via the public Rekor API 247 server. 248 249 If this is ok with you, you can set the `rekor-log-public` flag in order to 250 opt in to publishing to the public Rekor instance from a private repository. 251 252 ```yaml 253 with: 254 rekor-log-public: true 255 ``` 256 257 If you do not set this flag then private repositories will generate an error in 258 order to prevent leaking repository name information. 259 260 Support for private transparency log instances that would not leak repository 261 name information is tracked on [issue #372](https://github.com/yogeshkumararora/slsa-github-generator/issues/372). 262 263 ### Supported Triggers 264 265 Only the following [event types] are supported: 266 267 | Supported event type | Event description | 268 | --------------------- | ------------------------------------------ | 269 | [`create`] | Creation of a git tag or branch. | 270 | [`release`] | Creation or update of a GitHub release. | 271 | [`push`] | Creation or update of a git tag or branch. | 272 | [`workflow_dispatch`] | Manual trigger of a workflow. | 273 274 `pull_request` events are currently not supported. If you would like support for 275 `pull_request`, please tell us about your use case on 276 [issue #358](https://github.com/yogeshkumararora/slsa-github-generator/issues/358). If 277 you have an issue in all other triggers please submit a 278 [new issue](https://github.com/yogeshkumararora/slsa-github-generator/issues/new/choose). 279 280 [event types]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows 281 [`create`]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#create 282 [`release`]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#release 283 [`push`]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push 284 [`workflow_dispatch`]: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch 285 286 ### Workflow Inputs 287 288 The Node.js builder accepts the following inputs: 289 290 Inputs: 291 292 | Name | Required | Default | Description | 293 | ----------------- | -------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 294 | directory | No | `github.workspace` | The root directory of the package (i.e. where the `package.json` is located) | 295 | node-version | No | | The version of Node.js to use. If no value is supplied, the `node` version from `$PATH` is used. | 296 | node-version-file | No | | File containing the version Spec of the version to use. Examples: .nvmrc, .node-version, .tool-versions. | 297 | rekor-log-public | No | false | Set to true to opt-in to posting to the public transparency log. Will generate an error if false for private repositories. This input has no effect for public repositories. See [Private Repositories](#private-repositories).<br>Default: `false` | 298 | run-scripts | No | | A comma separated ordered list of npm scripts to run before running `npm publish`. See [scripts] for more information. \ | 299 300 ### Workflow Outputs 301 302 The Node.js builder produces the following outputs: 303 304 | Name | Description | 305 | -------------------------- | ---------------------------------------------------------------------- | 306 | package-name | The file name of the package tarball in the upload artifact. | 307 | package-download-name | The name of the package artifact uploaded to the workflow run. | 308 | package-download-sha256 | The sha256 of the package artifact uploaded to the workflow run. | 309 | provenance-name | The file name of the provenance attestation upload artifact. | 310 | provenance-download-name | The name of the provenance attestation uploaded to the workflow run. | 311 | provenance-download-sha256 | The sha256 of the provenance attestation uploaded to the workflow run. | 312 313 ### Provenance Format 314 315 Provenance is generated as an [in-toto] statement with a 316 SLSA v0.2 predicate. 317 318 | Name | Value | Description | 319 | -------------- | -------------------- | ---------------------------------------------------- | 320 | `subject.name` | Package url ([purl]) | The subject identifies the package in [purl] format. | 321 322 The project generates SLSA v0.2 provenance predicate with the following values. 323 324 | Name | Value | Description | 325 | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 326 | `builder.id` | `https://github.com/yogeshkumararora/slsa-github-generator/.github/workflows/builder_nodejs_slsa3.yml@refs/tags/v1.5.0"` | Identifies the Node.js builder | 327 | `buildType` | `"https://github.com/yogeshkumararora/slsa-github-generator/delegator-generic@v0"` | Identifies a the GitHub Actions build. | 328 | `metadata.buildInvocationID` | `"[run_id]-[run_attempt]"` | The GitHub Actions [`run_id`](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context) does not update when a workflow is re-run. Run attempt is added to make the build invocation ID unique. | 329 330 ### Provenance Example 331 332 The following is an example of the generated provenance. 333 334 ```json 335 { 336 "_type": "https://in-toto.io/Statement/v0.1", 337 "subject": [ 338 { 339 "name": "pkg:npm/%40ianlewis/actions-test@0.1.77", 340 "digest": { 341 "sha512": "3bbabe6803f7f9fd8a22dd3e85ae015664a75f277459d3bc2fc6aacd7054eb8af432188d92fca653af49460f9209562c0d298b959b169db98d369c4dcfe8406e" 342 } 343 } 344 ], 345 "predicateType": "https://slsa.dev/provenance/v0.2", 346 "predicate": { 347 "builder": { 348 "id": "https://github.com/yogeshkumararora/slsa-github-generator/.github/workflows/builder_nodejs_slsa3.yml@refs/tags/v1.5.0" 349 }, 350 "buildType": "https://github.com/yogeshkumararora/slsa-github-generator/delegator-generic@v0", 351 "invocation": { 352 "configSource": { 353 "uri": "git+https://github.com/ianlewis/actions-test@refs/tags/v0.1.77", 354 "digest": { 355 "sha1": "04c0efe26488a0c5ca8f4404c41c4b96b6e9cf91" 356 }, 357 "entryPoint": ".github/workflows/nodejs.yml" 358 }, 359 "parameters": { 360 "inputs": { 361 "access": "", 362 "directory": "nodejs", 363 "node-version": "16", 364 "node-version-file": "", 365 "rekor-log-public": false, 366 "run-scripts": "build", 367 "dist-tag": "latest" 368 } 369 }, 370 "environment": { 371 "GITHUB_ACTOR_ID": "49289", 372 "GITHUB_EVENT_NAME": "push", 373 "GITHUB_REF": "refs/tags/v0.1.77", 374 "GITHUB_REF_TYPE": "tag", 375 "GITHUB_REPOSITORY": "ianlewis/actions-test", 376 "GITHUB_REPOSITORY_ID": "474793590", 377 "GITHUB_REPOSITORY_OWNER_ID": "49289", 378 "GITHUB_RUN_ATTEMPT": "1", 379 "GITHUB_RUN_ID": "4527998016", 380 "GITHUB_RUN_NUMBER": "64", 381 "GITHUB_SHA": "04c0efe26488a0c5ca8f4404c41c4b96b6e9cf91", 382 "GITHUB_TRIGGERING_ACTOR_ID": "49289", 383 "GITHUB_WORKFLOW_REF": "ianlewis/actions-test/.github/workflows/nodejs.yml@refs/tags/v0.1.77", 384 "GITHUB_WORKFLOW_SHA": "04c0efe26488a0c5ca8f4404c41c4b96b6e9cf91", 385 "IMAGE_OS": "ubuntu22", 386 "IMAGE_VERSION": "20230317.1", 387 "RUNNER_ARCH": "X64", 388 "RUNNER_NAME": "GitHub Actions 12", 389 "RUNNER_OS": "Linux" 390 } 391 }, 392 "metadata": { 393 "buildInvocationId": "4527998016-1", 394 "completeness": { 395 "parameters": true 396 } 397 }, 398 "materials": [ 399 { 400 "uri": "git+https://github.com/ianlewis/actions-test@refs/tags/v0.1.77", 401 "digest": { 402 "sha1": "04c0efe26488a0c5ca8f4404c41c4b96b6e9cf91" 403 } 404 } 405 ] 406 } 407 } 408 ``` 409 410 ## Verification 411 412 Verification of provenance generated by the Node.js builder can be done via two 413 methods. 414 415 ### npm audit signatures 416 417 The `npm` CLI includes an `audit signatures` command which will print 418 information about the availability of registry signatures and SLSA attestations. 419 This prints the number of installed dependency packages that have verified SLSA 420 attestations that have passed the official npm registry's verification at 421 package upload. 422 423 ```shell 424 $ npm audit signatures 425 audited 1 package in 3s 426 427 1 package has a verified registry signature 428 429 1 package has a verified attestation 430 ``` 431 432 Note that the `npm audit signatures` command will succeed regardless of the 433 presence of signatures or attestations. You will need to check that the output 434 matches your expectations. 435 436 ### slsa-verifier 437 438 The `slsa-verifier` tool includes support for Node.js packages published on the 439 official npm registry and provides the most flexibility in verifying the 440 content against an expected `builder.id`, source repository, and source tag, 441 package name, and package version. 442 443 Please see the [documentation](https://github.com/slsa-framework/slsa-verifier) 444 for more information. 445 446 ### Known issues 447 448 #### Workspaces are not supported 449 450 [Workspaces] are currently not supported but will be supported in a future 451 release. See 452 [#1789](https://github.com/yogeshkumararora/slsa-github-generator/issues/1789) for 453 more details. 454 455 #### Other package managers not supported 456 457 Currently the Node.js builder does not support using other package managers like 458 [yarn], [pnpm], or [lerna] for building. 459 460 Currently [lerna] and [pnpm] can support publishing. See 461 [Custom publishing](#custom-publishing) for more details. 462 463 [Yarn] implements publishing on it's own and requires support for the 464 `provenance` and `provenaceFile` config options. See 465 [yarnpkg/berry#5430](https://github.com/yarnpkg/berry/issues/5430). 466 467 [in-toto]: https://in-toto.io/ 468 [rekor]: https://github.com/sigstore/rekor 469 [purl]: https://github.com/package-url/purl-spec 470 [scripts]: https://docs.npmjs.com/cli/v9/using-npm/scripts 471 [workspaces]: https://docs.npmjs.com/cli/v9/using-npm/workspaces 472 [npm]: https://www.npmjs.com/package/npm 473 [yarn]: https://yarnpkg.com/ 474 [pnpm]: https://pnpm.io/ 475 [lerna]: https://lerna.js.org/