Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge GitLab provenance generation into latest #6525

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -15828,6 +15828,7 @@
"license": "ISC",
"dependencies": {
"ci-info": "^3.6.1",
"libnpmpublish": "file:",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on here?

"normalize-package-data": "^5.0.0",
"npm-package-arg": "^10.1.0",
"npm-registry-fetch": "^14.0.3",
Expand Down
231 changes: 183 additions & 48 deletions workspaces/libnpmpublish/lib/provenance.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,204 @@
const { sigstore } = require('sigstore')
const { readFile } = require('fs/promises')
const ci = require('ci-info')

const INTOTO_PAYLOAD_TYPE = 'application/vnd.in-toto+json'
const INTOTO_STATEMENT_TYPE = 'https://in-toto.io/Statement/v0.1'
const SLSA_PREDICATE_TYPE = 'https://slsa.dev/provenance/v0.2'

const BUILDER_ID = 'https://github.com/actions/runner'
const BUILD_TYPE_PREFIX = 'https://github.com/npm/cli/gha'
const BUILD_TYPE_VERSION = 'v2'
const GITHUB_BUILDER_ID = 'https://github.com/actions/runner'
const GITHUB_BUILD_TYPE_PREFIX = 'https://github.com/npm/cli/gha'
const GITHUB_BUILD_TYPE_VERSION = 'v2'

const GITLAB_BUILD_TYPE_PREFIX = 'https://github.com/npm/cli/gitlab'
const GITLAB_BUILD_TYPE_VERSION = 'v0alpha1'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any point bumping this to something like v0beta1?


const generateProvenance = async (subject, opts) => {
const { env } = process
/* istanbul ignore next - not covering missing env var case */
const [workflowPath] = (env.GITHUB_WORKFLOW_REF || '')
.replace(env.GITHUB_REPOSITORY + '/', '')
.split('@')
const payload = {
_type: INTOTO_STATEMENT_TYPE,
subject,
predicateType: SLSA_PREDICATE_TYPE,
predicate: {
buildType: `${BUILD_TYPE_PREFIX}/${BUILD_TYPE_VERSION}`,
builder: { id: BUILDER_ID },
invocation: {
configSource: {
uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
digest: {
sha1: env.GITHUB_SHA,
let payload
if (ci.GITHUB_ACTIONS) {
/* istanbul ignore next - not covering missing env var case */
const [workflowPath] = (env.GITHUB_WORKFLOW_REF || '')
.replace(env.GITHUB_REPOSITORY + '/', '')
.split('@')
payload = {
_type: INTOTO_STATEMENT_TYPE,
subject,
predicateType: SLSA_PREDICATE_TYPE,
predicate: {
buildType: `${GITHUB_BUILD_TYPE_PREFIX}/${GITHUB_BUILD_TYPE_VERSION}`,
builder: { id: GITHUB_BUILDER_ID },
invocation: {
configSource: {
uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
digest: {
sha1: env.GITHUB_SHA,
},
entryPoint: workflowPath,
},
parameters: {},
environment: {
GITHUB_EVENT_NAME: env.GITHUB_EVENT_NAME,
GITHUB_REF: env.GITHUB_REF,
GITHUB_REPOSITORY: env.GITHUB_REPOSITORY,
GITHUB_REPOSITORY_ID: env.GITHUB_REPOSITORY_ID,
GITHUB_REPOSITORY_OWNER_ID: env.GITHUB_REPOSITORY_OWNER_ID,
GITHUB_RUN_ATTEMPT: env.GITHUB_RUN_ATTEMPT,
GITHUB_RUN_ID: env.GITHUB_RUN_ID,
GITHUB_SHA: env.GITHUB_SHA,
GITHUB_WORKFLOW_REF: env.GITHUB_WORKFLOW_REF,
GITHUB_WORKFLOW_SHA: env.GITHUB_WORKFLOW_SHA,
},
entryPoint: workflowPath,
},
parameters: {},
environment: {
GITHUB_EVENT_NAME: env.GITHUB_EVENT_NAME,
GITHUB_REF: env.GITHUB_REF,
GITHUB_REPOSITORY: env.GITHUB_REPOSITORY,
GITHUB_REPOSITORY_ID: env.GITHUB_REPOSITORY_ID,
GITHUB_REPOSITORY_OWNER_ID: env.GITHUB_REPOSITORY_OWNER_ID,
GITHUB_RUN_ATTEMPT: env.GITHUB_RUN_ATTEMPT,
GITHUB_RUN_ID: env.GITHUB_RUN_ID,
GITHUB_SHA: env.GITHUB_SHA,
GITHUB_WORKFLOW_REF: env.GITHUB_WORKFLOW_REF,
GITHUB_WORKFLOW_SHA: env.GITHUB_WORKFLOW_SHA,
metadata: {
buildInvocationId: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`,
completeness: {
parameters: false,
environment: false,
materials: false,
},
reproducible: false,
},
materials: [
{
uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
digest: {
sha1: env.GITHUB_SHA,
},
},
],
},
metadata: {
buildInvocationId: `${env.GITHUB_RUN_ID}-${env.GITHUB_RUN_ATTEMPT}`,
completeness: {
parameters: false,
environment: false,
materials: false,
}
}
if (ci.GITLAB) {
payload = {
_type: INTOTO_STATEMENT_TYPE,
subject,
predicateType: SLSA_PREDICATE_TYPE,
predicate: {
buildType: `${GITLAB_BUILD_TYPE_PREFIX}/${GITLAB_BUILD_TYPE_VERSION}`,
builder: { id: `${env.CI_PROJECT_URL}/-/runners/${env.CI_RUNNER_ID}` },
invocation: {
configSource: {
uri: `git+${env.CI_PROJECT_URL}`,
digest: {
sha1: env.CI_COMMIT_SHA,
},
entryPoint: env.CI_JOB_NAME,
},
parameters: {
CI: env.CI,
CI_API_GRAPHQL_URL: env.CI_API_GRAPHQL_URL,
CI_API_V4_URL: env.CI_API_V4_URL,
CI_BUILD_BEFORE_SHA: env.CI_BUILD_BEFORE_SHA,
CI_BUILD_ID: env.CI_BUILD_ID,
CI_BUILD_NAME: env.CI_BUILD_NAME,
CI_BUILD_REF: env.CI_BUILD_REF,
CI_BUILD_REF_NAME: env.CI_BUILD_REF_NAME,
CI_BUILD_REF_SLUG: env.CI_BUILD_REF_SLUG,
CI_BUILD_STAGE: env.CI_BUILD_STAGE,
CI_COMMIT_BEFORE_SHA: env.CI_COMMIT_BEFORE_SHA,
CI_COMMIT_BRANCH: env.CI_COMMIT_BRANCH,
CI_COMMIT_REF_NAME: env.CI_COMMIT_REF_NAME,
CI_COMMIT_REF_PROTECTED: env.CI_COMMIT_REF_PROTECTED,
CI_COMMIT_REF_SLUG: env.CI_COMMIT_REF_SLUG,
CI_COMMIT_SHA: env.CI_COMMIT_SHA,
CI_COMMIT_SHORT_SHA: env.CI_COMMIT_SHORT_SHA,
CI_COMMIT_TIMESTAMP: env.CI_COMMIT_TIMESTAMP,
CI_COMMIT_TITLE: env.CI_COMMIT_TITLE,
CI_CONFIG_PATH: env.CI_CONFIG_PATH,
CI_DEFAULT_BRANCH: env.CI_DEFAULT_BRANCH,
CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX:
env.CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX,
CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX: env.CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX,
CI_DEPENDENCY_PROXY_SERVER: env.CI_DEPENDENCY_PROXY_SERVER,
CI_DEPENDENCY_PROXY_USER: env.CI_DEPENDENCY_PROXY_USER,
CI_JOB_ID: env.CI_JOB_ID,
CI_JOB_NAME: env.CI_JOB_NAME,
CI_JOB_NAME_SLUG: env.CI_JOB_NAME_SLUG,
CI_JOB_STAGE: env.CI_JOB_STAGE,
CI_JOB_STARTED_AT: env.CI_JOB_STARTED_AT,
CI_JOB_URL: env.CI_JOB_URL,
CI_NODE_TOTAL: env.CI_NODE_TOTAL,
CI_PAGES_DOMAIN: env.CI_PAGES_DOMAIN,
CI_PAGES_URL: env.CI_PAGES_URL,
CI_PIPELINE_CREATED_AT: env.CI_PIPELINE_CREATED_AT,
CI_PIPELINE_ID: env.CI_PIPELINE_ID,
CI_PIPELINE_IID: env.CI_PIPELINE_IID,
CI_PIPELINE_SOURCE: env.CI_PIPELINE_SOURCE,
CI_PIPELINE_URL: env.CI_PIPELINE_URL,
CI_PROJECT_CLASSIFICATION_LABEL: env.CI_PROJECT_CLASSIFICATION_LABEL,
CI_PROJECT_DESCRIPTION: env.CI_PROJECT_DESCRIPTION,
CI_PROJECT_ID: env.CI_PROJECT_ID,
CI_PROJECT_NAME: env.CI_PROJECT_NAME,
CI_PROJECT_NAMESPACE: env.CI_PROJECT_NAMESPACE,
CI_PROJECT_NAMESPACE_ID: env.CI_PROJECT_NAMESPACE_ID,
CI_PROJECT_PATH: env.CI_PROJECT_PATH,
CI_PROJECT_PATH_SLUG: env.CI_PROJECT_PATH_SLUG,
CI_PROJECT_REPOSITORY_LANGUAGES: env.CI_PROJECT_REPOSITORY_LANGUAGES,
CI_PROJECT_ROOT_NAMESPACE: env.CI_PROJECT_ROOT_NAMESPACE,
CI_PROJECT_TITLE: env.CI_PROJECT_TITLE,
CI_PROJECT_URL: env.CI_PROJECT_URL,
CI_PROJECT_VISIBILITY: env.CI_PROJECT_VISIBILITY,
CI_REGISTRY: env.CI_REGISTRY,
CI_REGISTRY_IMAGE: env.CI_REGISTRY_IMAGE,
CI_REGISTRY_USER: env.CI_REGISTRY_USER,
CI_RUNNER_DESCRIPTION: env.CI_RUNNER_DESCRIPTION,
CI_RUNNER_ID: env.CI_RUNNER_ID,
CI_RUNNER_TAGS: env.CI_RUNNER_TAGS,
CI_SERVER_HOST: env.CI_SERVER_HOST,
CI_SERVER_NAME: env.CI_SERVER_NAME,
CI_SERVER_PORT: env.CI_SERVER_PORT,
CI_SERVER_PROTOCOL: env.CI_SERVER_PROTOCOL,
CI_SERVER_REVISION: env.CI_SERVER_REVISION,
CI_SERVER_SHELL_SSH_HOST: env.CI_SERVER_SHELL_SSH_HOST,
CI_SERVER_SHELL_SSH_PORT: env.CI_SERVER_SHELL_SSH_PORT,
CI_SERVER_URL: env.CI_SERVER_URL,
CI_SERVER_VERSION: env.CI_SERVER_VERSION,
CI_SERVER_VERSION_MAJOR: env.CI_SERVER_VERSION_MAJOR,
CI_SERVER_VERSION_MINOR: env.CI_SERVER_VERSION_MINOR,
CI_SERVER_VERSION_PATCH: env.CI_SERVER_VERSION_PATCH,
CI_TEMPLATE_REGISTRY_HOST: env.CI_TEMPLATE_REGISTRY_HOST,
GITLAB_CI: env.GITLAB_CI,
GITLAB_FEATURES: env.GITLAB_FEATURES,
GITLAB_USER_ID: env.GITLAB_USER_ID,
GITLAB_USER_LOGIN: env.GITLAB_USER_LOGIN,
RUNNER_GENERATE_ARTIFACTS_METADATA: env.RUNNER_GENERATE_ARTIFACTS_METADATA,
},
environment: {
name: env.CI_RUNNER_DESCRIPTION,
architecture: env.CI_RUNNER_EXECUTABLE_ARCH,
server: env.CI_SERVER_URL,
project: env.CI_PROJECT_PATH,
job: {
id: env.CI_JOB_ID,
},
pipeline: {
id: env.CI_PIPELINE_ID,
ref: env.CI_CONFIG_PATH,
},
},
},
reproducible: false,
},
materials: [
{
uri: `git+${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}@${env.GITHUB_REF}`,
digest: {
sha1: env.GITHUB_SHA,
metadata: {
buildInvocationId: `${env.CI_JOB_URL}`,
completeness: {
parameters: true,
environment: true,
materials: false,
},
reproducible: false,
},
],
},
materials: [
{
uri: `git+${env.CI_PROJECT_URL}`,
digest: {
sha1: env.CI_COMMIT_SHA,
},
},
],
},
}
}

return sigstore.attest(Buffer.from(JSON.stringify(payload)), INTOTO_PAYLOAD_TYPE, opts)
}

Expand Down
34 changes: 21 additions & 13 deletions workspaces/libnpmpublish/lib/publish.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => {
provenanceBundle = await generateProvenance([subject], opts)

/* eslint-disable-next-line max-len */
log.notice('publish', 'Signed provenance statement with source and build information from GitHub Actions')
log.notice('publish', `Signed provenance statement with source and build information from ${ciInfo.name}`)

const tlogEntry = provenanceBundle?.verificationMaterial?.tlogEntries[0]
/* istanbul ignore else */
Expand Down Expand Up @@ -242,19 +242,27 @@ const patchMetadata = (current, newData) => {

// Check that all the prereqs are met for provenance generation
const ensureProvenanceGeneration = async (registry, spec, opts) => {
// Ensure that we're running in GHA, currently the only supported build environment
if (ciInfo.name !== 'GitHub Actions') {
throw Object.assign(
new Error('Automatic provenance generation not supported outside of GitHub Actions'),
{ code: 'EUSAGE' }
)
}

// Ensure that the GHA OIDC token is available
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
if (ciInfo.GITHUB_ACTIONS) {
// Ensure that the GHA OIDC token is available
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
throw Object.assign(
/* eslint-disable-next-line max-len */
new Error('Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'),
{ code: 'EUSAGE' }
)
}
} else if (ciInfo.GITLAB) {
// Ensure that the Sigstore OIDC token is available
if (!process.env.SIGSTORE_ID_TOKEN) {
throw Object.assign(
/* eslint-disable-next-line max-len */
new Error('Provenance generation in GitLab CI requires "SIGSTORE_ID_TOKEN" with "sigstore" audience to be present in "id_tokens". For more info see:\nhttps://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html'),
{ code: 'EUSAGE' }
)
}
} else {
throw Object.assign(
/* eslint-disable-next-line max-len */
new Error('Provenance generation in GitHub Actions requires "write" access to the "id-token" permission'),
new Error('Automatic provenance generation not supported for provider: ' + ciInfo.name),
{ code: 'EUSAGE' }
)
}
Expand Down
1 change: 1 addition & 0 deletions workspaces/libnpmpublish/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"homepage": "https://npmjs.com/package/libnpmpublish",
"dependencies": {
"ci-info": "^3.6.1",
"libnpmpublish": "file:",
"normalize-package-data": "^5.0.0",
"npm-package-arg": "^10.1.0",
"npm-registry-fetch": "^14.0.3",
Expand Down
Loading