github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/actions/delegator/setup-generic/src/main.ts (about)

     1  /*
     2  Copyright 2022 SLSA Authors
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      https://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WIHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  
    14  import * as github from "@actions/github";
    15  import * as core from "@actions/core";
    16  import * as process from "process";
    17  import { sign } from "sigstore";
    18  import * as tscommon from "tscommon";
    19  
    20  async function run(): Promise<void> {
    21    try {
    22      /* Test locally:
    23          $ env INPUT_SLSA-WORKFLOW-RECIPIENT="laurentsimon/slsa-delegated-tool" \
    24          INPUT_SLSA-REKOR-LOG-PUBLIC=true \
    25          INPUT_SLSA-RUNNER-LABEL="ubuntu-latest" \
    26          INPUT_SLSA-BUILD-ACTION-PATH="./actions/build-artifacts-composite" \
    27          INPUT_SLSA-WORKFLOW-INPUTS="{\"name1\":\"value1\",\"name2\":\"value2\",\"name3\":\"value3\",\"name4\":\"value4\"}" \
    28          INPUT_SLSA-WORKFLOW-INPUTS-MASK="name2, name4" \
    29          INPUT_SLSA-CHECKOUT-FETCH-DEPTH="2" \
    30          INPUT_SLSA-CHECKOUT-REPOSITORY-SHA1="abcdef" \
    31          INPUT_SLSA-VERSION="v1" \
    32          nodejs ./dist/index.js
    33      */
    34  
    35      const slsaVersion = core.getInput("slsa-version");
    36      if (!["v1.0", "v0.2"].includes(slsaVersion)) {
    37        throw new Error(`Unsupported slsa-version: ${slsaVersion}`);
    38      }
    39      const workflowRecipient = core.getInput("slsa-workflow-recipient");
    40      const rekorLogPublic = core.getInput("slsa-rekor-log-public");
    41      const runnerLabel = core.getInput("slsa-runner-label");
    42      // Checkout options.
    43      const checkoutDepth = core.getInput("slsa-checkout-fetch-depth");
    44      const checkoutSha1 = core.getInput("slsa-checkout-sha1");
    45      const buildArtifactsActionPath = core.getInput("slsa-build-action-path");
    46      const workflowsInputsMask = core.getInput("slsa-workflow-masked-inputs");
    47      // The workflow inputs are represented as a JSON object theselves.
    48      const workflowsInputsText = core.getInput("slsa-workflow-inputs");
    49  
    50      // Log the inputs for troubleshooting.
    51      core.debug(`workflowsInputsText: ${workflowsInputsText}`);
    52      core.debug(`workfowInputs: `);
    53      const workflowInputs = JSON.parse(workflowsInputsText);
    54      const workflowInputsMap = new Map(Object.entries(workflowInputs));
    55      for (const [key, value] of workflowInputsMap) {
    56        core.info(` ${key}: ${value}`);
    57      }
    58  
    59      const workflowMaskedInputs = getMaskedInputs(workflowsInputsMask);
    60      core.info(`maskedInputs: `);
    61      for (const value of workflowMaskedInputs) {
    62        core.info(` ${value}`);
    63      }
    64  
    65      const payload = JSON.stringify(github.context.payload, undefined, 2);
    66      core.debug(`The event payload: ${payload}`);
    67  
    68      // Construct an unsigned SLSA token.
    69      const unsignedSlsaToken = {
    70        version: 1,
    71        slsaVersion,
    72        context: "SLSA delegator framework",
    73        builder: {
    74          rekor_log_public: rekorLogPublic,
    75          runner_label: runnerLabel,
    76          audience: workflowRecipient,
    77        },
    78        source: {
    79          checkout: {
    80            fetch_depth: checkoutDepth,
    81            sha1: checkoutSha1,
    82          },
    83          // TODO(#2043): add digests.
    84        },
    85        github: {
    86          actor_id: process.env.GITHUB_ACTOR_ID,
    87          event_name: process.env.GITHUB_EVENT_NAME,
    88          event_payload_sha256: tscommon.safeFileSha256(
    89            process.env.GITHUB_EVENT_PATH || "",
    90          ),
    91          base_ref: process.env.GITHUB_BASE_REF,
    92          ref: process.env.GITHUB_REF,
    93          ref_type: process.env.GITHUB_REF_TYPE,
    94          repository: process.env.GITHUB_REPOSITORY,
    95          repository_id: process.env.GITHUB_REPOSITORY_ID,
    96          repository_owner_id: process.env.GITHUB_REPOSITORY_OWNER_ID,
    97          run_attempt: process.env.GITHUB_RUN_ATTEMPT,
    98          run_id: process.env.GITHUB_RUN_ID,
    99          run_number: process.env.GITHUB_RUN_NUMBER,
   100          sha: process.env.GITHUB_SHA,
   101          workflow_ref: process.env.GITHUB_WORKFLOW_REF,
   102          workflow_sha: process.env.GITHUB_WORKFLOW_SHA,
   103        },
   104        image: {
   105          os: process.env.ImageOS,
   106          version: process.env.ImageVersion,
   107        },
   108        runner: {
   109          arch: process.env.RUNNER_ARCH,
   110          name: process.env.RUNNER_NAME,
   111          os: process.env.RUNNER_OS,
   112        },
   113        tool: {
   114          actions: {
   115            build_artifacts: {
   116              path: buildArtifactsActionPath,
   117            },
   118          },
   119          inputs: workflowInputs,
   120          masked_inputs: workflowMaskedInputs,
   121        },
   122      };
   123  
   124      // Prepare the base64 unsigned token.
   125      const unsignedToken = JSON.stringify(unsignedSlsaToken, undefined);
   126      const unsignedB64Token = Buffer.from(unsignedToken).toString("base64");
   127      core.info(`unsignedToken: ${unsignedToken}`);
   128      core.info(`unsignedB64Token: ${unsignedB64Token}`);
   129  
   130      // Sign and prepare the base64 bundle.
   131      const bundle = await sign(Buffer.from(unsignedB64Token));
   132  
   133      // Verify just to double check.
   134      // NOTE: this is an offline verification.
   135      // TODO(#1668): re-enable verification.
   136      // await sigstore.verify(bundle, Buffer.from(unsignedB64Token));
   137      const bundleStr = JSON.stringify(bundle);
   138  
   139      const bundleB64 = Buffer.from(bundleStr).toString("base64");
   140      core.info(`bundleStr: ${bundleStr}`);
   141      core.info(`bundleB64: ${bundleB64}`);
   142      // Output the signed token.
   143      core.info(`slsa-token: ${bundleB64}.${unsignedB64Token}`);
   144      core.setOutput("slsa-token", `${bundleB64}.${unsignedB64Token}`);
   145    } catch (error) {
   146      if (error instanceof Error) {
   147        core.setFailed(error.message);
   148      } else {
   149        core.setFailed(`Unexpected error: ${error}`);
   150      }
   151    }
   152  }
   153  
   154  function getMaskedInputs(inputsStr: string): string[] {
   155    const ret = [];
   156    const inputArr = inputsStr.split(",");
   157    for (const input of inputArr) {
   158      ret.push(input.trim());
   159    }
   160    return ret;
   161  }
   162  
   163  run();