add new OCI manifests for attestations
This commit is contained in:
+2
-2
@@ -11,7 +11,7 @@ export async function publishOCIArtifact(
|
||||
semver: string,
|
||||
zipFile: FileMetadata,
|
||||
tarFile: FileMetadata,
|
||||
manifest: ociContainer.Manifest
|
||||
manifest: ociContainer.OCIImageManifest
|
||||
): Promise<{ packageURL: URL; publishedDigest: string }> {
|
||||
const b64Token = Buffer.from(token).toString('base64')
|
||||
|
||||
@@ -81,7 +81,7 @@ export async function publishOCIArtifact(
|
||||
}
|
||||
|
||||
async function uploadLayer(
|
||||
layer: ociContainer.Layer,
|
||||
layer: ociContainer.Descriptor,
|
||||
file: FileMetadata,
|
||||
registryURL: URL,
|
||||
checkBlobEndpoint: string,
|
||||
|
||||
+130
-23
@@ -1,19 +1,46 @@
|
||||
import { FileMetadata } from './fs-helper'
|
||||
import * as crypto from 'crypto'
|
||||
|
||||
export interface Manifest {
|
||||
const imageIndexMediaType = 'application/vnd.oci.image.index.v1+json'
|
||||
const imageManifestMediaType = 'application/vnd.oci.image.manifest.v1+json'
|
||||
const actionsPackageMediaType = 'application/vnd.github.actions.package.v1+json'
|
||||
const actionsPackageTarLayerMediaType =
|
||||
'application/vnd.github.actions.package.layer.v1.tar+gzip'
|
||||
const actionsPackageZipLayerMediaType =
|
||||
'application/vnd.github.actions.package.layer.v1.zip'
|
||||
const sigstoreBundleMediaType = 'application/vnd.dev.sigstore.bundle.v0.3+json'
|
||||
const ociEmptyMediaType = 'application/vnd.oci.empty.v1+json'
|
||||
|
||||
const actionPackageAnnotationValue = 'actions_oci_pkg'
|
||||
const actionPackageAttestationAnnotationValue = 'actions_oci_pkg_attestation'
|
||||
const actionPackageReferrerTagAnnotationValue = 'actions_oci_pkg_referrer_tag'
|
||||
|
||||
const emptyConfigSize = 2
|
||||
const emptyConfigSha =
|
||||
'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'
|
||||
|
||||
export interface OCIImageManifest {
|
||||
schemaVersion: number
|
||||
mediaType: string
|
||||
artifactType: string
|
||||
config: Layer
|
||||
layers: Layer[]
|
||||
config: Descriptor
|
||||
layers: Descriptor[]
|
||||
subject?: Descriptor
|
||||
annotations: { [key: string]: string }
|
||||
}
|
||||
|
||||
export interface Layer {
|
||||
export interface OCIIndexManifest {
|
||||
schemaVersion: number
|
||||
mediaType: string
|
||||
manifests: Descriptor[]
|
||||
annotations: { [key: string]: string }
|
||||
}
|
||||
|
||||
export interface Descriptor {
|
||||
mediaType: string
|
||||
size: number
|
||||
digest: string
|
||||
artifactType?: string
|
||||
annotations?: { [key: string]: string }
|
||||
}
|
||||
|
||||
@@ -26,24 +53,24 @@ export function createActionPackageManifest(
|
||||
ownerId: string,
|
||||
sourceCommit: string,
|
||||
version: string,
|
||||
created: Date
|
||||
): Manifest {
|
||||
created: Date = new Date()
|
||||
): OCIImageManifest {
|
||||
const configLayer = createConfigLayer()
|
||||
const sanitizedRepo = sanitizeRepository(repository)
|
||||
const tarLayer = createTarLayer(tarFile, sanitizedRepo, version)
|
||||
const zipLayer = createZipLayer(zipFile, sanitizedRepo, version)
|
||||
|
||||
const manifest: Manifest = {
|
||||
const manifest: OCIImageManifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: 'application/vnd.oci.image.manifest.v1+json',
|
||||
artifactType: 'application/vnd.github.actions.package.v1+json',
|
||||
mediaType: imageManifestMediaType,
|
||||
artifactType: actionsPackageMediaType,
|
||||
config: configLayer,
|
||||
layers: [configLayer, tarLayer, zipLayer],
|
||||
annotations: {
|
||||
'org.opencontainers.image.created': created.toISOString(),
|
||||
'action.tar.gz.digest': tarFile.sha256,
|
||||
'action.zip.digest': zipFile.sha256,
|
||||
'com.github.package.type': 'actions_oci_pkg',
|
||||
'com.github.package.type': actionPackageAnnotationValue,
|
||||
'com.github.package.version': version,
|
||||
'com.github.source.repo.id': repoId,
|
||||
'com.github.source.repo.owner.id': ownerId,
|
||||
@@ -54,9 +81,83 @@ export function createActionPackageManifest(
|
||||
return manifest
|
||||
}
|
||||
|
||||
export function createSigstoreAttestationManifest(
|
||||
bundleSize: number,
|
||||
bundleDigest: string,
|
||||
subjectSize: number,
|
||||
subjectDigest: string,
|
||||
created: Date = new Date()
|
||||
): OCIImageManifest {
|
||||
const configLayer = createConfigLayer()
|
||||
|
||||
const sigstoreAttestationLayer: Descriptor = {
|
||||
mediaType: sigstoreBundleMediaType,
|
||||
size: bundleSize,
|
||||
digest: bundleDigest
|
||||
}
|
||||
|
||||
const subject: Descriptor = {
|
||||
mediaType: imageManifestMediaType,
|
||||
size: subjectSize,
|
||||
digest: subjectDigest
|
||||
}
|
||||
|
||||
const manifest: OCIImageManifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: imageManifestMediaType,
|
||||
artifactType: sigstoreBundleMediaType,
|
||||
config: configLayer,
|
||||
layers: [sigstoreAttestationLayer],
|
||||
subject,
|
||||
|
||||
annotations: {
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1',
|
||||
'com.github.package.type': actionPackageAttestationAnnotationValue,
|
||||
'org.opencontainers.image.created': created.toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
return manifest
|
||||
}
|
||||
|
||||
export function createReferrerTagManifest(
|
||||
attestationDigest: string,
|
||||
attestationSize: number,
|
||||
attestationCreated: Date,
|
||||
created: Date = new Date()
|
||||
): OCIIndexManifest {
|
||||
const manifest: OCIIndexManifest = {
|
||||
schemaVersion: 2,
|
||||
mediaType: imageIndexMediaType,
|
||||
manifests: [
|
||||
{
|
||||
mediaType: imageManifestMediaType,
|
||||
artifactType: sigstoreBundleMediaType,
|
||||
size: attestationSize,
|
||||
digest: attestationDigest,
|
||||
annotations: {
|
||||
'com.github.package.type': actionPackageAttestationAnnotationValue,
|
||||
'org.opencontainers.image.created': attestationCreated.toISOString(),
|
||||
'dev.sigstore.bundle.content': 'dsse-envelope',
|
||||
'dev.sigstore.bundle.predicateType': 'https://slsa.dev/provenance/v1'
|
||||
}
|
||||
}
|
||||
],
|
||||
annotations: {
|
||||
'com.github.package.type': actionPackageReferrerTagAnnotationValue,
|
||||
'org.opencontainers.image.created': created.toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
return manifest
|
||||
}
|
||||
|
||||
// Calculate the SHA256 digest of a given manifest.
|
||||
// This should match the digest which the GitHub container registry calculates for this manifest.
|
||||
export function sha256Digest(manifest: Manifest): string {
|
||||
export function sha256Digest(
|
||||
manifest: OCIImageManifest | OCIIndexManifest
|
||||
): string {
|
||||
const data = JSON.stringify(manifest)
|
||||
const buffer = Buffer.from(data, 'utf8')
|
||||
const hash = crypto.createHash('sha256')
|
||||
@@ -65,12 +166,18 @@ export function sha256Digest(manifest: Manifest): string {
|
||||
return `sha256:${hexHash}`
|
||||
}
|
||||
|
||||
function createConfigLayer(): Layer {
|
||||
const configLayer: Layer = {
|
||||
mediaType: 'application/vnd.oci.empty.v1+json',
|
||||
size: 2,
|
||||
digest:
|
||||
'sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a'
|
||||
export function sizeInBytes(
|
||||
manifest: OCIImageManifest | OCIIndexManifest
|
||||
): number {
|
||||
const data = JSON.stringify(manifest)
|
||||
return Buffer.byteLength(data, 'utf8')
|
||||
}
|
||||
|
||||
function createConfigLayer(): Descriptor {
|
||||
const configLayer: Descriptor = {
|
||||
mediaType: ociEmptyMediaType,
|
||||
size: emptyConfigSize,
|
||||
digest: emptyConfigSha
|
||||
}
|
||||
|
||||
return configLayer
|
||||
@@ -80,9 +187,9 @@ function createZipLayer(
|
||||
zipFile: FileMetadata,
|
||||
repository: string,
|
||||
version: string
|
||||
): Layer {
|
||||
const zipLayer: Layer = {
|
||||
mediaType: 'application/vnd.github.actions.package.layer.v1.zip',
|
||||
): Descriptor {
|
||||
const zipLayer: Descriptor = {
|
||||
mediaType: actionsPackageZipLayerMediaType,
|
||||
size: zipFile.size,
|
||||
digest: zipFile.sha256,
|
||||
annotations: {
|
||||
@@ -97,9 +204,9 @@ function createTarLayer(
|
||||
tarFile: FileMetadata,
|
||||
repository: string,
|
||||
version: string
|
||||
): Layer {
|
||||
const tarLayer: Layer = {
|
||||
mediaType: 'application/vnd.github.actions.package.layer.v1.tar+gzip',
|
||||
): Descriptor {
|
||||
const tarLayer: Descriptor = {
|
||||
mediaType: actionsPackageTarLayerMediaType,
|
||||
size: tarFile.size,
|
||||
digest: tarFile.sha256,
|
||||
annotations: {
|
||||
|
||||
Reference in New Issue
Block a user