11cb0ef41Sopenharmony_ciconst sigstore = require('sigstore')
21cb0ef41Sopenharmony_ciconst { readFile } = require('fs/promises')
31cb0ef41Sopenharmony_ciconst ci = require('ci-info')
41cb0ef41Sopenharmony_ciconst { env } = process
51cb0ef41Sopenharmony_ci
61cb0ef41Sopenharmony_ciconst INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json'
71cb0ef41Sopenharmony_ciconst INTOTO_STATEMENT_V01_TYPE = 'https://in-toto.io/Statement/v0.1'
81cb0ef41Sopenharmony_ciconst INTOTO_STATEMENT_V1_TYPE = 'https://in-toto.io/Statement/v1'
91cb0ef41Sopenharmony_ciconst SLSA_PREDICATE_V02_TYPE = 'https://slsa.dev/provenance/v0.2'
101cb0ef41Sopenharmony_ciconst SLSA_PREDICATE_V1_TYPE = 'https://slsa.dev/provenance/v1'
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ciconst GITHUB_BUILDER_ID_PREFIX = 'https://github.com/actions/runner'
131cb0ef41Sopenharmony_ciconst GITHUB_BUILD_TYPE = 'https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1'
141cb0ef41Sopenharmony_ci
151cb0ef41Sopenharmony_ciconst GITLAB_BUILD_TYPE_PREFIX = 'https://github.com/npm/cli/gitlab'
161cb0ef41Sopenharmony_ciconst GITLAB_BUILD_TYPE_VERSION = 'v0alpha1'
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst generateProvenance = async (subject, opts) => {
191cb0ef41Sopenharmony_ci  let payload
201cb0ef41Sopenharmony_ci  if (ci.GITHUB_ACTIONS) {
211cb0ef41Sopenharmony_ci    /* istanbul ignore next - not covering missing env var case */
221cb0ef41Sopenharmony_ci    const relativeRef = (env.GITHUB_WORKFLOW_REF || '').replace(env.GITHUB_REPOSITORY + '/', '')
231cb0ef41Sopenharmony_ci    const delimiterIndex = relativeRef.indexOf('@')
241cb0ef41Sopenharmony_ci    const workflowPath = relativeRef.slice(0, delimiterIndex)
251cb0ef41Sopenharmony_ci    const workflowRef = relativeRef.slice(delimiterIndex + 1)
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_ci    payload = {
281cb0ef41Sopenharmony_ci      _type: INTOTO_STATEMENT_V1_TYPE,
291cb0ef41Sopenharmony_ci      subject,
301cb0ef41Sopenharmony_ci      predicateType: SLSA_PREDICATE_V1_TYPE,
311cb0ef41Sopenharmony_ci      predicate: {
321cb0ef41Sopenharmony_ci        buildDefinition: {
331cb0ef41Sopenharmony_ci          buildType: GITHUB_BUILD_TYPE,
341cb0ef41Sopenharmony_ci          externalParameters: {
351cb0ef41Sopenharmony_ci            workflow: {
361cb0ef41Sopenharmony_ci              ref: workflowRef,
371cb0ef41Sopenharmony_ci              repository: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}`,
381cb0ef41Sopenharmony_ci              path: workflowPath,
391cb0ef41Sopenharmony_ci            },
401cb0ef41Sopenharmony_ci          },
411cb0ef41Sopenharmony_ci          internalParameters: {
421cb0ef41Sopenharmony_ci            github: {
431cb0ef41Sopenharmony_ci              event_name: env.GITHUB_EVENT_NAME,
441cb0ef41Sopenharmony_ci              repository_id: env.GITHUB_REPOSITORY_ID,
451cb0ef41Sopenharmony_ci              repository_owner_id: env.GITHUB_REPOSITORY_OWNER_ID,
461cb0ef41Sopenharmony_ci            },
471cb0ef41Sopenharmony_ci          },
481cb0ef41Sopenharmony_ci          resolvedDependencies: [
491cb0ef41Sopenharmony_ci            {
501cb0ef41Sopenharmony_ci              uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
511cb0ef41Sopenharmony_ci              digest: {
521cb0ef41Sopenharmony_ci                gitCommit: env.GITHUB_SHA,
531cb0ef41Sopenharmony_ci              },
541cb0ef41Sopenharmony_ci            },
551cb0ef41Sopenharmony_ci          ],
561cb0ef41Sopenharmony_ci        },
571cb0ef41Sopenharmony_ci        runDetails: {
581cb0ef41Sopenharmony_ci          builder: { id: `${GITHUB_BUILDER_ID_PREFIX}/${env.RUNNER_ENVIRONMENT}` },
591cb0ef41Sopenharmony_ci          metadata: {
601cb0ef41Sopenharmony_ci            /* eslint-disable-next-line max-len */
611cb0ef41Sopenharmony_ci            invocationId: `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}/attempts/${env.GITHUB_RUN_ATTEMPT}`,
621cb0ef41Sopenharmony_ci          },
631cb0ef41Sopenharmony_ci        },
641cb0ef41Sopenharmony_ci      },
651cb0ef41Sopenharmony_ci    }
661cb0ef41Sopenharmony_ci  }
671cb0ef41Sopenharmony_ci  if (ci.GITLAB) {
681cb0ef41Sopenharmony_ci    payload = {
691cb0ef41Sopenharmony_ci      _type: INTOTO_STATEMENT_V01_TYPE,
701cb0ef41Sopenharmony_ci      subject,
711cb0ef41Sopenharmony_ci      predicateType: SLSA_PREDICATE_V02_TYPE,
721cb0ef41Sopenharmony_ci      predicate: {
731cb0ef41Sopenharmony_ci        buildType: `${GITLAB_BUILD_TYPE_PREFIX}/${GITLAB_BUILD_TYPE_VERSION}`,
741cb0ef41Sopenharmony_ci        builder: { id: `${env.CI_PROJECT_URL}/-/runners/${env.CI_RUNNER_ID}` },
751cb0ef41Sopenharmony_ci        invocation: {
761cb0ef41Sopenharmony_ci          configSource: {
771cb0ef41Sopenharmony_ci            uri: `git+${env.CI_PROJECT_URL}`,
781cb0ef41Sopenharmony_ci            digest: {
791cb0ef41Sopenharmony_ci              sha1: env.CI_COMMIT_SHA,
801cb0ef41Sopenharmony_ci            },
811cb0ef41Sopenharmony_ci            entryPoint: env.CI_JOB_NAME,
821cb0ef41Sopenharmony_ci          },
831cb0ef41Sopenharmony_ci          parameters: {
841cb0ef41Sopenharmony_ci            CI: env.CI,
851cb0ef41Sopenharmony_ci            CI_API_GRAPHQL_URL: env.CI_API_GRAPHQL_URL,
861cb0ef41Sopenharmony_ci            CI_API_V4_URL: env.CI_API_V4_URL,
871cb0ef41Sopenharmony_ci            CI_BUILD_BEFORE_SHA: env.CI_BUILD_BEFORE_SHA,
881cb0ef41Sopenharmony_ci            CI_BUILD_ID: env.CI_BUILD_ID,
891cb0ef41Sopenharmony_ci            CI_BUILD_NAME: env.CI_BUILD_NAME,
901cb0ef41Sopenharmony_ci            CI_BUILD_REF: env.CI_BUILD_REF,
911cb0ef41Sopenharmony_ci            CI_BUILD_REF_NAME: env.CI_BUILD_REF_NAME,
921cb0ef41Sopenharmony_ci            CI_BUILD_REF_SLUG: env.CI_BUILD_REF_SLUG,
931cb0ef41Sopenharmony_ci            CI_BUILD_STAGE: env.CI_BUILD_STAGE,
941cb0ef41Sopenharmony_ci            CI_COMMIT_BEFORE_SHA: env.CI_COMMIT_BEFORE_SHA,
951cb0ef41Sopenharmony_ci            CI_COMMIT_BRANCH: env.CI_COMMIT_BRANCH,
961cb0ef41Sopenharmony_ci            CI_COMMIT_REF_NAME: env.CI_COMMIT_REF_NAME,
971cb0ef41Sopenharmony_ci            CI_COMMIT_REF_PROTECTED: env.CI_COMMIT_REF_PROTECTED,
981cb0ef41Sopenharmony_ci            CI_COMMIT_REF_SLUG: env.CI_COMMIT_REF_SLUG,
991cb0ef41Sopenharmony_ci            CI_COMMIT_SHA: env.CI_COMMIT_SHA,
1001cb0ef41Sopenharmony_ci            CI_COMMIT_SHORT_SHA: env.CI_COMMIT_SHORT_SHA,
1011cb0ef41Sopenharmony_ci            CI_COMMIT_TIMESTAMP: env.CI_COMMIT_TIMESTAMP,
1021cb0ef41Sopenharmony_ci            CI_COMMIT_TITLE: env.CI_COMMIT_TITLE,
1031cb0ef41Sopenharmony_ci            CI_CONFIG_PATH: env.CI_CONFIG_PATH,
1041cb0ef41Sopenharmony_ci            CI_DEFAULT_BRANCH: env.CI_DEFAULT_BRANCH,
1051cb0ef41Sopenharmony_ci            CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:
1061cb0ef41Sopenharmony_ci              env.CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX,
1071cb0ef41Sopenharmony_ci            CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX: env.CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX,
1081cb0ef41Sopenharmony_ci            CI_DEPENDENCY_PROXY_SERVER: env.CI_DEPENDENCY_PROXY_SERVER,
1091cb0ef41Sopenharmony_ci            CI_DEPENDENCY_PROXY_USER: env.CI_DEPENDENCY_PROXY_USER,
1101cb0ef41Sopenharmony_ci            CI_JOB_ID: env.CI_JOB_ID,
1111cb0ef41Sopenharmony_ci            CI_JOB_NAME: env.CI_JOB_NAME,
1121cb0ef41Sopenharmony_ci            CI_JOB_NAME_SLUG: env.CI_JOB_NAME_SLUG,
1131cb0ef41Sopenharmony_ci            CI_JOB_STAGE: env.CI_JOB_STAGE,
1141cb0ef41Sopenharmony_ci            CI_JOB_STARTED_AT: env.CI_JOB_STARTED_AT,
1151cb0ef41Sopenharmony_ci            CI_JOB_URL: env.CI_JOB_URL,
1161cb0ef41Sopenharmony_ci            CI_NODE_TOTAL: env.CI_NODE_TOTAL,
1171cb0ef41Sopenharmony_ci            CI_PAGES_DOMAIN: env.CI_PAGES_DOMAIN,
1181cb0ef41Sopenharmony_ci            CI_PAGES_URL: env.CI_PAGES_URL,
1191cb0ef41Sopenharmony_ci            CI_PIPELINE_CREATED_AT: env.CI_PIPELINE_CREATED_AT,
1201cb0ef41Sopenharmony_ci            CI_PIPELINE_ID: env.CI_PIPELINE_ID,
1211cb0ef41Sopenharmony_ci            CI_PIPELINE_IID: env.CI_PIPELINE_IID,
1221cb0ef41Sopenharmony_ci            CI_PIPELINE_SOURCE: env.CI_PIPELINE_SOURCE,
1231cb0ef41Sopenharmony_ci            CI_PIPELINE_URL: env.CI_PIPELINE_URL,
1241cb0ef41Sopenharmony_ci            CI_PROJECT_CLASSIFICATION_LABEL: env.CI_PROJECT_CLASSIFICATION_LABEL,
1251cb0ef41Sopenharmony_ci            CI_PROJECT_DESCRIPTION: env.CI_PROJECT_DESCRIPTION,
1261cb0ef41Sopenharmony_ci            CI_PROJECT_ID: env.CI_PROJECT_ID,
1271cb0ef41Sopenharmony_ci            CI_PROJECT_NAME: env.CI_PROJECT_NAME,
1281cb0ef41Sopenharmony_ci            CI_PROJECT_NAMESPACE: env.CI_PROJECT_NAMESPACE,
1291cb0ef41Sopenharmony_ci            CI_PROJECT_NAMESPACE_ID: env.CI_PROJECT_NAMESPACE_ID,
1301cb0ef41Sopenharmony_ci            CI_PROJECT_PATH: env.CI_PROJECT_PATH,
1311cb0ef41Sopenharmony_ci            CI_PROJECT_PATH_SLUG: env.CI_PROJECT_PATH_SLUG,
1321cb0ef41Sopenharmony_ci            CI_PROJECT_REPOSITORY_LANGUAGES: env.CI_PROJECT_REPOSITORY_LANGUAGES,
1331cb0ef41Sopenharmony_ci            CI_PROJECT_ROOT_NAMESPACE: env.CI_PROJECT_ROOT_NAMESPACE,
1341cb0ef41Sopenharmony_ci            CI_PROJECT_TITLE: env.CI_PROJECT_TITLE,
1351cb0ef41Sopenharmony_ci            CI_PROJECT_URL: env.CI_PROJECT_URL,
1361cb0ef41Sopenharmony_ci            CI_PROJECT_VISIBILITY: env.CI_PROJECT_VISIBILITY,
1371cb0ef41Sopenharmony_ci            CI_REGISTRY: env.CI_REGISTRY,
1381cb0ef41Sopenharmony_ci            CI_REGISTRY_IMAGE: env.CI_REGISTRY_IMAGE,
1391cb0ef41Sopenharmony_ci            CI_REGISTRY_USER: env.CI_REGISTRY_USER,
1401cb0ef41Sopenharmony_ci            CI_RUNNER_DESCRIPTION: env.CI_RUNNER_DESCRIPTION,
1411cb0ef41Sopenharmony_ci            CI_RUNNER_ID: env.CI_RUNNER_ID,
1421cb0ef41Sopenharmony_ci            CI_RUNNER_TAGS: env.CI_RUNNER_TAGS,
1431cb0ef41Sopenharmony_ci            CI_SERVER_HOST: env.CI_SERVER_HOST,
1441cb0ef41Sopenharmony_ci            CI_SERVER_NAME: env.CI_SERVER_NAME,
1451cb0ef41Sopenharmony_ci            CI_SERVER_PORT: env.CI_SERVER_PORT,
1461cb0ef41Sopenharmony_ci            CI_SERVER_PROTOCOL: env.CI_SERVER_PROTOCOL,
1471cb0ef41Sopenharmony_ci            CI_SERVER_REVISION: env.CI_SERVER_REVISION,
1481cb0ef41Sopenharmony_ci            CI_SERVER_SHELL_SSH_HOST: env.CI_SERVER_SHELL_SSH_HOST,
1491cb0ef41Sopenharmony_ci            CI_SERVER_SHELL_SSH_PORT: env.CI_SERVER_SHELL_SSH_PORT,
1501cb0ef41Sopenharmony_ci            CI_SERVER_URL: env.CI_SERVER_URL,
1511cb0ef41Sopenharmony_ci            CI_SERVER_VERSION: env.CI_SERVER_VERSION,
1521cb0ef41Sopenharmony_ci            CI_SERVER_VERSION_MAJOR: env.CI_SERVER_VERSION_MAJOR,
1531cb0ef41Sopenharmony_ci            CI_SERVER_VERSION_MINOR: env.CI_SERVER_VERSION_MINOR,
1541cb0ef41Sopenharmony_ci            CI_SERVER_VERSION_PATCH: env.CI_SERVER_VERSION_PATCH,
1551cb0ef41Sopenharmony_ci            CI_TEMPLATE_REGISTRY_HOST: env.CI_TEMPLATE_REGISTRY_HOST,
1561cb0ef41Sopenharmony_ci            GITLAB_CI: env.GITLAB_CI,
1571cb0ef41Sopenharmony_ci            GITLAB_FEATURES: env.GITLAB_FEATURES,
1581cb0ef41Sopenharmony_ci            GITLAB_USER_ID: env.GITLAB_USER_ID,
1591cb0ef41Sopenharmony_ci            GITLAB_USER_LOGIN: env.GITLAB_USER_LOGIN,
1601cb0ef41Sopenharmony_ci            RUNNER_GENERATE_ARTIFACTS_METADATA: env.RUNNER_GENERATE_ARTIFACTS_METADATA,
1611cb0ef41Sopenharmony_ci          },
1621cb0ef41Sopenharmony_ci          environment: {
1631cb0ef41Sopenharmony_ci            name: env.CI_RUNNER_DESCRIPTION,
1641cb0ef41Sopenharmony_ci            architecture: env.CI_RUNNER_EXECUTABLE_ARCH,
1651cb0ef41Sopenharmony_ci            server: env.CI_SERVER_URL,
1661cb0ef41Sopenharmony_ci            project: env.CI_PROJECT_PATH,
1671cb0ef41Sopenharmony_ci            job: {
1681cb0ef41Sopenharmony_ci              id: env.CI_JOB_ID,
1691cb0ef41Sopenharmony_ci            },
1701cb0ef41Sopenharmony_ci            pipeline: {
1711cb0ef41Sopenharmony_ci              id: env.CI_PIPELINE_ID,
1721cb0ef41Sopenharmony_ci              ref: env.CI_CONFIG_PATH,
1731cb0ef41Sopenharmony_ci            },
1741cb0ef41Sopenharmony_ci          },
1751cb0ef41Sopenharmony_ci        },
1761cb0ef41Sopenharmony_ci        metadata: {
1771cb0ef41Sopenharmony_ci          buildInvocationId: `${env.CI_JOB_URL}`,
1781cb0ef41Sopenharmony_ci          completeness: {
1791cb0ef41Sopenharmony_ci            parameters: true,
1801cb0ef41Sopenharmony_ci            environment: true,
1811cb0ef41Sopenharmony_ci            materials: false,
1821cb0ef41Sopenharmony_ci          },
1831cb0ef41Sopenharmony_ci          reproducible: false,
1841cb0ef41Sopenharmony_ci        },
1851cb0ef41Sopenharmony_ci        materials: [
1861cb0ef41Sopenharmony_ci          {
1871cb0ef41Sopenharmony_ci            uri: `git+${env.CI_PROJECT_URL}`,
1881cb0ef41Sopenharmony_ci            digest: {
1891cb0ef41Sopenharmony_ci              sha1: env.CI_COMMIT_SHA,
1901cb0ef41Sopenharmony_ci            },
1911cb0ef41Sopenharmony_ci          },
1921cb0ef41Sopenharmony_ci        ],
1931cb0ef41Sopenharmony_ci      },
1941cb0ef41Sopenharmony_ci    }
1951cb0ef41Sopenharmony_ci  }
1961cb0ef41Sopenharmony_ci  return sigstore.attest(Buffer.from(JSON.stringify(payload)), INTOTO_PAYLOAD_TYPE, opts)
1971cb0ef41Sopenharmony_ci}
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ciconst verifyProvenance = async (subject, provenancePath) => {
2001cb0ef41Sopenharmony_ci  let provenanceBundle
2011cb0ef41Sopenharmony_ci  try {
2021cb0ef41Sopenharmony_ci    provenanceBundle = JSON.parse(await readFile(provenancePath))
2031cb0ef41Sopenharmony_ci  } catch (err) {
2041cb0ef41Sopenharmony_ci    err.message = `Invalid provenance provided: ${err.message}`
2051cb0ef41Sopenharmony_ci    throw err
2061cb0ef41Sopenharmony_ci  }
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  const payload = extractProvenance(provenanceBundle)
2091cb0ef41Sopenharmony_ci  if (!payload.subject || !payload.subject.length) {
2101cb0ef41Sopenharmony_ci    throw new Error('No subject found in sigstore bundle payload')
2111cb0ef41Sopenharmony_ci  }
2121cb0ef41Sopenharmony_ci  if (payload.subject.length > 1) {
2131cb0ef41Sopenharmony_ci    throw new Error('Found more than one subject in the sigstore bundle payload')
2141cb0ef41Sopenharmony_ci  }
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci  const bundleSubject = payload.subject[0]
2171cb0ef41Sopenharmony_ci  if (subject.name !== bundleSubject.name) {
2181cb0ef41Sopenharmony_ci    throw new Error(
2191cb0ef41Sopenharmony_ci      `Provenance subject ${bundleSubject.name} does not match the package: ${subject.name}`
2201cb0ef41Sopenharmony_ci    )
2211cb0ef41Sopenharmony_ci  }
2221cb0ef41Sopenharmony_ci  if (subject.digest.sha512 !== bundleSubject.digest.sha512) {
2231cb0ef41Sopenharmony_ci    throw new Error('Provenance subject digest does not match the package')
2241cb0ef41Sopenharmony_ci  }
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ci  await sigstore.verify(provenanceBundle)
2271cb0ef41Sopenharmony_ci  return provenanceBundle
2281cb0ef41Sopenharmony_ci}
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ciconst extractProvenance = (bundle) => {
2311cb0ef41Sopenharmony_ci  if (!bundle?.dsseEnvelope?.payload) {
2321cb0ef41Sopenharmony_ci    throw new Error('No dsseEnvelope with payload found in sigstore bundle')
2331cb0ef41Sopenharmony_ci  }
2341cb0ef41Sopenharmony_ci  try {
2351cb0ef41Sopenharmony_ci    return JSON.parse(Buffer.from(bundle.dsseEnvelope.payload, 'base64').toString('utf8'))
2361cb0ef41Sopenharmony_ci  } catch (err) {
2371cb0ef41Sopenharmony_ci    err.message = `Failed to parse payload from dsseEnvelope: ${err.message}`
2381cb0ef41Sopenharmony_ci    throw err
2391cb0ef41Sopenharmony_ci  }
2401cb0ef41Sopenharmony_ci}
2411cb0ef41Sopenharmony_ci
2421cb0ef41Sopenharmony_cimodule.exports = {
2431cb0ef41Sopenharmony_ci  generateProvenance,
2441cb0ef41Sopenharmony_ci  verifyProvenance,
2451cb0ef41Sopenharmony_ci}
246