Skip to content
This repository was archived by the owner on Dec 3, 2024. It is now read-only.

Commit 4ca962b

Browse files
authored
Add function for parsing DOI definition files (#172)
Add a Rego builtin called `attest.internals.parse_library_definition` for parsing the DOI definition files in https://github.com/docker-library/official-images/tree/master/library. This will allow us to verify DOI provenance fields against these files which are the source of truth for DOI images. This function just defers to https://github.com/docker-library/bashbrew/blob/master/manifest/rfc2822.go.
1 parent 2a4bef0 commit 4ca962b

File tree

8 files changed

+144
-43
lines changed

8 files changed

+144
-43
lines changed

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/containerd/containerd/v2 v2.0.0-rc.4
1010
github.com/containerd/platforms v0.2.1
1111
github.com/distribution/reference v0.6.0
12+
github.com/docker-library/bashbrew v0.1.12
1213
github.com/go-openapi/runtime v0.28.0
1314
github.com/go-openapi/strfmt v0.23.0
1415
github.com/google/go-containerregistry v0.20.2
@@ -39,6 +40,7 @@ require (
3940
cloud.google.com/go/iam v1.2.0 // indirect
4041
cloud.google.com/go/kms v1.19.0 // indirect
4142
cloud.google.com/go/longrunning v0.6.0 // indirect
43+
github.com/Microsoft/hcsshim v0.12.6 // indirect
4244
github.com/OneOfOne/xxhash v1.2.8 // indirect
4345
github.com/ProtonMail/go-crypto v1.0.0 // indirect
4446
github.com/agnivade/levenshtein v1.1.1 // indirect
@@ -62,6 +64,7 @@ require (
6264
github.com/blang/semver v3.5.1+incompatible // indirect
6365
github.com/cespare/xxhash/v2 v2.3.0 // indirect
6466
github.com/cloudflare/circl v1.3.8 // indirect
67+
github.com/containerd/containerd v1.7.21 // indirect
6568
github.com/containerd/errdefs v0.1.0 // indirect
6669
github.com/containerd/log v0.1.0 // indirect
6770
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
@@ -177,4 +180,6 @@ require (
177180
gopkg.in/yaml.v3 v3.0.1 // indirect
178181
gotest.tools/v3 v3.5.1 // indirect
179182
k8s.io/klog/v2 v2.130.1 // indirect
183+
pault.ag/go/debian v0.12.0 // indirect
184+
pault.ag/go/topsort v0.1.1 // indirect
180185
)

go.sum

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,13 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
5454
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
5555
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
5656
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
57+
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
5758
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
5859
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
5960
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
6061
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
62+
github.com/Microsoft/hcsshim v0.12.6 h1:qEnZjoHXv+4/s0LmKZWE0/AiZmMWEIkFfWBSf1a0wlU=
63+
github.com/Microsoft/hcsshim v0.12.6/go.mod h1:ZABCLVcvLMjIkzr9rUGcQ1QA0p0P3Ps+d3N1g2DsFfk=
6164
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
6265
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
6366
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
@@ -172,6 +175,8 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
172175
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
173176
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
174177
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
178+
github.com/containerd/containerd v1.7.21 h1:USGXRK1eOC/SX0L195YgxTHb0a00anxajOzgfN0qrCA=
179+
github.com/containerd/containerd v1.7.21/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g=
175180
github.com/containerd/containerd/v2 v2.0.0-rc.4 h1:Bvto4h5i2VZkQ+L5SrGupg5ilQ+zkVPILdjf9RWMego=
176181
github.com/containerd/containerd/v2 v2.0.0-rc.4/go.mod h1:p35nJi4Pl9ibzuoVOPc3MputVh6Gbp9xoDg9VHz6/YI=
177182
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
@@ -208,6 +213,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
208213
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
209214
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
210215
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
216+
github.com/docker-library/bashbrew v0.1.12 h1:qykd2fxTMiudN/70XItEQqgk/7LeVoDiBTEnKTpkst8=
217+
github.com/docker-library/bashbrew v0.1.12/go.mod h1:6fyRRSm4vgBAgTw87EsfOT7wXKsc4JA9I5cdQJmwOm8=
211218
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
212219
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
213220
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -401,6 +408,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
401408
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
402409
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
403410
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
411+
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d/go.mod h1:phT/jsRPBAEqjAibu1BurrabCBNTYiVI+zbmyCZJY6Q=
404412
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
405413
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
406414
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -591,6 +599,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMc
591599
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
592600
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
593601
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
602+
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
594603
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
595604
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
596605
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -632,6 +641,7 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
632641
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
633642
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
634643
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
644+
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
635645
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
636646
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
637647
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
@@ -806,6 +816,11 @@ k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/A
806816
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
807817
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
808818
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
819+
pault.ag/go/debian v0.12.0 h1:b8ctSdBSGJ98NE1VLn06aSx70EUpczlP2qqSHEiYYJA=
820+
pault.ag/go/debian v0.12.0/go.mod h1:UbnMr3z/KZepjq7VzbYgBEfz8j4+Pyrm2L5X1fzhy/k=
821+
pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a/go.mod h1:INqx0ClF7kmPAMk2zVTX8DRnhZ/yaA/Mg52g8KFKE7k=
822+
pault.ag/go/topsort v0.1.1 h1:L0QnhUly6LmTv0e3DEzbN2q6/FGgAcQvaEw65S53Bg4=
823+
pault.ag/go/topsort v0.1.1/go.mod h1:r1kc/L0/FZ3HhjezBIPaNVhkqv8L0UJ9bxRuHRVZ0q4=
809824
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
810825
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
811826
sigs.k8s.io/release-utils v0.8.4 h1:4QVr3UgbyY/d9p74LBhg0njSVQofUsAZqYOzVZBhdBw=

policy/rego.go

Lines changed: 72 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package policy
22

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"fmt"
78
"os"
89
"path/filepath"
910

11+
"github.com/docker-library/bashbrew/manifest"
1012
"github.com/docker/attest/attestation"
1113
intoto "github.com/in-toto/in-toto-golang/in_toto"
1214
"github.com/open-policy-agent/opa/ast"
@@ -149,6 +151,12 @@ var attestDecl = &ast.Builtin{
149151
Nondeterministic: true,
150152
}
151153

154+
var internalParseLibraryDefinitionDecl = &ast.Builtin{
155+
Name: "attest.internals.parse_library_definition",
156+
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
157+
Nondeterministic: false,
158+
}
159+
152160
func wrapFunctionResult(value *ast.Term, err error) (*ast.Term, error) {
153161
var terms [][2]*ast.Term
154162
if err != nil {
@@ -174,28 +182,50 @@ func handleErrors2(f func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term,
174182

175183
func RegoFunctions(regoOpts *RegoFnOpts) []*tester.Builtin {
176184
return []*tester.Builtin{
177-
{
178-
Decl: verifyDecl,
179-
Func: rego.Function2(
180-
&rego.Function{
181-
Name: verifyDecl.Name,
182-
Decl: verifyDecl.Decl,
183-
Memoize: true,
184-
Nondeterministic: verifyDecl.Nondeterministic,
185-
},
186-
handleErrors2(regoOpts.verifyInTotoEnvelope)),
187-
},
188-
{
189-
Decl: attestDecl,
190-
Func: rego.Function1(
191-
&rego.Function{
192-
Name: attestDecl.Name,
193-
Decl: attestDecl.Decl,
194-
Memoize: true,
195-
Nondeterministic: attestDecl.Nondeterministic,
196-
},
197-
handleErrors1(regoOpts.fetchInTotoAttestations)),
198-
},
185+
builtin2(verifyDecl, regoOpts.verifyInTotoEnvelope),
186+
builtin1(attestDecl, regoOpts.fetchInTotoAttestations),
187+
builtin1(internalParseLibraryDefinitionDecl, regoOpts.internalParseLibraryDefinition),
188+
}
189+
}
190+
191+
func builtin1(decl *ast.Builtin, f rego.Builtin1) *tester.Builtin {
192+
return &tester.Builtin{
193+
Decl: decl,
194+
Func: rego.Function1(
195+
&rego.Function{
196+
Name: decl.Name,
197+
Decl: decl.Decl,
198+
Memoize: true,
199+
Nondeterministic: decl.Nondeterministic,
200+
},
201+
handleErrors1(f)),
202+
}
203+
}
204+
205+
func builtin2(decl *ast.Builtin, f rego.Builtin2) *tester.Builtin {
206+
return &tester.Builtin{
207+
Decl: decl,
208+
Func: rego.Function2(
209+
&rego.Function{
210+
Name: decl.Name,
211+
Decl: decl.Decl,
212+
Memoize: true,
213+
Nondeterministic: decl.Nondeterministic,
214+
},
215+
handleErrors2(f)),
216+
}
217+
}
218+
219+
type RegoFnOpts struct {
220+
attestationResolver attestation.Resolver
221+
attestationVerifier attestation.Verifier
222+
}
223+
224+
// this is exported for testing here and in clients of the library.
225+
func NewRegoFunctionOptions(resolver attestation.Resolver, verifier attestation.Verifier) *RegoFnOpts {
226+
return &RegoFnOpts{
227+
attestationResolver: resolver,
228+
attestationVerifier: verifier,
199229
}
200230
}
201231

@@ -229,19 +259,6 @@ func (regoOpts *RegoFnOpts) fetchInTotoAttestations(rCtx rego.BuiltinContext, pr
229259
return set, nil
230260
}
231261

232-
type RegoFnOpts struct {
233-
attestationResolver attestation.Resolver
234-
attestationVerifier attestation.Verifier
235-
}
236-
237-
// this is exported for testing here and in clients of the library.
238-
func NewRegoFunctionOptions(resolver attestation.Resolver, verifier attestation.Verifier) *RegoFnOpts {
239-
return &RegoFnOpts{
240-
attestationResolver: resolver,
241-
attestationVerifier: verifier,
242-
}
243-
}
244-
245262
// because we don't control the signature here (blame rego)
246263
// nolint:gocritic
247264
func (regoOpts *RegoFnOpts) verifyInTotoEnvelope(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
@@ -285,6 +302,26 @@ func (regoOpts *RegoFnOpts) verifyInTotoEnvelope(rCtx rego.BuiltinContext, envTe
285302
return ast.NewTerm(value), nil
286303
}
287304

305+
// because we don't control the signature here (blame rego)
306+
// nolint:gocritic
307+
func (regoOpts *RegoFnOpts) internalParseLibraryDefinition(_ rego.BuiltinContext, definitionTerm *ast.Term) (*ast.Term, error) {
308+
definitionStr, ok := definitionTerm.Value.(ast.String)
309+
if !ok {
310+
return nil, fmt.Errorf("predicateTypeTerm is not a string")
311+
}
312+
definition := string(definitionStr)
313+
defBuffer := bytes.NewBufferString(definition)
314+
parsed, err := manifest.Parse2822(defBuffer)
315+
if err != nil {
316+
return nil, err
317+
}
318+
value, err := ast.InterfaceToValue(parsed)
319+
if err != nil {
320+
return nil, err
321+
}
322+
return ast.NewTerm(value), nil
323+
}
324+
288325
func loadYAML(path string, bs []byte) (interface{}, error) {
289326
var x interface{}
290327
bs, err := yaml.YAMLToJSON(bs)

policy/rego_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
func TestPolicy(t *testing.T) {
15-
paths := []string{"testdata/policies/test"}
15+
paths := []string{"testdata/policies/test/fetch"}
1616
modules, store, err := tester.Load(paths, nil)
1717
require.NoError(t, err)
1818
resolver := &NullAttestationResolver{}
@@ -35,6 +35,30 @@ func TestPolicy(t *testing.T) {
3535
assert.True(t, resolver.called)
3636
}
3737

38+
func TestPolicyDefParse(t *testing.T) {
39+
paths := []string{"testdata/policies/test/def_parse"}
40+
modules, store, err := tester.Load(paths, nil)
41+
require.NoError(t, err)
42+
resolver := &NullAttestationResolver{}
43+
44+
opts := NewRegoFunctionOptions(resolver, nil)
45+
ctx := context.Background()
46+
ch, err := tester.NewRunner().
47+
SetStore(store).
48+
AddCustomBuiltins(RegoFunctions(opts)).
49+
CapturePrintOutput(true).
50+
RaiseBuiltinErrors(true).
51+
EnableTracing(true).
52+
SetModules(modules).
53+
RunTests(ctx, nil)
54+
require.NoError(t, err)
55+
require.NoError(t, err)
56+
results := buffer(ch)
57+
t.Log(string(results[0].Output))
58+
assert.Equalf(t, 1, len(results), "expected 1 results, got %d", len(results))
59+
assert.Truef(t, results[0].Pass(), "expected result 1 to pass, got %v", results[0].Location)
60+
}
61+
3862
func buffer[T any](ch chan T) []T {
3963
var out []T
4064
for v := range ch {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package def_parse_test
2+
3+
import rego.v1
4+
5+
test_parse_library_definition if {
6+
def := `Maintainers: me <me@example.com> (@me)
7+
GitRepo: blah
8+
9+
Tags: 1, 2, 3
10+
GitCommit: fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688
11+
File: Dockerfile.foo`
12+
result := attest.internals.parse_library_definition(def)
13+
definition := result.value
14+
definition.Entries[0].GitRepo == "blah"
15+
definition.Entries[0].GitCommit == "fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688"
16+
definition.Entries[0].Tags == ["1", "2", "3"]
17+
definition.Entries[0].File == "Dockerfile.foo"
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package attest_test
2+
3+
import rego.v1
4+
5+
import data.attest
6+
7+
test_sucess if {
8+
attest.success
9+
}

policy/testdata/policies/test/fetch_test.rego

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)