Merge pull request #820 from crazy-max/signing

sigstore class to sign and verify buildkit provenance blobs
This commit is contained in:
CrazyMax
2025-11-03 09:34:37 +01:00
committed by GitHub
13 changed files with 2438 additions and 9 deletions

View File

@@ -146,6 +146,9 @@ jobs:
fail-fast: false
matrix:
include: ${{ fromJson(needs.prepare-itg.outputs.includes) }}
permissions:
contents: read
id-token: write # needed for signing with GitHub OIDC Token
steps:
-
name: Checkout

View File

@@ -0,0 +1 @@
Hello, World! This is linux/amd64

View File

@@ -0,0 +1,462 @@
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v1",
"subject": [
{
"name": "hello.txt",
"digest": {
"sha256": "1b37929e66644beb58b3d28d44fba0d82aa90cab03c55a492adb81fe6e833ec8"
}
}
],
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/moby/buildkit/blob/master/docs/attestations/slsa-definitions.md",
"resolvedDependencies": [
{
"uri": "pkg:docker/docker/buildkit-syft-scanner@stable-1",
"digest": {
"sha256": "e930c2697be77cb7271d316ecfa78768b5eac73de3b16018ed38eb0ea0b5a7cb"
}
},
{
"uri": "pkg:docker/alpine@latest?platform=linux%2Famd64",
"digest": {
"sha256": "4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
{
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
}
],
"externalParameters": {
"configSource": {
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
},
"path": "hello.Dockerfile"
},
"request": {
"frontend": "dockerfile.v0",
"secrets": [
{
"id": "GIT_AUTH_HEADER",
"optional": true
},
{
"id": "GIT_AUTH_TOKEN",
"optional": true
}
]
}
},
"internalParameters": {
"buildConfig": {
"digestMapping": {
"sha256:23dcbc3cce701a8a9bbb1e33f2ea88304527a4a935c89c4564af698095463ac2": "step3",
"sha256:3192c1bd53f90cca959db778dcee30edc9a79f8cd3f9a2c54adc4606507fd3b6": "step0",
"sha256:7f1c9e959980ea3e2cf4af8ef97b6c3797a0926752b436bff11474e436defe7f": "step1",
"sha256:c8737331fb8e5f5bcb6b22320012d975057514982c788e63db13332a4219b984": "step2"
},
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "docker-image://docker.io/library/alpine:latest@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
"constraints": {},
"platform": {
"Architecture": "amd64",
"OS": "linux"
}
}
},
{
"id": "step1",
"inputs": [
"step0:0"
],
"op": {
"Op": {
"exec": {
"meta": {
"args": [
"/bin/sh",
"-c",
"echo \"Hello, World! This is ${TARGETPLATFORM}\" \u003e /hello.txt"
],
"cwd": "/",
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TARGETPLATFORM=linux/amd64"
],
"removeMountStubsRecursive": true
},
"mounts": [
{
"dest": "/"
}
]
}
},
"constraints": {},
"platform": {
"Architecture": "amd64",
"OS": "linux"
}
}
},
{
"id": "step2",
"inputs": [
"step1:0"
],
"op": {
"Op": {
"file": {
"actions": [
{
"Action": {
"copy": {
"allowEmptyWildcard": true,
"allowWildcard": true,
"createDestPath": true,
"dest": "/",
"dirCopyContents": true,
"followSymlink": true,
"mode": -1,
"src": "/hello.txt",
"timestamp": -1
}
},
"input": -1,
"output": 0,
"secondaryInput": 0
}
]
}
},
"constraints": {}
}
},
{
"id": "step3",
"inputs": [
"step2:0"
],
"op": {
"Op": {}
}
}
]
},
"builderPlatform": "linux/amd64",
"github_actor": "crazy-max",
"github_actor_id": "1951866",
"github_event_name": "workflow_dispatch",
"github_event_payload": {
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/19176?v=4",
"created_at": "2022-12-30T23:53:17Z",
"description": null,
"html_url": "https://github.com/enterprises/docker",
"id": 19176,
"name": "Docker",
"node_id": "E_kgDNSug",
"slug": "docker",
"updated_at": "2025-10-20T20:39:05Z",
"website_url": null
},
"inputs": null,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"description": "Docker helps developers bring their ideas to life by conquering the complexity of app development.",
"events_url": "https://api.github.com/orgs/docker/events",
"hooks_url": "https://api.github.com/orgs/docker/hooks",
"id": 5429470,
"issues_url": "https://api.github.com/orgs/docker/issues",
"login": "docker",
"members_url": "https://api.github.com/orgs/docker/members{/member}",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"public_members_url": "https://api.github.com/orgs/docker/public_members{/member}",
"repos_url": "https://api.github.com/orgs/docker/repos",
"url": "https://api.github.com/orgs/docker"
},
"ref": "refs/heads/main",
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/docker/github-builder-test/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/docker/github-builder-test/assignees{/user}",
"blobs_url": "https://api.github.com/repos/docker/github-builder-test/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/docker/github-builder-test/branches{/branch}",
"clone_url": "https://github.com/docker/github-builder-test.git",
"collaborators_url": "https://api.github.com/repos/docker/github-builder-test/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/docker/github-builder-test/comments{/number}",
"commits_url": "https://api.github.com/repos/docker/github-builder-test/commits{/sha}",
"compare_url": "https://api.github.com/repos/docker/github-builder-test/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/docker/github-builder-test/contents/{+path}",
"contributors_url": "https://api.github.com/repos/docker/github-builder-test/contributors",
"created_at": "2025-08-19T08:08:29Z",
"custom_properties": {},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/docker/github-builder-test/deployments",
"description": "Test repo for https://github.com/docker/github-builder-experimental",
"disabled": false,
"downloads_url": "https://api.github.com/repos/docker/github-builder-test/downloads",
"events_url": "https://api.github.com/repos/docker/github-builder-test/events",
"fork": false,
"forks": 0,
"forks_count": 0,
"forks_url": "https://api.github.com/repos/docker/github-builder-test/forks",
"full_name": "docker/github-builder-test",
"git_commits_url": "https://api.github.com/repos/docker/github-builder-test/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/docker/github-builder-test/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/docker/github-builder-test/git/tags{/sha}",
"git_url": "git://github.com/docker/github-builder-test.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": false,
"has_pages": false,
"has_projects": false,
"has_wiki": false,
"homepage": null,
"hooks_url": "https://api.github.com/repos/docker/github-builder-test/hooks",
"html_url": "https://github.com/docker/github-builder-test",
"id": 1040594287,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/docker/github-builder-test/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/docker/github-builder-test/issues/events{/number}",
"issues_url": "https://api.github.com/repos/docker/github-builder-test/issues{/number}",
"keys_url": "https://api.github.com/repos/docker/github-builder-test/keys{/key_id}",
"labels_url": "https://api.github.com/repos/docker/github-builder-test/labels{/name}",
"language": "Dockerfile",
"languages_url": "https://api.github.com/repos/docker/github-builder-test/languages",
"license": null,
"merges_url": "https://api.github.com/repos/docker/github-builder-test/merges",
"milestones_url": "https://api.github.com/repos/docker/github-builder-test/milestones{/number}",
"mirror_url": null,
"name": "github-builder-test",
"node_id": "R_kgDOPgY1bw",
"notifications_url": "https://api.github.com/repos/docker/github-builder-test/notifications{?since,all,participating}",
"open_issues": 0,
"open_issues_count": 0,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"events_url": "https://api.github.com/users/docker/events{/privacy}",
"followers_url": "https://api.github.com/users/docker/followers",
"following_url": "https://api.github.com/users/docker/following{/other_user}",
"gists_url": "https://api.github.com/users/docker/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/docker",
"id": 5429470,
"login": "docker",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"organizations_url": "https://api.github.com/users/docker/orgs",
"received_events_url": "https://api.github.com/users/docker/received_events",
"repos_url": "https://api.github.com/users/docker/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/docker/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/docker/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/docker",
"user_view_type": "public"
},
"private": true,
"pulls_url": "https://api.github.com/repos/docker/github-builder-test/pulls{/number}",
"pushed_at": "2025-10-30T10:04:10Z",
"releases_url": "https://api.github.com/repos/docker/github-builder-test/releases{/id}",
"size": 25,
"ssh_url": "git@github.com:docker/github-builder-test.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos/docker/github-builder-test/stargazers",
"statuses_url": "https://api.github.com/repos/docker/github-builder-test/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/docker/github-builder-test/subscribers",
"subscription_url": "https://api.github.com/repos/docker/github-builder-test/subscription",
"svn_url": "https://github.com/docker/github-builder-test",
"tags_url": "https://api.github.com/repos/docker/github-builder-test/tags",
"teams_url": "https://api.github.com/repos/docker/github-builder-test/teams",
"topics": [],
"trees_url": "https://api.github.com/repos/docker/github-builder-test/git/trees{/sha}",
"updated_at": "2025-10-30T10:04:14Z",
"url": "https://api.github.com/repos/docker/github-builder-test",
"visibility": "internal",
"watchers": 0,
"watchers_count": 0,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/1951866?v=4",
"events_url": "https://api.github.com/users/crazy-max/events{/privacy}",
"followers_url": "https://api.github.com/users/crazy-max/followers",
"following_url": "https://api.github.com/users/crazy-max/following{/other_user}",
"gists_url": "https://api.github.com/users/crazy-max/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/crazy-max",
"id": 1951866,
"login": "crazy-max",
"node_id": "MDQ6VXNlcjE5NTE4NjY=",
"organizations_url": "https://api.github.com/users/crazy-max/orgs",
"received_events_url": "https://api.github.com/users/crazy-max/received_events",
"repos_url": "https://api.github.com/users/crazy-max/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/crazy-max/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/crazy-max/subscriptions",
"type": "User",
"url": "https://api.github.com/users/crazy-max",
"user_view_type": "public"
},
"workflow": ".github/workflows/ci.yml"
},
"github_job": "build",
"github_ref": "refs/heads/main",
"github_ref_name": "main",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "docker/github-builder-test",
"github_repository_id": "1040594287",
"github_repository_owner": "docker",
"github_repository_owner_id": "5429470",
"github_run_attempt": "1",
"github_run_id": "18937328894",
"github_run_number": "183",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20250929.60.1",
"github_runner_name": "GitHub Actions 1002376925",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_7c0a7521-2999-41e5-af30-b7f0681f204f",
"github_server_url": "https://github.com",
"github_triggering_actor": "crazy-max",
"github_workflow": "ci",
"github_workflow_ref": "docker/github-builder-test/.github/workflows/ci.yml@refs/heads/main",
"github_workflow_sha": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
},
"runDetails": {
"builder": {
"id": "https://github.com/docker/github-builder-test/actions/runs/18937328894/attempts/1"
},
"metadata": {
"invocationID": "7qg2yuux3iklv02ktbmbtwgeb",
"startedOn": "2025-10-30T10:19:52.868710505Z",
"finishedOn": "2025-10-30T10:19:57.635810119Z",
"buildkit_metadata": {
"source": {
"locations": {
"step0": {
"locations": [
{
"ranges": [
{
"start": {
"line": 1
},
"end": {
"line": 1
}
}
]
}
]
},
"step1": {
"locations": [
{
"ranges": [
{
"start": {
"line": 3
},
"end": {
"line": 3
}
}
]
}
]
},
"step2": {
"locations": [
{
"ranges": [
{
"start": {
"line": 7
},
"end": {
"line": 7
}
}
]
}
]
}
},
"infos": [
{
"filename": "hello.Dockerfile",
"language": "Dockerfile",
"data": "RlJPTSBhbHBpbmUgQVMgYmFzZQpBUkcgVEFSR0VUUExBVEZPUk0KUlVOIGVjaG8gIkhlbGxvLCBXb3JsZCEgVGhpcyBpcyAke1RBUkdFVFBMQVRGT1JNfSIgPiAvaGVsbG8udHh0CkFSRyBCVUlMREtJVF9TQk9NX1NDQU5fU1RBR0U9dHJ1ZQoKRlJPTSBzY3JhdGNoCkNPUFkgLS1mcm9tPWJhc2UgL2hlbGxvLnR4dCAvCg==",
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "git://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"attrs": {
"git.authheadersecret": "GIT_AUTH_HEADER",
"git.authtokensecret": "GIT_AUTH_TOKEN",
"git.fullurl": "https://github.com/docker/github-builder-test.git"
}
}
},
"constraints": {}
}
},
{
"id": "step1",
"op": {
"Op": {}
},
"inputs": [
"step0:0"
]
}
],
"digestMapping": {
"sha256:47540f0959d81a7ff2fc9742b9ef0bb37d7eca99c13aa6df83b883d06e808ef2": "step0",
"sha256:96933c546ff00debd500304305864192fcb51d348e8c41b6a6e1569a051e66ed": "step1"
}
}
]
},
"layers": {
"step0:0": [
[
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:2d35ebdb57d9971fea0cac1582aa78935adf8058b2cc32db163c98822e5dfa1b",
"size": 3802452
}
]
]
}
},
"buildkit_completeness": {
"request": true,
"resolvedDependencies": true
}
}
}
}
}

View File

@@ -0,0 +1 @@
Hello, World! This is linux/arm64

View File

@@ -0,0 +1,462 @@
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v1",
"subject": [
{
"name": "hello.txt",
"digest": {
"sha256": "870e0065e68cbdeacbf9cec21b598bb579b0ef55bc48b65b71509667edb570bd"
}
}
],
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/moby/buildkit/blob/master/docs/attestations/slsa-definitions.md",
"resolvedDependencies": [
{
"uri": "pkg:docker/docker/buildkit-syft-scanner@stable-1",
"digest": {
"sha256": "e930c2697be77cb7271d316ecfa78768b5eac73de3b16018ed38eb0ea0b5a7cb"
}
},
{
"uri": "pkg:docker/alpine@latest?platform=linux%2Farm64",
"digest": {
"sha256": "4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
{
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
}
],
"externalParameters": {
"configSource": {
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
},
"path": "hello.Dockerfile"
},
"request": {
"frontend": "dockerfile.v0",
"secrets": [
{
"id": "GIT_AUTH_HEADER",
"optional": true
},
{
"id": "GIT_AUTH_TOKEN",
"optional": true
}
]
}
},
"internalParameters": {
"buildConfig": {
"digestMapping": {
"sha256:69f88b22af1cbbe236f4b5d834dfe9c4adb9535c1c602f726fd90212c302a7da": "step0",
"sha256:98c0e3a8b1a38f5aa8db558b99aace8e7645a18b0e24d6807018146d21788ae2": "step2",
"sha256:a560219f1e0cfb232a9727b9bbf9d6735c0b2190f8e00364f844cfc82bd42479": "step1",
"sha256:c7edeef726371083a69f15a3565d6c62be55a91e820f251d9a965f8e36fe3e32": "step3"
},
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "docker-image://docker.io/library/alpine:latest@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
"constraints": {},
"platform": {
"Architecture": "arm64",
"OS": "linux"
}
}
},
{
"id": "step1",
"inputs": [
"step0:0"
],
"op": {
"Op": {
"exec": {
"meta": {
"args": [
"/bin/sh",
"-c",
"echo \"Hello, World! This is ${TARGETPLATFORM}\" \u003e /hello.txt"
],
"cwd": "/",
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TARGETPLATFORM=linux/arm64"
],
"removeMountStubsRecursive": true
},
"mounts": [
{
"dest": "/"
}
]
}
},
"constraints": {},
"platform": {
"Architecture": "arm64",
"OS": "linux"
}
}
},
{
"id": "step2",
"inputs": [
"step1:0"
],
"op": {
"Op": {
"file": {
"actions": [
{
"Action": {
"copy": {
"allowEmptyWildcard": true,
"allowWildcard": true,
"createDestPath": true,
"dest": "/",
"dirCopyContents": true,
"followSymlink": true,
"mode": -1,
"src": "/hello.txt",
"timestamp": -1
}
},
"input": -1,
"output": 0,
"secondaryInput": 0
}
]
}
},
"constraints": {}
}
},
{
"id": "step3",
"inputs": [
"step2:0"
],
"op": {
"Op": {}
}
}
]
},
"builderPlatform": "linux/amd64",
"github_actor": "crazy-max",
"github_actor_id": "1951866",
"github_event_name": "workflow_dispatch",
"github_event_payload": {
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/19176?v=4",
"created_at": "2022-12-30T23:53:17Z",
"description": null,
"html_url": "https://github.com/enterprises/docker",
"id": 19176,
"name": "Docker",
"node_id": "E_kgDNSug",
"slug": "docker",
"updated_at": "2025-10-20T20:39:05Z",
"website_url": null
},
"inputs": null,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"description": "Docker helps developers bring their ideas to life by conquering the complexity of app development.",
"events_url": "https://api.github.com/orgs/docker/events",
"hooks_url": "https://api.github.com/orgs/docker/hooks",
"id": 5429470,
"issues_url": "https://api.github.com/orgs/docker/issues",
"login": "docker",
"members_url": "https://api.github.com/orgs/docker/members{/member}",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"public_members_url": "https://api.github.com/orgs/docker/public_members{/member}",
"repos_url": "https://api.github.com/orgs/docker/repos",
"url": "https://api.github.com/orgs/docker"
},
"ref": "refs/heads/main",
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/docker/github-builder-test/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/docker/github-builder-test/assignees{/user}",
"blobs_url": "https://api.github.com/repos/docker/github-builder-test/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/docker/github-builder-test/branches{/branch}",
"clone_url": "https://github.com/docker/github-builder-test.git",
"collaborators_url": "https://api.github.com/repos/docker/github-builder-test/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/docker/github-builder-test/comments{/number}",
"commits_url": "https://api.github.com/repos/docker/github-builder-test/commits{/sha}",
"compare_url": "https://api.github.com/repos/docker/github-builder-test/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/docker/github-builder-test/contents/{+path}",
"contributors_url": "https://api.github.com/repos/docker/github-builder-test/contributors",
"created_at": "2025-08-19T08:08:29Z",
"custom_properties": {},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/docker/github-builder-test/deployments",
"description": "Test repo for https://github.com/docker/github-builder-experimental",
"disabled": false,
"downloads_url": "https://api.github.com/repos/docker/github-builder-test/downloads",
"events_url": "https://api.github.com/repos/docker/github-builder-test/events",
"fork": false,
"forks": 0,
"forks_count": 0,
"forks_url": "https://api.github.com/repos/docker/github-builder-test/forks",
"full_name": "docker/github-builder-test",
"git_commits_url": "https://api.github.com/repos/docker/github-builder-test/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/docker/github-builder-test/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/docker/github-builder-test/git/tags{/sha}",
"git_url": "git://github.com/docker/github-builder-test.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": false,
"has_pages": false,
"has_projects": false,
"has_wiki": false,
"homepage": null,
"hooks_url": "https://api.github.com/repos/docker/github-builder-test/hooks",
"html_url": "https://github.com/docker/github-builder-test",
"id": 1040594287,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/docker/github-builder-test/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/docker/github-builder-test/issues/events{/number}",
"issues_url": "https://api.github.com/repos/docker/github-builder-test/issues{/number}",
"keys_url": "https://api.github.com/repos/docker/github-builder-test/keys{/key_id}",
"labels_url": "https://api.github.com/repos/docker/github-builder-test/labels{/name}",
"language": "Dockerfile",
"languages_url": "https://api.github.com/repos/docker/github-builder-test/languages",
"license": null,
"merges_url": "https://api.github.com/repos/docker/github-builder-test/merges",
"milestones_url": "https://api.github.com/repos/docker/github-builder-test/milestones{/number}",
"mirror_url": null,
"name": "github-builder-test",
"node_id": "R_kgDOPgY1bw",
"notifications_url": "https://api.github.com/repos/docker/github-builder-test/notifications{?since,all,participating}",
"open_issues": 0,
"open_issues_count": 0,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"events_url": "https://api.github.com/users/docker/events{/privacy}",
"followers_url": "https://api.github.com/users/docker/followers",
"following_url": "https://api.github.com/users/docker/following{/other_user}",
"gists_url": "https://api.github.com/users/docker/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/docker",
"id": 5429470,
"login": "docker",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"organizations_url": "https://api.github.com/users/docker/orgs",
"received_events_url": "https://api.github.com/users/docker/received_events",
"repos_url": "https://api.github.com/users/docker/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/docker/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/docker/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/docker",
"user_view_type": "public"
},
"private": true,
"pulls_url": "https://api.github.com/repos/docker/github-builder-test/pulls{/number}",
"pushed_at": "2025-10-30T10:04:10Z",
"releases_url": "https://api.github.com/repos/docker/github-builder-test/releases{/id}",
"size": 25,
"ssh_url": "git@github.com:docker/github-builder-test.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos/docker/github-builder-test/stargazers",
"statuses_url": "https://api.github.com/repos/docker/github-builder-test/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/docker/github-builder-test/subscribers",
"subscription_url": "https://api.github.com/repos/docker/github-builder-test/subscription",
"svn_url": "https://github.com/docker/github-builder-test",
"tags_url": "https://api.github.com/repos/docker/github-builder-test/tags",
"teams_url": "https://api.github.com/repos/docker/github-builder-test/teams",
"topics": [],
"trees_url": "https://api.github.com/repos/docker/github-builder-test/git/trees{/sha}",
"updated_at": "2025-10-30T10:04:14Z",
"url": "https://api.github.com/repos/docker/github-builder-test",
"visibility": "internal",
"watchers": 0,
"watchers_count": 0,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/1951866?v=4",
"events_url": "https://api.github.com/users/crazy-max/events{/privacy}",
"followers_url": "https://api.github.com/users/crazy-max/followers",
"following_url": "https://api.github.com/users/crazy-max/following{/other_user}",
"gists_url": "https://api.github.com/users/crazy-max/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/crazy-max",
"id": 1951866,
"login": "crazy-max",
"node_id": "MDQ6VXNlcjE5NTE4NjY=",
"organizations_url": "https://api.github.com/users/crazy-max/orgs",
"received_events_url": "https://api.github.com/users/crazy-max/received_events",
"repos_url": "https://api.github.com/users/crazy-max/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/crazy-max/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/crazy-max/subscriptions",
"type": "User",
"url": "https://api.github.com/users/crazy-max",
"user_view_type": "public"
},
"workflow": ".github/workflows/ci.yml"
},
"github_job": "build",
"github_ref": "refs/heads/main",
"github_ref_name": "main",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "docker/github-builder-test",
"github_repository_id": "1040594287",
"github_repository_owner": "docker",
"github_repository_owner_id": "5429470",
"github_run_attempt": "1",
"github_run_id": "18937328894",
"github_run_number": "183",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20250929.60.1",
"github_runner_name": "GitHub Actions 1002376925",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_7c0a7521-2999-41e5-af30-b7f0681f204f",
"github_server_url": "https://github.com",
"github_triggering_actor": "crazy-max",
"github_workflow": "ci",
"github_workflow_ref": "docker/github-builder-test/.github/workflows/ci.yml@refs/heads/main",
"github_workflow_sha": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
},
"runDetails": {
"builder": {
"id": "https://github.com/docker/github-builder-test/actions/runs/18937328894/attempts/1"
},
"metadata": {
"invocationID": "7qg2yuux3iklv02ktbmbtwgeb",
"startedOn": "2025-10-30T10:19:52.868710505Z",
"finishedOn": "2025-10-30T10:19:57.635810119Z",
"buildkit_metadata": {
"source": {
"locations": {
"step0": {
"locations": [
{
"ranges": [
{
"start": {
"line": 1
},
"end": {
"line": 1
}
}
]
}
]
},
"step1": {
"locations": [
{
"ranges": [
{
"start": {
"line": 3
},
"end": {
"line": 3
}
}
]
}
]
},
"step2": {
"locations": [
{
"ranges": [
{
"start": {
"line": 7
},
"end": {
"line": 7
}
}
]
}
]
}
},
"infos": [
{
"filename": "hello.Dockerfile",
"language": "Dockerfile",
"data": "RlJPTSBhbHBpbmUgQVMgYmFzZQpBUkcgVEFSR0VUUExBVEZPUk0KUlVOIGVjaG8gIkhlbGxvLCBXb3JsZCEgVGhpcyBpcyAke1RBUkdFVFBMQVRGT1JNfSIgPiAvaGVsbG8udHh0CkFSRyBCVUlMREtJVF9TQk9NX1NDQU5fU1RBR0U9dHJ1ZQoKRlJPTSBzY3JhdGNoCkNPUFkgLS1mcm9tPWJhc2UgL2hlbGxvLnR4dCAvCg==",
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "git://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"attrs": {
"git.authheadersecret": "GIT_AUTH_HEADER",
"git.authtokensecret": "GIT_AUTH_TOKEN",
"git.fullurl": "https://github.com/docker/github-builder-test.git"
}
}
},
"constraints": {}
}
},
{
"id": "step1",
"op": {
"Op": {}
},
"inputs": [
"step0:0"
]
}
],
"digestMapping": {
"sha256:47540f0959d81a7ff2fc9742b9ef0bb37d7eca99c13aa6df83b883d06e808ef2": "step0",
"sha256:96933c546ff00debd500304305864192fcb51d348e8c41b6a6e1569a051e66ed": "step1"
}
}
]
},
"layers": {
"step0:0": [
[
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:6b59a28fa20117e6048ad0616b8d8c901877ef15ff4c7f18db04e4f01f43bc39",
"size": 4138069
}
]
]
}
},
"buildkit_completeness": {
"request": true,
"resolvedDependencies": true
}
}
}
}
}

View File

@@ -0,0 +1 @@
Hello, World! This is linux/amd64

View File

@@ -0,0 +1,462 @@
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v1",
"subject": [
{
"name": "hello.txt",
"digest": {
"sha256": "1b37929e66644beb58b3d28d44fba0d82aa90cab03c55a492adb81fe6e833ec8"
}
}
],
"predicate": {
"buildDefinition": {
"buildType": "https://github.com/moby/buildkit/blob/master/docs/attestations/slsa-definitions.md",
"resolvedDependencies": [
{
"uri": "pkg:docker/docker/buildkit-syft-scanner@stable-1",
"digest": {
"sha256": "e930c2697be77cb7271d316ecfa78768b5eac73de3b16018ed38eb0ea0b5a7cb"
}
},
{
"uri": "pkg:docker/alpine@latest?platform=linux%2Famd64",
"digest": {
"sha256": "4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
{
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
}
],
"externalParameters": {
"configSource": {
"uri": "https://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"digest": {
"sha1": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
},
"path": "hello.Dockerfile"
},
"request": {
"frontend": "dockerfile.v0",
"secrets": [
{
"id": "GIT_AUTH_HEADER",
"optional": true
},
{
"id": "GIT_AUTH_TOKEN",
"optional": true
}
]
}
},
"internalParameters": {
"buildConfig": {
"digestMapping": {
"sha256:23dcbc3cce701a8a9bbb1e33f2ea88304527a4a935c89c4564af698095463ac2": "step3",
"sha256:3192c1bd53f90cca959db778dcee30edc9a79f8cd3f9a2c54adc4606507fd3b6": "step0",
"sha256:7f1c9e959980ea3e2cf4af8ef97b6c3797a0926752b436bff11474e436defe7f": "step1",
"sha256:c8737331fb8e5f5bcb6b22320012d975057514982c788e63db13332a4219b984": "step2"
},
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "docker-image://docker.io/library/alpine:latest@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412"
}
},
"constraints": {},
"platform": {
"Architecture": "amd64",
"OS": "linux"
}
}
},
{
"id": "step1",
"inputs": [
"step0:0"
],
"op": {
"Op": {
"exec": {
"meta": {
"args": [
"/bin/sh",
"-c",
"echo \"Hello, World! This is ${TARGETPLATFORM}\" \u003e /hello.txt"
],
"cwd": "/",
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TARGETPLATFORM=linux/amd64"
],
"removeMountStubsRecursive": true
},
"mounts": [
{
"dest": "/"
}
]
}
},
"constraints": {},
"platform": {
"Architecture": "amd64",
"OS": "linux"
}
}
},
{
"id": "step2",
"inputs": [
"step1:0"
],
"op": {
"Op": {
"file": {
"actions": [
{
"Action": {
"copy": {
"allowEmptyWildcard": true,
"allowWildcard": true,
"createDestPath": true,
"dest": "/",
"dirCopyContents": true,
"followSymlink": true,
"mode": -1,
"src": "/hello.txt",
"timestamp": -1
}
},
"input": -1,
"output": 0,
"secondaryInput": 0
}
]
}
},
"constraints": {}
}
},
{
"id": "step3",
"inputs": [
"step2:0"
],
"op": {
"Op": {}
}
}
]
},
"builderPlatform": "linux/amd64",
"github_actor": "crazy-max",
"github_actor_id": "1951866",
"github_event_name": "workflow_dispatch",
"github_event_payload": {
"enterprise": {
"avatar_url": "https://avatars.githubusercontent.com/b/19176?v=4",
"created_at": "2022-12-30T23:53:17Z",
"description": null,
"html_url": "https://github.com/enterprises/docker",
"id": 19176,
"name": "Docker",
"node_id": "E_kgDNSug",
"slug": "docker",
"updated_at": "2025-10-20T20:39:05Z",
"website_url": null
},
"inputs": null,
"organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"description": "Docker helps developers bring their ideas to life by conquering the complexity of app development.",
"events_url": "https://api.github.com/orgs/docker/events",
"hooks_url": "https://api.github.com/orgs/docker/hooks",
"id": 5429470,
"issues_url": "https://api.github.com/orgs/docker/issues",
"login": "docker",
"members_url": "https://api.github.com/orgs/docker/members{/member}",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"public_members_url": "https://api.github.com/orgs/docker/public_members{/member}",
"repos_url": "https://api.github.com/orgs/docker/repos",
"url": "https://api.github.com/orgs/docker"
},
"ref": "refs/heads/main",
"repository": {
"allow_forking": true,
"archive_url": "https://api.github.com/repos/docker/github-builder-test/{archive_format}{/ref}",
"archived": false,
"assignees_url": "https://api.github.com/repos/docker/github-builder-test/assignees{/user}",
"blobs_url": "https://api.github.com/repos/docker/github-builder-test/git/blobs{/sha}",
"branches_url": "https://api.github.com/repos/docker/github-builder-test/branches{/branch}",
"clone_url": "https://github.com/docker/github-builder-test.git",
"collaborators_url": "https://api.github.com/repos/docker/github-builder-test/collaborators{/collaborator}",
"comments_url": "https://api.github.com/repos/docker/github-builder-test/comments{/number}",
"commits_url": "https://api.github.com/repos/docker/github-builder-test/commits{/sha}",
"compare_url": "https://api.github.com/repos/docker/github-builder-test/compare/{base}...{head}",
"contents_url": "https://api.github.com/repos/docker/github-builder-test/contents/{+path}",
"contributors_url": "https://api.github.com/repos/docker/github-builder-test/contributors",
"created_at": "2025-08-19T08:08:29Z",
"custom_properties": {},
"default_branch": "main",
"deployments_url": "https://api.github.com/repos/docker/github-builder-test/deployments",
"description": "Test repo for https://github.com/docker/github-builder-experimental",
"disabled": false,
"downloads_url": "https://api.github.com/repos/docker/github-builder-test/downloads",
"events_url": "https://api.github.com/repos/docker/github-builder-test/events",
"fork": false,
"forks": 0,
"forks_count": 0,
"forks_url": "https://api.github.com/repos/docker/github-builder-test/forks",
"full_name": "docker/github-builder-test",
"git_commits_url": "https://api.github.com/repos/docker/github-builder-test/git/commits{/sha}",
"git_refs_url": "https://api.github.com/repos/docker/github-builder-test/git/refs{/sha}",
"git_tags_url": "https://api.github.com/repos/docker/github-builder-test/git/tags{/sha}",
"git_url": "git://github.com/docker/github-builder-test.git",
"has_discussions": false,
"has_downloads": true,
"has_issues": false,
"has_pages": false,
"has_projects": false,
"has_wiki": false,
"homepage": null,
"hooks_url": "https://api.github.com/repos/docker/github-builder-test/hooks",
"html_url": "https://github.com/docker/github-builder-test",
"id": 1040594287,
"is_template": false,
"issue_comment_url": "https://api.github.com/repos/docker/github-builder-test/issues/comments{/number}",
"issue_events_url": "https://api.github.com/repos/docker/github-builder-test/issues/events{/number}",
"issues_url": "https://api.github.com/repos/docker/github-builder-test/issues{/number}",
"keys_url": "https://api.github.com/repos/docker/github-builder-test/keys{/key_id}",
"labels_url": "https://api.github.com/repos/docker/github-builder-test/labels{/name}",
"language": "Dockerfile",
"languages_url": "https://api.github.com/repos/docker/github-builder-test/languages",
"license": null,
"merges_url": "https://api.github.com/repos/docker/github-builder-test/merges",
"milestones_url": "https://api.github.com/repos/docker/github-builder-test/milestones{/number}",
"mirror_url": null,
"name": "github-builder-test",
"node_id": "R_kgDOPgY1bw",
"notifications_url": "https://api.github.com/repos/docker/github-builder-test/notifications{?since,all,participating}",
"open_issues": 0,
"open_issues_count": 0,
"owner": {
"avatar_url": "https://avatars.githubusercontent.com/u/5429470?v=4",
"events_url": "https://api.github.com/users/docker/events{/privacy}",
"followers_url": "https://api.github.com/users/docker/followers",
"following_url": "https://api.github.com/users/docker/following{/other_user}",
"gists_url": "https://api.github.com/users/docker/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/docker",
"id": 5429470,
"login": "docker",
"node_id": "MDEyOk9yZ2FuaXphdGlvbjU0Mjk0NzA=",
"organizations_url": "https://api.github.com/users/docker/orgs",
"received_events_url": "https://api.github.com/users/docker/received_events",
"repos_url": "https://api.github.com/users/docker/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/docker/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/docker/subscriptions",
"type": "Organization",
"url": "https://api.github.com/users/docker",
"user_view_type": "public"
},
"private": true,
"pulls_url": "https://api.github.com/repos/docker/github-builder-test/pulls{/number}",
"pushed_at": "2025-10-30T10:04:10Z",
"releases_url": "https://api.github.com/repos/docker/github-builder-test/releases{/id}",
"size": 25,
"ssh_url": "git@github.com:docker/github-builder-test.git",
"stargazers_count": 0,
"stargazers_url": "https://api.github.com/repos/docker/github-builder-test/stargazers",
"statuses_url": "https://api.github.com/repos/docker/github-builder-test/statuses/{sha}",
"subscribers_url": "https://api.github.com/repos/docker/github-builder-test/subscribers",
"subscription_url": "https://api.github.com/repos/docker/github-builder-test/subscription",
"svn_url": "https://github.com/docker/github-builder-test",
"tags_url": "https://api.github.com/repos/docker/github-builder-test/tags",
"teams_url": "https://api.github.com/repos/docker/github-builder-test/teams",
"topics": [],
"trees_url": "https://api.github.com/repos/docker/github-builder-test/git/trees{/sha}",
"updated_at": "2025-10-30T10:04:14Z",
"url": "https://api.github.com/repos/docker/github-builder-test",
"visibility": "internal",
"watchers": 0,
"watchers_count": 0,
"web_commit_signoff_required": false
},
"sender": {
"avatar_url": "https://avatars.githubusercontent.com/u/1951866?v=4",
"events_url": "https://api.github.com/users/crazy-max/events{/privacy}",
"followers_url": "https://api.github.com/users/crazy-max/followers",
"following_url": "https://api.github.com/users/crazy-max/following{/other_user}",
"gists_url": "https://api.github.com/users/crazy-max/gists{/gist_id}",
"gravatar_id": "",
"html_url": "https://github.com/crazy-max",
"id": 1951866,
"login": "crazy-max",
"node_id": "MDQ6VXNlcjE5NTE4NjY=",
"organizations_url": "https://api.github.com/users/crazy-max/orgs",
"received_events_url": "https://api.github.com/users/crazy-max/received_events",
"repos_url": "https://api.github.com/users/crazy-max/repos",
"site_admin": false,
"starred_url": "https://api.github.com/users/crazy-max/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/crazy-max/subscriptions",
"type": "User",
"url": "https://api.github.com/users/crazy-max",
"user_view_type": "public"
},
"workflow": ".github/workflows/ci.yml"
},
"github_job": "build",
"github_ref": "refs/heads/main",
"github_ref_name": "main",
"github_ref_protected": "false",
"github_ref_type": "branch",
"github_repository": "docker/github-builder-test",
"github_repository_id": "1040594287",
"github_repository_owner": "docker",
"github_repository_owner_id": "5429470",
"github_run_attempt": "1",
"github_run_id": "18937328894",
"github_run_number": "183",
"github_runner_arch": "X64",
"github_runner_environment": "github-hosted",
"github_runner_image_os": "ubuntu24",
"github_runner_image_version": "20250929.60.1",
"github_runner_name": "GitHub Actions 1002376925",
"github_runner_os": "Linux",
"github_runner_tracking_id": "github_7c0a7521-2999-41e5-af30-b7f0681f204f",
"github_server_url": "https://github.com",
"github_triggering_actor": "crazy-max",
"github_workflow": "ci",
"github_workflow_ref": "docker/github-builder-test/.github/workflows/ci.yml@refs/heads/main",
"github_workflow_sha": "bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe"
}
},
"runDetails": {
"builder": {
"id": "https://github.com/docker/github-builder-test/actions/runs/18937328894/attempts/1"
},
"metadata": {
"invocationID": "7qg2yuux3iklv02ktbmbtwgeb",
"startedOn": "2025-10-30T10:19:52.868710505Z",
"finishedOn": "2025-10-30T10:19:57.635810119Z",
"buildkit_metadata": {
"source": {
"locations": {
"step0": {
"locations": [
{
"ranges": [
{
"start": {
"line": 1
},
"end": {
"line": 1
}
}
]
}
]
},
"step1": {
"locations": [
{
"ranges": [
{
"start": {
"line": 3
},
"end": {
"line": 3
}
}
]
}
]
},
"step2": {
"locations": [
{
"ranges": [
{
"start": {
"line": 7
},
"end": {
"line": 7
}
}
]
}
]
}
},
"infos": [
{
"filename": "hello.Dockerfile",
"language": "Dockerfile",
"data": "RlJPTSBhbHBpbmUgQVMgYmFzZQpBUkcgVEFSR0VUUExBVEZPUk0KUlVOIGVjaG8gIkhlbGxvLCBXb3JsZCEgVGhpcyBpcyAke1RBUkdFVFBMQVRGT1JNfSIgPiAvaGVsbG8udHh0CkFSRyBCVUlMREtJVF9TQk9NX1NDQU5fU1RBR0U9dHJ1ZQoKRlJPTSBzY3JhdGNoCkNPUFkgLS1mcm9tPWJhc2UgL2hlbGxvLnR4dCAvCg==",
"llbDefinition": [
{
"id": "step0",
"op": {
"Op": {
"source": {
"identifier": "git://github.com/docker/github-builder-test.git#bdb96fcfe8cc9e3a54800bc2537a4d4a14f0c5fe",
"attrs": {
"git.authheadersecret": "GIT_AUTH_HEADER",
"git.authtokensecret": "GIT_AUTH_TOKEN",
"git.fullurl": "https://github.com/docker/github-builder-test.git"
}
}
},
"constraints": {}
}
},
{
"id": "step1",
"op": {
"Op": {}
},
"inputs": [
"step0:0"
]
}
],
"digestMapping": {
"sha256:47540f0959d81a7ff2fc9742b9ef0bb37d7eca99c13aa6df83b883d06e808ef2": "step0",
"sha256:96933c546ff00debd500304305864192fcb51d348e8c41b6a6e1569a051e66ed": "step1"
}
}
]
},
"layers": {
"step0:0": [
[
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:2d35ebdb57d9971fea0cac1582aa78935adf8058b2cc32db163c98822e5dfa1b",
"size": 3802452
}
]
]
}
},
"buildkit_completeness": {
"request": true,
"resolvedDependencies": true
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright 2025 actions-toolkit authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {describe, expect, jest, it, beforeAll} from '@jest/globals';
import fs from 'fs';
import * as path from 'path';
import {Install as CosignInstall} from '../../src/cosign/install';
import {Sigstore} from '../../src/sigstore/sigstore';
const fixturesDir = path.join(__dirname, '..', '.fixtures');
const maybe = process.env.GITHUB_ACTIONS && process.env.GITHUB_ACTIONS === 'true' && process.env.ACTIONS_ID_TOKEN_REQUEST_URL && process.env.ImageOS && process.env.ImageOS.startsWith('ubuntu') ? describe : describe.skip;
// needs current GitHub repo info
jest.unmock('@actions/github');
beforeAll(async () => {
const cosignInstall = new CosignInstall();
const cosignBinPath = await cosignInstall.download('v3.0.2', true);
await cosignInstall.install(cosignBinPath);
}, 100000);
maybe('signProvenanceBlobs', () => {
it('single platform', async () => {
const sigstore = new Sigstore();
const results = await sigstore.signProvenanceBlobs({
localExportDir: path.join(fixturesDir, 'sigstore', 'single')
});
expect(Object.keys(results).length).toEqual(1);
const provenancePath = Object.keys(results)[0];
expect(provenancePath).toEqual(path.join(fixturesDir, 'sigstore', 'single', 'provenance.json'));
expect(fs.existsSync(results[provenancePath].bundlePath)).toBe(true);
expect(results[provenancePath].bundle).toBeDefined();
expect(results[provenancePath].certificate).toBeDefined();
expect(results[provenancePath].tlogID).toBeDefined();
expect(results[provenancePath].attestationID).not.toBeDefined();
console.log(provenancePath, JSON.stringify(results[provenancePath].bundle, null, 2));
});
it('multi-platform', async () => {
const sigstore = new Sigstore();
const results = await sigstore.signProvenanceBlobs({
localExportDir: path.join(fixturesDir, 'sigstore', 'multi')
});
expect(Object.keys(results).length).toEqual(2);
for (const [provenancePath, res] of Object.entries(results)) {
expect(provenancePath).toMatch(/linux_(amd64|arm64)\/provenance.json/);
expect(fs.existsSync(res.bundlePath)).toBe(true);
expect(res.bundle).toBeDefined();
expect(res.certificate).toBeDefined();
expect(res.tlogID).toBeDefined();
expect(res.attestationID).not.toBeDefined();
console.log(provenancePath, JSON.stringify(res.bundle, null, 2));
}
});
});
maybe('verifySignedArtifacts', () => {
it('sign and verify', async () => {
const sigstore = new Sigstore();
const signResults = await sigstore.signProvenanceBlobs({
localExportDir: path.join(fixturesDir, 'sigstore', 'multi')
});
expect(Object.keys(signResults).length).toEqual(2);
const verifyResults = await sigstore.verifySignedArtifacts(
{
certificateIdentityRegexp: `^https://github.com/docker/actions-toolkit/.github/workflows/test.yml.*$`
},
signResults
);
expect(Object.keys(verifyResults).length).toEqual(2);
for (const [artifactPath, res] of Object.entries(verifyResults)) {
expect(fs.existsSync(artifactPath)).toBe(true);
expect(res.bundlePath).toBeDefined();
expect(res.cosignArgs).toBeDefined();
}
});
});

View File

@@ -46,6 +46,7 @@
},
"dependencies": {
"@actions/artifact": "^4.0.0",
"@actions/attest": "^2.0.0",
"@actions/cache": "^4.1.0",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
@@ -56,6 +57,8 @@
"@azure/storage-blob": "^12.15.0",
"@octokit/core": "^5.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.1",
"@sigstore/bundle": "^3.1.0",
"@sigstore/sign": "^3.1.0",
"async-retry": "^1.3.3",
"csv-parse": "^6.1.0",
"gunzip-maybe": "^1.4.2",
@@ -68,6 +71,8 @@
"tmp": "^0.2.5"
},
"devDependencies": {
"@sigstore/mock": "^0.10.0",
"@sigstore/rekor-types": "^3.0.0",
"@types/gunzip-maybe": "^1.4.2",
"@types/he": "^1.2.3",
"@types/js-yaml": "^4.0.9",

225
src/sigstore/sigstore.ts Normal file
View File

@@ -0,0 +1,225 @@
/**
* Copyright 2025 actions-toolkit authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {X509Certificate} from 'crypto';
import fs from 'fs';
import path from 'path';
import {Endpoints} from '@actions/attest/lib/endpoints';
import * as core from '@actions/core';
import {signPayload} from '@actions/attest/lib/sign';
import {bundleToJSON} from '@sigstore/bundle';
import {Attestation} from '@actions/attest';
import {Bundle} from '@sigstore/sign';
import {Cosign} from '../cosign/cosign';
import {Exec} from '../exec';
import {GitHub} from '../github';
import {MEDIATYPE_PAYLOAD as intotoMediatypePayload, Subject} from '../types/intoto/intoto';
import {FULCIO_URL, REKOR_URL, SEARCH_URL, TSASERVER_URL} from '../types/sigstore/sigstore';
export interface SignProvenanceBlobsOpts {
localExportDir: string;
name?: string;
noTransparencyLog?: boolean;
}
export interface SignProvenanceBlobsResult extends Attestation {
bundlePath: string;
subjects: Array<Subject>;
}
export interface VerifySignedArtifactsOpts {
certificateIdentityRegexp: string;
}
export interface VerifySignedArtifactsResult {
bundlePath: string;
cosignArgs: Array<string>;
}
export interface SigstoreOpts {
cosign?: Cosign;
}
export class Sigstore {
private readonly cosign: Cosign;
constructor(opts?: SigstoreOpts) {
this.cosign = opts?.cosign || new Cosign();
}
public async signProvenanceBlobs(opts: SignProvenanceBlobsOpts): Promise<Record<string, SignProvenanceBlobsResult>> {
const result: Record<string, SignProvenanceBlobsResult> = {};
try {
if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) {
throw new Error('missing "id-token" permission. Please add "permissions: id-token: write" to your workflow.');
}
const endpoints = this.signingEndpoints(opts);
core.info(`Using Sigstore signing endpoint: ${endpoints.fulcioURL}`);
const provenanceBlobs = Sigstore.getProvenanceBlobs(opts);
for (const p of Object.keys(provenanceBlobs)) {
await core.group(`Signing ${p}`, async () => {
const blob = provenanceBlobs[p];
const bundlePath = path.join(path.dirname(p), `${opts.name ?? 'provenance'}.sigstore.json`);
const subjects = Sigstore.getProvenanceSubjects(blob);
if (subjects.length === 0) {
core.warning(`No subjects found in provenance ${p}, skip signing.`);
return;
}
const bundle = await signPayload(
{
body: blob,
type: intotoMediatypePayload
},
endpoints
);
const attest = Sigstore.toAttestation(bundle);
core.info(`Provenance blob signed for:`);
for (const subject of subjects) {
const [digestAlg, digestValue] = Object.entries(subject.digest)[0] || [];
core.info(` - ${subject.name} (${digestAlg}:${digestValue})`);
}
if (attest.tlogID) {
core.info(`Attestation signature uploaded to Rekor transparency log: ${SEARCH_URL}?logIndex=${attest.tlogID}`);
}
core.info(`Writing Sigstore bundle to: ${bundlePath}`);
fs.writeFileSync(bundlePath, JSON.stringify(attest.bundle, null, 2), {
encoding: 'utf-8'
});
result[p] = {
...attest,
bundlePath: bundlePath,
subjects: subjects
};
});
}
} catch (err) {
throw new Error(`Signing BuildKit provenance blobs failed: ${(err as Error).message}`);
}
return result;
}
public async verifySignedArtifacts(opts: VerifySignedArtifactsOpts, signed: Record<string, SignProvenanceBlobsResult>): Promise<Record<string, VerifySignedArtifactsResult>> {
const result: Record<string, VerifySignedArtifactsResult> = {};
if (!(await this.cosign.isAvailable())) {
throw new Error('Cosign is required to verify signed artifacts');
}
for (const [provenancePath, signedRes] of Object.entries(signed)) {
const baseDir = path.dirname(provenancePath);
await core.group(`Verifying ${signedRes.bundlePath}`, async () => {
for (const subject of signedRes.subjects) {
const artifactPath = path.join(baseDir, subject.name);
core.info(`Verifying signed artifact ${artifactPath}`);
// prettier-ignore
const cosignArgs = [
'verify-blob-attestation',
'--new-bundle-format',
'--certificate-oidc-issuer', 'https://token.actions.githubusercontent.com',
'--certificate-identity-regexp', opts.certificateIdentityRegexp
]
if (!signedRes.bundle.verificationMaterial || !Array.isArray(signedRes.bundle.verificationMaterial.tlogEntries) || signedRes.bundle.verificationMaterial.tlogEntries.length === 0) {
// if there is no tlog entry, we skip tlog verification but still verify the signed timestamp
cosignArgs.push('--use-signed-timestamps', '--insecure-ignore-tlog');
}
const execRes = await Exec.getExecOutput('cosign', [...cosignArgs, '--bundle', signedRes.bundlePath, artifactPath], {
ignoreReturnCode: true
});
if (execRes.stderr.length > 0 && execRes.exitCode != 0) {
throw new Error(execRes.stderr);
}
result[artifactPath] = {
bundlePath: signedRes.bundlePath,
cosignArgs: cosignArgs
};
}
});
}
return result;
}
private signingEndpoints(opts: SignProvenanceBlobsOpts): Endpoints {
const noTransparencyLog = opts.noTransparencyLog ?? GitHub.context.payload.repository?.private;
core.info(`Upload to transparency log: ${noTransparencyLog ? 'disabled' : 'enabled'}`);
return {
fulcioURL: FULCIO_URL,
rekorURL: noTransparencyLog ? undefined : REKOR_URL,
tsaServerURL: TSASERVER_URL
};
}
private static getProvenanceBlobs(opts: SignProvenanceBlobsOpts): Record<string, Buffer> {
// For single platform build
const singleProvenance = path.join(opts.localExportDir, 'provenance.json');
if (fs.existsSync(singleProvenance)) {
return {[singleProvenance]: fs.readFileSync(singleProvenance)};
}
// For multi-platform build
const dirents = fs.readdirSync(opts.localExportDir, {withFileTypes: true});
const platformFolders = dirents.filter(dirent => dirent.isDirectory());
if (platformFolders.length > 0 && platformFolders.length === dirents.length && platformFolders.every(platformFolder => fs.existsSync(path.join(opts.localExportDir, platformFolder.name, 'provenance.json')))) {
const result: Record<string, Buffer> = {};
for (const platformFolder of platformFolders) {
const p = path.join(opts.localExportDir, platformFolder.name, 'provenance.json');
result[p] = fs.readFileSync(p);
}
return result;
}
throw new Error(`No valid provenance.json found in ${opts.localExportDir}`);
}
private static getProvenanceSubjects(body: Buffer): Array<Subject> {
const statement = JSON.parse(body.toString()) as {
subject: Array<{name: string; digest: Record<string, string>}>;
};
return statement.subject.map(s => ({
name: s.name,
digest: s.digest
}));
}
// https://github.com/actions/toolkit/blob/d3ab50471b4ff1d1274dffb90ef9c5d9949b4886/packages/attest/src/attest.ts#L90
private static toAttestation(bundle: Bundle): Attestation {
let certBytes: Buffer;
switch (bundle.verificationMaterial.content.$case) {
case 'x509CertificateChain':
certBytes = bundle.verificationMaterial.content.x509CertificateChain.certificates[0].rawBytes;
break;
case 'certificate':
certBytes = bundle.verificationMaterial.content.certificate.rawBytes;
break;
default:
throw new Error('Bundle must contain an x509 certificate');
}
const signingCert = new X509Certificate(certBytes);
// Collect transparency log ID if available
const tlogEntries = bundle.verificationMaterial.tlogEntries;
const tlogID = tlogEntries.length > 0 ? tlogEntries[0].logIndex : undefined;
return {
bundle: bundleToJSON(bundle),
certificate: signingCert.toString(),
tlogID: tlogID
};
}
}

View File

@@ -18,3 +18,9 @@
export const MEDIATYPE_PAYLOAD = 'application/vnd.in-toto+json';
export const MEDIATYPE_PREDICATE = 'in-toto.io/predicate-type';
// https://github.com/in-toto/in-toto-golang/blob/0a34c087cedcc36de065b4fccb7cf7c9bc16e29f/in_toto/attestations.go#L30-L42
export interface Subject {
name: string;
digest: Record<string, string>;
}

View File

@@ -0,0 +1,20 @@
/**
* Copyright 2025 actions-toolkit authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const FULCIO_URL = 'https://fulcio.sigstore.dev';
export const REKOR_URL = 'https://rekor.sigstore.dev';
export const TSASERVER_URL = 'https://timestamp.sigstore.dev';
export const SEARCH_URL = 'https://search.sigstore.dev';

707
yarn.lock

File diff suppressed because it is too large Load Diff