Skip to content

Commit 1445ae1

Browse files
committed
provenance: avoid intermediate wrapper for custom fields
While the custom fields used embedded Go struct field, this does not work for map types and needs custom JSON marshaller to make sure custom fields appear without wrapper. Signed-off-by: Tonis Tiigi <[email protected]>
1 parent fe65d5f commit 1445ae1

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

solver/llbsolver/provenance/types/types.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package types
22

33
import (
4+
"encoding/json"
45
"slices"
56

67
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
@@ -311,3 +312,103 @@ func (p *ProvenancePredicateSLSA02) ConvertToSLSA1() *ProvenancePredicateSLSA1 {
311312
RunDetails: runDetails,
312313
}
313314
}
315+
316+
// MarshalJSON flattens ProvenanceCustomEnv into top level.
317+
func (p ProvenanceInternalParametersSLSA1) MarshalJSON() ([]byte, error) {
318+
type Alias ProvenanceInternalParametersSLSA1
319+
base, err := json.Marshal(Alias(p))
320+
if err != nil {
321+
return nil, err
322+
}
323+
324+
var m map[string]any
325+
if err := json.Unmarshal(base, &m); err != nil {
326+
return nil, err
327+
}
328+
329+
for k, v := range p.ProvenanceCustomEnv {
330+
m[k] = v
331+
}
332+
delete(m, "ProvenanceCustomEnv")
333+
return json.Marshal(m)
334+
}
335+
336+
// UnmarshalJSON fills both struct fields and flattened custom env.
337+
func (p *ProvenanceInternalParametersSLSA1) UnmarshalJSON(data []byte) error {
338+
var m map[string]any
339+
if err := json.Unmarshal(data, &m); err != nil {
340+
return err
341+
}
342+
343+
type Alias ProvenanceInternalParametersSLSA1
344+
var a Alias
345+
if err := json.Unmarshal(data, &a); err != nil {
346+
return err
347+
}
348+
349+
// Unmarshal known struct again to identify its keys
350+
structBytes, err := json.Marshal(a)
351+
if err != nil {
352+
return err
353+
}
354+
var known map[string]any
355+
if err := json.Unmarshal(structBytes, &known); err != nil {
356+
return err
357+
}
358+
359+
for k := range known {
360+
delete(m, k)
361+
}
362+
363+
*p = ProvenanceInternalParametersSLSA1(a)
364+
p.ProvenanceCustomEnv = m
365+
return nil
366+
}
367+
368+
func (p Environment) MarshalJSON() ([]byte, error) {
369+
type Alias Environment
370+
base, err := json.Marshal(Alias(p))
371+
if err != nil {
372+
return nil, err
373+
}
374+
375+
var m map[string]any
376+
if err := json.Unmarshal(base, &m); err != nil {
377+
return nil, err
378+
}
379+
380+
for k, v := range p.ProvenanceCustomEnv {
381+
m[k] = v
382+
}
383+
delete(m, "ProvenanceCustomEnv")
384+
return json.Marshal(m)
385+
}
386+
387+
func (p *Environment) UnmarshalJSON(data []byte) error {
388+
var m map[string]any
389+
if err := json.Unmarshal(data, &m); err != nil {
390+
return err
391+
}
392+
393+
type Alias Environment
394+
var a Alias
395+
if err := json.Unmarshal(data, &a); err != nil {
396+
return err
397+
}
398+
// Unmarshal known struct again to identify its keys
399+
structBytes, err := json.Marshal(a)
400+
if err != nil {
401+
return err
402+
}
403+
var known map[string]any
404+
if err := json.Unmarshal(structBytes, &known); err != nil {
405+
return err
406+
}
407+
408+
for k := range known {
409+
delete(m, k)
410+
}
411+
*p = Environment(a)
412+
p.ProvenanceCustomEnv = m
413+
return nil
414+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package types
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestMarsalBuildDefinitionSLSA1(t *testing.T) {
11+
inp := `{
12+
"buildType": "btype1",
13+
"externalParameters": {
14+
"configSource": {},
15+
"request": {}
16+
},
17+
"internalParameters": {
18+
"builderPlatform": "linux/amd64",
19+
"foo": "bar",
20+
"abc": 123,
21+
"def": {"one": 1}
22+
}
23+
}`
24+
25+
var def ProvenanceBuildDefinitionSLSA1
26+
err := json.Unmarshal([]byte(inp), &def)
27+
require.NoError(t, err)
28+
29+
require.Equal(t, "btype1", def.BuildType)
30+
require.Equal(t, "linux/amd64", def.InternalParameters.BuilderPlatform)
31+
require.Equal(t, "bar", def.InternalParameters.ProvenanceCustomEnv["foo"])
32+
require.InEpsilon(t, float64(123), def.InternalParameters.ProvenanceCustomEnv["abc"], 0.001)
33+
require.Equal(t, map[string]any{"one": float64(1)}, def.InternalParameters.ProvenanceCustomEnv["def"])
34+
35+
out, err := json.Marshal(def)
36+
require.NoError(t, err)
37+
38+
require.JSONEq(t, inp, string(out))
39+
}
40+
41+
func TestMarshalInvocation(t *testing.T) {
42+
inp := `{
43+
"configSource": {
44+
"uri": "git+https://github.com/example/repo.git"
45+
},
46+
"parameters": {
47+
"frontend": "dockerfile.v0"
48+
},
49+
"environment": {
50+
"platform": "linux/amd64",
51+
"buildkit": "v0.10.3",
52+
"custom": {
53+
"foo": "bar"
54+
},
55+
"bar": [1,2,3]
56+
}
57+
}`
58+
59+
var inv ProvenanceInvocationSLSA02
60+
err := json.Unmarshal([]byte(inp), &inv)
61+
require.NoError(t, err)
62+
63+
require.Equal(t, "git+https://github.com/example/repo.git", inv.ConfigSource.URI)
64+
require.Equal(t, "dockerfile.v0", inv.Parameters.Frontend)
65+
require.Equal(t, "linux/amd64", inv.Environment.Platform)
66+
require.Equal(t, "v0.10.3", inv.Environment.ProvenanceCustomEnv["buildkit"])
67+
require.Equal(t, "bar", inv.Environment.ProvenanceCustomEnv["custom"].(map[string]any)["foo"])
68+
require.Equal(t, []any{float64(1), float64(2), float64(3)}, inv.Environment.ProvenanceCustomEnv["bar"])
69+
out, err := json.Marshal(inv)
70+
require.NoError(t, err)
71+
72+
require.JSONEq(t, inp, string(out))
73+
}

0 commit comments

Comments
 (0)