handle attests correctly with provenance and sbom inputs

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2024-03-26 15:31:37 +01:00
parent f8bc7f4600
commit 96acf63e4c
No known key found for this signature in database
GPG key ID: ADE44D8C9D44FBE4
2 changed files with 126 additions and 41 deletions

View file

@ -481,7 +481,7 @@ nproc=3`],
[ [
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
@ -500,7 +500,7 @@ nproc=3`],
[ [
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--provenance", `builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
@ -519,7 +519,7 @@ nproc=3`],
[ [
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--provenance", `mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
@ -538,7 +538,7 @@ nproc=3`],
[ [
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--provenance", 'false', '--attest', 'type=provenance,disabled=true',
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
@ -557,7 +557,7 @@ nproc=3`],
[ [
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--provenance", 'builder-id=foo', '--attest', 'type=provenance,builder-id=foo',
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
@ -620,7 +620,7 @@ nproc=3`],
] ]
], ],
[ [
25, 26,
'0.10.0', '0.10.0',
new Map<string, string>([ new Map<string, string>([
['context', '.'], ['context', '.'],
@ -642,7 +642,7 @@ ANOTHER_SECRET=ANOTHER_SECRET_ENV`]
] ]
], ],
[ [
26, 27,
'0.10.0', '0.10.0',
new Map<string, string>([ new Map<string, string>([
['context', '.'], ['context', '.'],
@ -663,7 +663,7 @@ ANOTHER_SECRET=ANOTHER_SECRET_ENV`]
] ]
], ],
[ [
27, 28,
'0.11.0', '0.11.0',
new Map<string, string>([ new Map<string, string>([
['context', '.'], ['context', '.'],
@ -677,13 +677,13 @@ ANOTHER_SECRET=ANOTHER_SECRET_ENV`]
[ [
'build', 'build',
'--output', 'type=local,dest=./release-out', '--output', 'type=local,dest=./release-out',
"--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
], ],
[ [
28, 29,
'0.12.0', '0.12.0',
new Map<string, string>([ new Map<string, string>([
['context', '.'], ['context', '.'],
@ -701,13 +701,13 @@ ANOTHER_SECRET=ANOTHER_SECRET_ENV`]
'--annotation', 'manifest:example3=yyy', '--annotation', 'manifest:example3=yyy',
'--annotation', 'manifest-descriptor[linux/amd64]:example4=zzz', '--annotation', 'manifest-descriptor[linux/amd64]:example4=zzz',
'--output', 'type=local,dest=./release-out', '--output', 'type=local,dest=./release-out',
"--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
], ],
[ [
29, 30,
'0.12.0', '0.12.0',
new Map<string, string>([ new Map<string, string>([
['context', '.'], ['context', '.'],
@ -721,11 +721,71 @@ ANOTHER_SECRET=ANOTHER_SECRET_ENV`]
'build', 'build',
'--iidfile', path.join(tmpDir, 'iidfile'), '--iidfile', path.join(tmpDir, 'iidfile'),
"--output", `type=image,"name=localhost:5000/name/app:latest,localhost:5000/name/app:foo",push-by-digest=true,name-canonical=true,push=true`, "--output", `type=image,"name=localhost:5000/name/app:latest,localhost:5000/name/app:foo",push-by-digest=true,name-canonical=true,push=true`,
"--provenance", `mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`, '--attest', `type=provenance,mode=min,inline-only=true,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'), '--metadata-file', path.join(tmpDir, 'metadata-file'),
'.' '.'
] ]
],
[
31,
'0.13.1',
new Map<string, string>([
['context', '.'],
['load', 'false'],
['no-cache', 'false'],
['push', 'false'],
['pull', 'false'],
['provenance', 'mode=max'],
['sbom', 'true'],
]),
[
'build',
'--iidfile', path.join(tmpDir, 'iidfile'),
'--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--attest', `type=sbom,disabled=false`,
'--metadata-file', path.join(tmpDir, 'metadata-file'),
'.'
] ]
],
[
32,
'0.13.1',
new Map<string, string>([
['context', '.'],
['load', 'false'],
['no-cache', 'false'],
['push', 'false'],
['pull', 'false'],
['attests', 'type=provenance,mode=min'],
['provenance', 'mode=max'],
]),
[
'build',
'--iidfile', path.join(tmpDir, 'iidfile'),
'--attest', `type=provenance,mode=max,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'),
'.'
]
],
[
33,
'0.13.1',
new Map<string, string>([
['context', '.'],
['load', 'false'],
['no-cache', 'false'],
['push', 'false'],
['pull', 'false'],
['attests', 'type=provenance,mode=min'],
]),
[
'build',
'--iidfile', path.join(tmpDir, 'iidfile'),
'--attest', `type=provenance,mode=min,builder-id=https://github.com/docker/build-push-action/actions/runs/123456789`,
'--metadata-file', path.join(tmpDir, 'metadata-file'),
'.'
]
],
])( ])(
'[%d] given %p with %p as inputs, returns %p', '[%d] given %p with %p as inputs, returns %p',
async (num: number, buildxVersion: string, inputs: Map<string, string>, expected: Array<string>) => { async (num: number, buildxVersion: string, inputs: Map<string, string>, expected: Array<string>) => {

View file

@ -98,13 +98,6 @@ async function getBuildArgs(inputs: Inputs, context: string, toolkit: Toolkit):
if (inputs.allow.length > 0) { if (inputs.allow.length > 0) {
args.push('--allow', inputs.allow.join(',')); args.push('--allow', inputs.allow.join(','));
} }
if (await toolkit.buildx.versionSatisfies('>=0.10.0')) {
await Util.asyncForEach(inputs.attests, async attest => {
args.push('--attest', attest);
});
} else if (inputs.attests.length > 0) {
core.warning("Attestations are only supported by buildx >= 0.10.0; the input 'attests' is ignored.");
}
if (await toolkit.buildx.versionSatisfies('>=0.12.0')) { if (await toolkit.buildx.versionSatisfies('>=0.12.0')) {
await Util.asyncForEach(inputs.annotations, async annotation => { await Util.asyncForEach(inputs.annotations, async annotation => {
args.push('--annotation', annotation); args.push('--annotation', annotation);
@ -157,26 +150,9 @@ async function getBuildArgs(inputs: Inputs, context: string, toolkit: Toolkit):
args.push('--platform', inputs.platforms.join(',')); args.push('--platform', inputs.platforms.join(','));
} }
if (await toolkit.buildx.versionSatisfies('>=0.10.0')) { if (await toolkit.buildx.versionSatisfies('>=0.10.0')) {
if (inputs.provenance) { args.push(...(await getAttestArgs(inputs, toolkit)));
args.push('--provenance', inputs.provenance);
} else if ((await toolkit.buildkit.versionSatisfies(inputs.builder, '>=0.11.0')) && !BuildxInputs.hasDockerExporter(inputs.outputs, inputs.load)) {
// if provenance not specified and BuildKit version compatible for
// attestation, set default provenance. Also needs to make sure user
// doesn't want to explicitly load the image to docker.
if (GitHub.context.payload.repository?.private ?? false) {
// if this is a private repository, we set the default provenance
// attributes being set in buildx: https://github.com/docker/buildx/blob/fb27e3f919dcbf614d7126b10c2bc2d0b1927eb6/build/build.go#L603
args.push('--provenance', BuildxInputs.resolveProvenanceAttrs(`mode=min,inline-only=true`));
} else { } else {
// for a public repository, we set max provenance mode. core.warning("Attestations are only supported by buildx >= 0.10.0; the inputs 'attests', 'provenance' and 'sbom' are ignored.");
args.push('--provenance', BuildxInputs.resolveProvenanceAttrs(`mode=max`));
}
}
if (inputs.sbom) {
args.push('--sbom', inputs.sbom);
}
} else if (inputs.provenance || inputs.sbom) {
core.warning("Attestations are only supported by buildx >= 0.10.0; the inputs 'provenance' and 'sbom' are ignored.");
} }
await Util.asyncForEach(inputs.secrets, async secret => { await Util.asyncForEach(inputs.secrets, async secret => {
try { try {
@ -238,3 +214,52 @@ async function getCommonArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<st
} }
return args; return args;
} }
async function getAttestArgs(inputs: Inputs, toolkit: Toolkit): Promise<Array<string>> {
const args: Array<string> = [];
// check if provenance attestation is set in attests input
let hasAttestProvenance = false;
await Util.asyncForEach(inputs.attests, async (attest: string) => {
if (BuildxInputs.hasAttestationType('provenance', attest)) {
hasAttestProvenance = true;
}
});
let provenanceSet = false;
let sbomSet = false;
if (inputs.provenance) {
args.push('--attest', BuildxInputs.resolveAttestationAttrs(`type=provenance,${inputs.provenance}`));
provenanceSet = true;
} else if (!hasAttestProvenance && (await toolkit.buildkit.versionSatisfies(inputs.builder, '>=0.11.0')) && !BuildxInputs.hasDockerExporter(inputs.outputs, inputs.load)) {
// if provenance not specified in provenance or attests inputs and BuildKit
// version compatible for attestation, set default provenance. Also needs
// to make sure user doesn't want to explicitly load the image to docker.
if (GitHub.context.payload.repository?.private ?? false) {
// if this is a private repository, we set the default provenance
// attributes being set in buildx: https://github.com/docker/buildx/blob/fb27e3f919dcbf614d7126b10c2bc2d0b1927eb6/build/build.go#L603
args.push('--attest', `type=provenance,${BuildxInputs.resolveProvenanceAttrs(`mode=min,inline-only=true`)}`);
} else {
// for a public repository, we set max provenance mode.
args.push('--attest', `type=provenance,${BuildxInputs.resolveProvenanceAttrs(`mode=max`)}`);
}
}
if (inputs.sbom) {
args.push('--attest', BuildxInputs.resolveAttestationAttrs(`type=sbom,${inputs.sbom}`));
sbomSet = true;
}
// set attests but check if provenance or sbom types already set as
// provenance and sbom inputs take precedence over attests input.
await Util.asyncForEach(inputs.attests, async (attest: string) => {
if (!BuildxInputs.hasAttestationType('provenance', attest) && !BuildxInputs.hasAttestationType('sbom', attest)) {
args.push('--attest', BuildxInputs.resolveAttestationAttrs(attest));
} else if (!provenanceSet && BuildxInputs.hasAttestationType('provenance', attest)) {
args.push('--attest', BuildxInputs.resolveProvenanceAttrs(attest));
} else if (!sbomSet && BuildxInputs.hasAttestationType('sbom', attest)) {
args.push('--attest', attest);
}
});
return args;
}