Skip to content

Commit 86e3a07

Browse files
committed
vex: allow FixedInVersion rewrite via CEL
This patch allows callers to rewrite FixedInVersions with a CEL expression. Expressions have access to all the PURL data with everything in a StringType except for the qualifiers which are MapType. Signed-off-by: crozzy <joseph.crosland@gmail.com>
1 parent 61dbcd6 commit 86e3a07

6 files changed

Lines changed: 380 additions & 22 deletions

File tree

go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ go 1.25.0
44

55
require (
66
github.com/Masterminds/semver v1.5.0
7+
github.com/cespare/xxhash/v2 v2.3.0
78
github.com/doug-martin/goqu/v8 v8.6.0
9+
github.com/google/cel-go v0.30.0
810
github.com/google/go-cmp v0.7.0
911
github.com/google/uuid v1.6.0
1012
github.com/jackc/pgx/v5 v5.10.0
@@ -35,9 +37,10 @@ require (
3537
)
3638

3739
require (
40+
cel.dev/expr v0.25.1 // indirect
3841
github.com/anchore/go-struct-converter v0.1.0 // indirect
42+
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
3943
github.com/beorn7/perks v1.0.1 // indirect
40-
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4144
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect
4245
github.com/dustin/go-humanize v1.0.1 // indirect
4346
github.com/go-logr/logr v1.4.3 // indirect
@@ -56,7 +59,11 @@ require (
5659
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
5760
github.com/sirupsen/logrus v1.9.4 // indirect
5861
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
62+
go.yaml.in/yaml/v3 v3.0.4 // indirect
63+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect
5964
golang.org/x/mod v0.38.0 // indirect
65+
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 // indirect
66+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
6067
google.golang.org/protobuf v1.36.11 // indirect
6168
modernc.org/libc v1.74.1 // indirect
6269
modernc.org/mathutil v1.7.1 // indirect

go.sum

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
2+
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
13
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
24
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
35
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
46
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
57
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
68
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
9+
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
10+
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
711
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
812
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
913
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -26,6 +30,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
2630
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
2731
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
2832
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
33+
github.com/google/cel-go v0.30.0 h1:ll54AkzKunWkBn9wSoiUXbFZXYZTkdJGNXTBXUoolGo=
34+
github.com/google/cel-go v0.30.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
2935
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
3036
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
3137
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
@@ -49,6 +55,10 @@ github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GX
4955
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8=
5056
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c=
5157
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
58+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
59+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
60+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
61+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
5262
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
5363
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
5464
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -85,6 +95,8 @@ github.com/regclient/regclient v0.11.5 h1:OHRsXO0F3qHGfa4HEUv+EkMH9NXNcCTBKjNzyC
8595
github.com/regclient/regclient v0.11.5/go.mod h1:DZUOfIT14WFTK2Pj4vjd93avy9O4Fdpjrf9ir23TbRE=
8696
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
8797
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
98+
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
99+
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
88100
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
89101
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
90102
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
@@ -114,10 +126,14 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
114126
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
115127
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
116128
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
129+
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
130+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
117131
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
118132
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
119133
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
120134
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
135+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
136+
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
121137
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
122138
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
123139
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -149,9 +165,15 @@ golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk
149165
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
150166
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
151167
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
168+
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw=
169+
google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
170+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
171+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
152172
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
153173
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
154174
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
175+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
176+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
155177
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
156178
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
157179
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

rhel/vex/fixed_in_cel.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package vex
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/cespare/xxhash/v2"
7+
"github.com/google/cel-go/cel"
8+
"github.com/google/cel-go/ext"
9+
"github.com/package-url/packageurl-go"
10+
)
11+
12+
// CompileFixedInVersionCEL compiles a CEL expression that evaluates to a string.
13+
//
14+
// The expression may use these variables:
15+
//
16+
// - type (string): PURL type (oci, rpm, etc.)
17+
// - namespace (string): PURL namespace
18+
// - name (string): PURL name
19+
// - version (string): PURL version field
20+
// - qualifiers (map[string]string): PURL qualifiers
21+
// - fixed_in (string): default FixedInVersion from stock extraction
22+
//
23+
// The environment includes the CEL strings extension (startsWith,
24+
// substring, split etc.) and bindings to be able to set variables.
25+
//
26+
// An empty expression returns a nil program.
27+
// Production expressions are supplied by callers if desired.
28+
func CompileFixedInVersionCEL(expr string) (cel.Program, error) {
29+
if expr == "" {
30+
return nil, nil
31+
}
32+
env, err := fixedInCELEnv()
33+
if err != nil {
34+
return nil, err
35+
}
36+
ast, iss := env.Compile(expr)
37+
if iss.Err() != nil {
38+
return nil, fmt.Errorf("fixed_in_version_cel: compile: %w", iss.Err())
39+
}
40+
if !ast.OutputType().IsExactType(cel.StringType) {
41+
return nil, fmt.Errorf("fixed_in_version_cel: expression must evaluate to string, got %v", ast.OutputType())
42+
}
43+
prog, err := env.Program(ast)
44+
if err != nil {
45+
return nil, fmt.Errorf("fixed_in_version_cel: program: %w", err)
46+
}
47+
return prog, nil
48+
}
49+
50+
// EvalFixedInVersionCEL evaluates a compiled FixedInVersion CEL program.
51+
func EvalFixedInVersionCEL(prog cel.Program, p *packageurl.PackageURL, defaultVersion string) (string, error) {
52+
if prog == nil {
53+
return defaultVersion, nil
54+
}
55+
out, _, err := prog.Eval(map[string]any{
56+
"type": p.Type,
57+
"namespace": p.Namespace,
58+
"name": p.Name,
59+
"version": p.Version,
60+
"qualifiers": p.Qualifiers.Map(),
61+
"fixed_in": defaultVersion,
62+
})
63+
if err != nil {
64+
return "", fmt.Errorf("fixed_in_version_cel: eval: %w", err)
65+
}
66+
s, ok := out.Value().(string)
67+
if !ok {
68+
return "", fmt.Errorf("fixed_in_version_cel: result type %T, want string", out.Value())
69+
}
70+
return s, nil
71+
}
72+
73+
func fixedInCELEnv() (*cel.Env, error) {
74+
return cel.NewEnv(
75+
cel.Variable("type", cel.StringType),
76+
cel.Variable("namespace", cel.StringType),
77+
cel.Variable("name", cel.StringType),
78+
cel.Variable("version", cel.StringType),
79+
cel.Variable("qualifiers", cel.MapType(cel.StringType, cel.StringType)),
80+
cel.Variable("fixed_in", cel.StringType),
81+
ext.Strings(),
82+
ext.Bindings(),
83+
)
84+
}
85+
86+
// CelExprFingerprintDigest returns a hex-encoded xxhash of expr for fingerprinting.
87+
// Empty expr returns an empty string.
88+
func celExprFingerprintDigest(expr string) string {
89+
if expr == "" {
90+
return ""
91+
}
92+
return fmt.Sprintf("%016x", xxhash.Sum64String(expr))
93+
}

rhel/vex/fixed_in_cel_test.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package vex
2+
3+
import (
4+
"testing"
5+
6+
"github.com/package-url/packageurl-go"
7+
)
8+
9+
func TestCompileFixedInVersionCEL(t *testing.T) {
10+
t.Parallel()
11+
12+
testcases := []struct {
13+
name string
14+
expr string
15+
wantNil bool
16+
wantErr bool
17+
}{
18+
{
19+
name: "empty",
20+
expr: "",
21+
wantNil: true,
22+
},
23+
{
24+
name: "invalid",
25+
expr: "this is not valid CEL",
26+
wantErr: true,
27+
},
28+
{
29+
name: "non-string",
30+
expr: "true",
31+
wantErr: true,
32+
},
33+
{
34+
name: "valid",
35+
expr: `fixed_in`,
36+
},
37+
}
38+
for _, tc := range testcases {
39+
t.Run(tc.name, func(t *testing.T) {
40+
prog, err := CompileFixedInVersionCEL(tc.expr)
41+
if tc.wantErr {
42+
if err == nil {
43+
t.Fatal("expected compile error")
44+
}
45+
return
46+
}
47+
if err != nil {
48+
t.Fatalf("compile: %v", err)
49+
}
50+
if tc.wantNil {
51+
if prog != nil {
52+
t.Fatal("expected nil program")
53+
}
54+
return
55+
}
56+
if prog == nil {
57+
t.Fatal("expected non-nil program")
58+
}
59+
})
60+
}
61+
}
62+
63+
// TestEvalFixedInVersionCEL exercises rewriting FixedInVersion via a CEL
64+
// expression that uses PURL fields such as type and qualifiers.
65+
func TestEvalFixedInVersionCEL(t *testing.T) {
66+
t.Parallel()
67+
68+
testcases := []struct {
69+
name string
70+
expr string
71+
purl packageurl.PackageURL
72+
stock string
73+
want string
74+
}{
75+
{
76+
name: "oci with tag2",
77+
// Prefer an alternate qualifier when present; otherwise keep stock fixed_in.
78+
expr: `type == "oci" && has(qualifiers.tag2) ? qualifiers.tag2 : fixed_in`,
79+
purl: packageurl.PackageURL{
80+
Type: packageurl.TypeOCI,
81+
Namespace: "redhat",
82+
Name: "example",
83+
Qualifiers: packageurl.QualifiersFromMap(map[string]string{
84+
"tag": "1.0.0",
85+
"tag2": "2.0.0-from-tag2",
86+
"repository_url": "registry.example/example",
87+
}),
88+
},
89+
stock: "1.0.0",
90+
want: "2.0.0-from-tag2",
91+
},
92+
{
93+
name: "oci without tag2",
94+
expr: `type == "oci" && has(qualifiers.tag2) ? qualifiers.tag2 : fixed_in`,
95+
purl: packageurl.PackageURL{
96+
Type: packageurl.TypeOCI,
97+
Name: "example",
98+
Qualifiers: packageurl.QualifiersFromMap(map[string]string{
99+
"tag": "1.0.0",
100+
}),
101+
},
102+
stock: "1.0.0",
103+
want: "1.0.0",
104+
},
105+
{
106+
name: "rpm unchanged",
107+
expr: `type == "oci" && has(qualifiers.tag2) ? qualifiers.tag2 : fixed_in`,
108+
purl: packageurl.PackageURL{
109+
Type: packageurl.TypeRPM,
110+
Namespace: "redhat",
111+
Name: "bash",
112+
Version: "5.1.8-6.el9",
113+
Qualifiers: packageurl.QualifiersFromMap(map[string]string{
114+
"epoch": "0",
115+
}),
116+
},
117+
stock: "0:5.1.8-6.el9",
118+
want: "0:5.1.8-6.el9",
119+
},
120+
}
121+
for _, tc := range testcases {
122+
t.Run(tc.name, func(t *testing.T) {
123+
prog, err := CompileFixedInVersionCEL(tc.expr)
124+
if err != nil {
125+
t.Fatalf("compile: %v", err)
126+
}
127+
opt, err := WithFixedInVersionCEL(tc.expr)
128+
if err != nil {
129+
t.Fatalf("WithFixedInVersionCEL: %v", err)
130+
}
131+
p := NewParser(opt)
132+
133+
stock, err := extractFixedInVersion(&tc.purl)
134+
if err != nil {
135+
t.Fatalf("stock extract: %v", err)
136+
}
137+
if stock != tc.stock {
138+
t.Fatalf("stock = %q, want %q", stock, tc.stock)
139+
}
140+
got, err := EvalFixedInVersionCEL(prog, &tc.purl, stock)
141+
if err != nil {
142+
t.Fatalf("cel eval: %v", err)
143+
}
144+
if got != tc.want {
145+
t.Fatalf("cel rewrite = %q, want %q", got, tc.want)
146+
}
147+
gotCreator, err := (&creator{fixedInCEL: p.fixedInCEL}).FixedInVersion(&tc.purl)
148+
if err != nil {
149+
t.Fatalf("creator.FixedInVersion: %v", err)
150+
}
151+
if gotCreator != tc.want {
152+
t.Fatalf("creator rewrite = %q, want %q", gotCreator, tc.want)
153+
}
154+
})
155+
}
156+
}
157+
158+
func TestFingerprintVersionCEL(t *testing.T) {
159+
t.Parallel()
160+
base := &Updater{}
161+
withCEL := &Updater{fixedInCELDigest: celExprFingerprintDigest(`fixed_in`)}
162+
otherCEL := &Updater{fixedInCELDigest: celExprFingerprintDigest(`type == "oci" ? fixed_in : fixed_in`)}
163+
164+
if base.fingerprintVersion() != updaterVersion {
165+
t.Fatalf("base = %q, want %q", base.fingerprintVersion(), updaterVersion)
166+
}
167+
if withCEL.fingerprintVersion() == base.fingerprintVersion() {
168+
t.Fatal("CEL fingerprint should differ from base")
169+
}
170+
if withCEL.fingerprintVersion() == otherCEL.fingerprintVersion() {
171+
t.Fatal("different CEL expressions should produce different fingerprints")
172+
}
173+
if d := celExprFingerprintDigest(""); d != "" {
174+
t.Fatalf("empty digest = %q", d)
175+
}
176+
}

0 commit comments

Comments
 (0)