From 78d87a96b85f5a0139c41294974d1409d939f55e Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Tue, 28 May 2024 13:11:37 +0200 Subject: [PATCH] buildx: history load .dockerbuild Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- __tests__/buildx/history.test.ts | 49 ++++++++ .../docker~login-action~T0XYYW.dockerbuild | Bin 0 -> 25331 bytes src/buildx/history.ts | 88 +++++++++++++- src/types/buildkit/buildkit.ts | 24 ++++ src/types/buildkit/client.ts | 78 +++++++++++++ src/types/buildkit/control.ts | 108 ++++++++++++++++++ src/types/buildkit/ops.ts | 82 +++++++++++++ src/types/buildkit/rpc.ts | 27 +++++ src/types/buildx/buildx.ts | 24 ++++ src/types/buildx/history.ts | 4 + src/types/intoto/intoto.ts | 20 ++++ .../intoto/slsa_provenance/v0.2/provenance.ts | 69 +++++++++++ 12 files changed, 571 insertions(+), 2 deletions(-) create mode 100644 __tests__/buildx/history.test.ts create mode 100644 __tests__/fixtures/oci-archive/docker~login-action~T0XYYW.dockerbuild create mode 100644 src/types/buildkit/buildkit.ts create mode 100644 src/types/buildkit/client.ts create mode 100644 src/types/buildkit/control.ts create mode 100644 src/types/buildkit/ops.ts create mode 100644 src/types/buildkit/rpc.ts create mode 100644 src/types/intoto/intoto.ts create mode 100644 src/types/intoto/slsa_provenance/v0.2/provenance.ts diff --git a/__tests__/buildx/history.test.ts b/__tests__/buildx/history.test.ts new file mode 100644 index 00000000..dffbf0ad --- /dev/null +++ b/__tests__/buildx/history.test.ts @@ -0,0 +1,49 @@ +/** + * Copyright 2024 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 {afterEach, beforeEach, describe, expect, jest, test} from '@jest/globals'; +import path from 'path'; +import * as rimraf from 'rimraf'; + +import {History} from '../../src/buildx/history'; + +const fixturesDir = path.join(__dirname, '..', 'fixtures'); + +// prettier-ignore +const tmpDir = path.join(process.env.TEMP || '/tmp', 'docker-jest'); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +afterEach(function () { + rimraf.sync(tmpDir); +}); + +describe('load', () => { + // prettier-ignore + test.each([ + ['crazy-max~docker-alpine-s6~II9A63.dockerbuild'], + ['docker~login-action~T0XYYW.dockerbuild'], + ['moby~buildkit~LWDOW6.dockerbuild'], + ])('loading %p', async (filename) => { + const res = await History.load({ + file: path.join(fixturesDir, 'oci-archive', filename) + }); + // console.log(JSON.stringify(res, null, 2)); + expect(res).toBeDefined(); + }); +}); diff --git a/__tests__/fixtures/oci-archive/docker~login-action~T0XYYW.dockerbuild b/__tests__/fixtures/oci-archive/docker~login-action~T0XYYW.dockerbuild new file mode 100644 index 0000000000000000000000000000000000000000..9d0e18de7baaefe70a4611f540b20dfd8528617c GIT binary patch literal 25331 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 { + const ociArchive = await OCI.loadArchive({ + file: opts.file + }); + return History.readRecords(ociArchive.root.index, ociArchive); + } + + private static readRecords(index: Index, archive: Archive): Record { + const res: Record = {}; + index.manifests.forEach(desc => { + switch (desc.mediaType) { + case MEDIATYPE_IMAGE_MANIFEST_V1: { + const record = History.readRecord(desc, archive); + res[record.Ref] = record; + break; + } + case MEDIATYPE_IMAGE_INDEX_V1: { + if (!Object.prototype.hasOwnProperty.call(archive.indexes, desc.digest)) { + throw new Error(`Missing index: ${desc.digest}`); + } + const records = History.readRecords(archive.indexes[desc.digest], archive); + for (const ref in records) { + if (!Object.prototype.hasOwnProperty.call(records, ref)) { + continue; + } + res[ref] = records[ref]; + } + break; + } + } + }); + return res; + } + + private static readRecord(desc: Descriptor, archive: Archive): BuildRecord { + if (!Object.prototype.hasOwnProperty.call(archive.manifests, desc.digest)) { + throw new Error(`Missing manifest: ${desc.digest}`); + } + const manifest = archive.manifests[desc.digest]; + if (manifest.config.mediaType !== MEDIATYPE_HISTORY_RECORD_V0) { + throw new Error(`Unexpected config media type: ${manifest.config.mediaType}`); + } + if (!Object.prototype.hasOwnProperty.call(archive.blobs, manifest.config.digest)) { + throw new Error(`Missing config blob: ${manifest.config.digest}`); + } + const record = JSON.parse(archive.blobs[manifest.config.digest]); + if (manifest.annotations && ANNOTATION_REF_KEY in manifest.annotations) { + if (record.Ref !== manifest.annotations[ANNOTATION_REF_KEY]) { + throw new Error(`Mismatched ref ${desc.digest}: ${record.Ref} != ${manifest.annotations[ANNOTATION_REF_KEY]}`); + } + } + manifest.layers.forEach(layer => { + switch (layer.mediaType) { + case MEDIATYPE_SOLVE_STATUS_V0: { + if (!Object.prototype.hasOwnProperty.call(archive.blobs, layer.digest)) { + throw new Error(`Missing blob: ${layer.digest}`); + } + record.solveStatus = JSON.parse(archive.blobs[layer.digest]); + break; + } + case MEDIATYPE_INTOTO_PAYLOAD: { + if (!Object.prototype.hasOwnProperty.call(archive.blobs, layer.digest)) { + throw new Error(`Missing blob: ${layer.digest}`); + } + if (layer.annotations && MEDIATYPE_PREDICATE in layer.annotations && layer.annotations[MEDIATYPE_PREDICATE].startsWith('https://slsa.dev/provenance/')) { + record.provenance = JSON.parse(archive.blobs[layer.digest]); + } + break; + } + } + }); + return record; + } + public async export(opts: ExportRecordOpts): Promise { if (os.platform() === 'win32') { throw new Error('Exporting a build record is currently not supported on Windows'); diff --git a/src/types/buildkit/buildkit.ts b/src/types/buildkit/buildkit.ts new file mode 100644 index 00000000..24680ef7 --- /dev/null +++ b/src/types/buildkit/buildkit.ts @@ -0,0 +1,24 @@ +/** + * Copyright 2024 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 ANNOTATION_REF_KEY = 'vnd.buildkit.history.reference'; + +export const MEDIATYPE_SOLVE_STATUS_V0 = 'application/vnd.buildkit.solvestatus.v0'; + +export const MEDIATYPE_HISTORY_RECORD_V0 = 'application/vnd.buildkit.historyrecord.v0'; + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/llbsolver/history.go#L672 +export const MEDIATYPE_STATUS_V0 = 'application/vnd.buildkit.status.v0'; diff --git a/src/types/buildkit/client.ts b/src/types/buildkit/client.ts new file mode 100644 index 00000000..3c1f7fcb --- /dev/null +++ b/src/types/buildkit/client.ts @@ -0,0 +1,78 @@ +/** + * Copyright 2024 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 {Digest} from '../oci/digest'; +import {ProgressGroup, Range, SourceInfo} from './ops'; + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L10-L19 +export interface Vertex { + digest?: Digest; + inputs?: Array; + name?: string; + started?: Date; + completed?: Date; + cached?: boolean; + error?: string; + progressGroup?: ProgressGroup; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L21-L30 +export interface VertexStatus { + id: string; + vertex?: Digest; + name?: string; + total?: number; + current: number; + timestamp?: Date; + started?: Date; + completed?: Date; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L32-L37 +export interface VertexLog { + vertex?: Digest; + stream?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any; + timestamp: Date; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L39-L48 +export interface VertexWarning { + vertex?: Digest; + level?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + short?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + detail?: Array; + url?: string; + + sourceInfo?: SourceInfo; + range?: Range[]; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L50-L55 +export interface SolveStatus { + vertexes?: Vertex[]; + statuses?: VertexStatus[]; + logs?: VertexLog[]; + warnings?: VertexWarning[]; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/client/graph.go#L57-L60 +export interface SolveResponse { + exporterResponse: Record; +} diff --git a/src/types/buildkit/control.ts b/src/types/buildkit/control.ts new file mode 100644 index 00000000..18367387 --- /dev/null +++ b/src/types/buildkit/control.ts @@ -0,0 +1,108 @@ +/** + * Copyright 2024 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 {Descriptor} from '../oci/descriptor'; +import {Digest} from '../oci/digest'; +import {ProgressGroup, Range, SourceInfo} from './ops'; +import {RpcStatus} from './rpc'; + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L1504-L1525 +export interface BuildHistoryRecord { + Ref: string; + Frontend: string; + FrontendAttrs: Record; + Exporters: Exporter[]; + error?: RpcStatus; + CreatedAt?: Date; + CompletedAt?: Date; + logs?: Descriptor; + ExporterResponse: Record; + Result?: BuildResultInfo; + Results: Record; + Generation: number; + trace?: Descriptor; + pinned: boolean; + numCachedSteps: number; + numTotalSteps: number; + numCompletedSteps: number; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L1909-L1917 +export interface Exporter { + Type: string; + Attrs: Record; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L1845-L1852 +export interface BuildResultInfo { + ResultDeprecated?: Descriptor; + Attestations?: Descriptor[]; + Results?: Record; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L751-L759 +export interface StatusResponse { + vertexes?: Vertex[]; + statuses?: VertexStatus[]; + logs?: VertexLog[]; + warnings?: VertexWarning[]; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L822-L834 +export interface Vertex { + digest: Digest; + inputs: Digest[]; + name?: string; + cached?: boolean; + started?: Date; + completed?: Date; + error?: string; + progressGroup?: ProgressGroup; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L911-L923 +export interface VertexStatus { + ID?: string; + vertex: Digest; + name?: string; + current?: number; + total?: number; + timestamp: Date; + started?: Date; + completed?: Date; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L1007-L1015 +export interface VertexLog { + vertex: Digest; + timestamp: Date; + stream?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + msg?: any; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/api/services/control/control.pb.go#L1071-L1082 +export interface VertexWarning { + vertex: Digest; + level?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + short?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + detail?: any[]; + url?: string; + info?: SourceInfo; + ranges?: Range[]; +} diff --git a/src/types/buildkit/ops.ts b/src/types/buildkit/ops.ts new file mode 100644 index 00000000..1a2d295f --- /dev/null +++ b/src/types/buildkit/ops.ts @@ -0,0 +1,82 @@ +/** + * Copyright 2024 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. + */ + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1901-L1909 +export interface Definition { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + def?: Array; + metadata: Record; + Source?: Source; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1312-L1323 +export interface OpMetadata { + ignore_cache?: boolean; + description?: Record; + export_cache?: ExportCache; + caps: Record; + progress_group?: ProgressGroup; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1390-L1393 +export interface Source { + locations?: Record; + infos?: Array; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1439-L1441 +export interface Locations { + locations?: Array; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1545-L1548 +export interface Location { + sourceIndex?: number; + ranges?: Array; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1594-L1597 +export interface Range { + start: Position; + end: Position; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1643-L1646 +export interface Position { + line: number; + character: number; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1480-L1485 +export interface SourceInfo { + filename?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?: any; + definition?: Definition; + language?: string; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1691-L1693 +export interface ExportCache { + Value?: boolean; +} + +// https://github.com/moby/buildkit/blob/593aad1ea909b978d2c54fef2a138c6d3a9107e6/solver/pb/ops.pb.go#L1731-L1735 +export interface ProgressGroup { + id?: string; + name?: string; + weak?: boolean; +} diff --git a/src/types/buildkit/rpc.ts b/src/types/buildkit/rpc.ts new file mode 100644 index 00000000..130e3151 --- /dev/null +++ b/src/types/buildkit/rpc.ts @@ -0,0 +1,27 @@ +/** + * Copyright 2024 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 interface RpcStatus { + Code: number; + Message: string; + Details: { + // Define properties based on google.protobuf.Any + // For simplicity, assuming it has at least a type_url and a value. + type_url: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any; + }[]; +} diff --git a/src/types/buildx/buildx.ts b/src/types/buildx/buildx.ts index 6dc5e6fb..123d9142 100644 --- a/src/types/buildx/buildx.ts +++ b/src/types/buildx/buildx.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +import {SolveStatus} from '../buildkit/client'; +import {BuildHistoryRecord} from '../buildkit/control'; +import {ProvenancePredicate} from '../intoto/slsa_provenance/v0.2/provenance'; + export interface Cert { cacert?: string; cert?: string; @@ -44,3 +48,23 @@ export interface LocalState { DockerfilePath: string; GroupRef?: string; } + +export interface StateGroup { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Definition: any; + Targets: Array; + Inputs: Array; + Refs?: Array; +} + +// https://github.com/docker/desktop-build/blob/b609016485f6d37cb22cdfb616c6222c85c30683/tools/export-build/main.go#L48-L54 +export interface ExportedRecord extends BuildHistoryRecord { + localState: LocalState; + stateGroup: StateGroup; + DefaultPlatform: string; +} + +export interface BuildRecord extends ExportedRecord { + solveStatus?: SolveStatus; + provenance?: ProvenancePredicate; +} diff --git a/src/types/buildx/history.ts b/src/types/buildx/history.ts index 67fbe685..5db393de 100644 --- a/src/types/buildx/history.ts +++ b/src/types/buildx/history.ts @@ -42,3 +42,7 @@ export interface RecordSummary { frontendAttrs: Record; error?: string; } + +export interface LoadRecordOpts { + file: string; +} diff --git a/src/types/intoto/intoto.ts b/src/types/intoto/intoto.ts new file mode 100644 index 00000000..0bad854e --- /dev/null +++ b/src/types/intoto/intoto.ts @@ -0,0 +1,20 @@ +/** + * Copyright 2024 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. + */ + +// https://github.com/in-toto/in-toto-golang/blob/dd6278764ab1dae7301609c7510129888e2fd569/in_toto/envelope.go#L17 +export const MEDIATYPE_PAYLOAD = 'application/vnd.in-toto+json'; + +export const MEDIATYPE_PREDICATE = 'in-toto.io/predicate-type'; diff --git a/src/types/intoto/slsa_provenance/v0.2/provenance.ts b/src/types/intoto/slsa_provenance/v0.2/provenance.ts new file mode 100644 index 00000000..832fd38b --- /dev/null +++ b/src/types/intoto/slsa_provenance/v0.2/provenance.ts @@ -0,0 +1,69 @@ +/** + * Copyright 2024 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. + */ + +// https://github.com/in-toto/in-toto-golang/blob/master/in_toto/slsa_provenance/v0.2/provenance.go + +export const PREDICATE_SLSA_PROVENANCE = 'https://slsa.dev/provenance/v0.2'; + +export interface ProvenancePredicate { + builder: ProvenanceBuilder; + buildType: string; + invocation?: ProvenanceInvocation; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + buildConfig?: any; + metadata: ProvenanceMetadata; + materials?: Material[]; +} + +export interface ProvenanceBuilder { + id: string; +} + +export interface ProvenanceInvocation { + configSource?: ConfigSource; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + parameters?: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + environment?: any; +} + +export interface DigestSet { + [key: string]: string; +} + +export interface ConfigSource { + uri?: string; + digest?: DigestSet; + entryPoint?: string; +} + +export interface Completeness { + parameters?: boolean; + environment?: boolean; + materials?: boolean; +} + +export interface ProvenanceMetadata { + buildInvocationId?: string; + buildStartedOn?: string; + completeness?: Completeness; + reproducible?: boolean; +} + +export interface Material { + uri: string; + digest: DigestSet; +}