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/