mirror of
https://github.com/containers/podman.git
synced 2025-07-02 08:47:43 +08:00
Preserve all unknown PolicyRequirement fields on (podman image trust set)
We are unmarshaling and re-marshaling JSON, which can _silently_ drop data with the Go design decision.data. Try harder, by using json.RawMessage at least for the data we care about. Alternatively, this could use json.Decoder.DisallowUnknownFields. Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
@ -40,6 +40,18 @@ type repoContent struct {
|
|||||||
SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"`
|
SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// genericPolicyContent is the overall structure of a policy.json file (= c/image/v5/signature.Policy), using generic data for individual requirements.
|
||||||
|
type genericPolicyContent struct {
|
||||||
|
Default json.RawMessage `json:"default"`
|
||||||
|
Transports genericTransportsContent `json:"transports,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// genericTransportsContent contains policies for individual transports (= c/image/v5/signature.Policy.Transports), using generic data for individual requirements.
|
||||||
|
type genericTransportsContent map[string]genericRepoMap
|
||||||
|
|
||||||
|
// genericRepoMap maps a scope name to requirements that apply to that scope (= c/image/v5/signature.PolicyTransportScopes)
|
||||||
|
type genericRepoMap map[string]json.RawMessage
|
||||||
|
|
||||||
// DefaultPolicyPath returns a path to the default policy of the system.
|
// DefaultPolicyPath returns a path to the default policy of the system.
|
||||||
func DefaultPolicyPath(sys *types.SystemContext) string {
|
func DefaultPolicyPath(sys *types.SystemContext) string {
|
||||||
systemDefaultPolicyPath := "/etc/containers/policy.json"
|
systemDefaultPolicyPath := "/etc/containers/policy.json"
|
||||||
@ -152,7 +164,7 @@ type AddPolicyEntriesInput struct {
|
|||||||
// AddPolicyEntries adds one or more policy entries necessary to implement AddPolicyEntriesInput.
|
// AddPolicyEntries adds one or more policy entries necessary to implement AddPolicyEntriesInput.
|
||||||
func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
|
func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
|
||||||
var (
|
var (
|
||||||
policyContentStruct policyContent
|
policyContentStruct genericPolicyContent
|
||||||
newReposContent []repoContent
|
newReposContent []repoContent
|
||||||
)
|
)
|
||||||
trustType := input.Type
|
trustType := input.Type
|
||||||
@ -188,8 +200,12 @@ func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown trust type %q", input.Type)
|
return fmt.Errorf("unknown trust type %q", input.Type)
|
||||||
}
|
}
|
||||||
|
newReposJSON, err := json.Marshal(newReposContent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
_, err := os.Stat(policyPath)
|
_, err = os.Stat(policyPath)
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
policyContent, err := ioutil.ReadFile(policyPath)
|
policyContent, err := ioutil.ReadFile(policyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -200,7 +216,7 @@ func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if input.Scope == "default" {
|
if input.Scope == "default" {
|
||||||
policyContentStruct.Default = newReposContent
|
policyContentStruct.Default = json.RawMessage(newReposJSON)
|
||||||
} else {
|
} else {
|
||||||
if len(policyContentStruct.Default) == 0 {
|
if len(policyContentStruct.Default) == 0 {
|
||||||
return errors.New("default trust policy must be set")
|
return errors.New("default trust policy must be set")
|
||||||
@ -209,18 +225,18 @@ func AddPolicyEntries(policyPath string, input AddPolicyEntriesInput) error {
|
|||||||
for transport, transportval := range policyContentStruct.Transports {
|
for transport, transportval := range policyContentStruct.Transports {
|
||||||
_, registryExists = transportval[input.Scope]
|
_, registryExists = transportval[input.Scope]
|
||||||
if registryExists {
|
if registryExists {
|
||||||
policyContentStruct.Transports[transport][input.Scope] = newReposContent
|
policyContentStruct.Transports[transport][input.Scope] = json.RawMessage(newReposJSON)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !registryExists {
|
if !registryExists {
|
||||||
if policyContentStruct.Transports == nil {
|
if policyContentStruct.Transports == nil {
|
||||||
policyContentStruct.Transports = make(map[string]repoMap)
|
policyContentStruct.Transports = make(map[string]genericRepoMap)
|
||||||
}
|
}
|
||||||
if policyContentStruct.Transports["docker"] == nil {
|
if policyContentStruct.Transports["docker"] == nil {
|
||||||
policyContentStruct.Transports["docker"] = make(map[string][]repoContent)
|
policyContentStruct.Transports["docker"] = make(map[string]json.RawMessage)
|
||||||
}
|
}
|
||||||
policyContentStruct.Transports["docker"][input.Scope] = append(policyContentStruct.Transports["docker"][input.Scope], newReposContent...)
|
policyContentStruct.Transports["docker"][input.Scope] = json.RawMessage(newReposJSON)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +108,70 @@ func TestAddPolicyEntries(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, parsedPolicy)
|
}, parsedPolicy)
|
||||||
|
|
||||||
|
// Test that completely unknown JSON is preserved
|
||||||
|
jsonWithUnknownData := `{
|
||||||
|
"default": [
|
||||||
|
{
|
||||||
|
"type": "this is unknown",
|
||||||
|
"unknown field": "should be preserved"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transports":
|
||||||
|
{
|
||||||
|
"docker-daemon":
|
||||||
|
{
|
||||||
|
"": [{
|
||||||
|
"type":"this is unknown 2",
|
||||||
|
"unknown field 2": "should be preserved 2"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
err = os.WriteFile(policyPath, []byte(jsonWithUnknownData), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = AddPolicyEntries(policyPath, AddPolicyEntriesInput{
|
||||||
|
Scope: "quay.io/innocuous",
|
||||||
|
Type: "signedBy",
|
||||||
|
PubKeyFiles: []string{"/1.pub"},
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
updatedJSONWithUnknownData, err := os.ReadFile(policyPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// Decode updatedJSONWithUnknownData so that this test does not depend on details of the encoding.
|
||||||
|
// To reduce noise in the constants below:
|
||||||
|
type a = []interface{}
|
||||||
|
type m = map[string]interface{}
|
||||||
|
var parsedUpdatedJSON m
|
||||||
|
err = json.Unmarshal(updatedJSONWithUnknownData, &parsedUpdatedJSON)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, m{
|
||||||
|
"default": a{
|
||||||
|
m{
|
||||||
|
"type": "this is unknown",
|
||||||
|
"unknown field": "should be preserved",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"transports": m{
|
||||||
|
"docker-daemon": m{
|
||||||
|
"": a{
|
||||||
|
m{
|
||||||
|
"type": "this is unknown 2",
|
||||||
|
"unknown field 2": "should be preserved 2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"docker": m{
|
||||||
|
"quay.io/innocuous": a{
|
||||||
|
m{
|
||||||
|
"type": "signedBy",
|
||||||
|
"keyType": "GPGKeys",
|
||||||
|
"keyPath": "/1.pub",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, parsedUpdatedJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// xNewPRSignedByKeyPath is a wrapper for NewPRSignedByKeyPath which must not fail.
|
// xNewPRSignedByKeyPath is a wrapper for NewPRSignedByKeyPath which must not fail.
|
||||||
|
Reference in New Issue
Block a user