summaryrefslogtreecommitdiff
path: root/pkg/trust/trust.go
blob: 07d144bc112ca822c738443392f89c47b907bd56 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package trust

import (
	"fmt"
	"sort"
	"strings"
)

// Policy describes a basic trust policy configuration
type Policy struct {
	Transport      string   `json:"transport"`
	Name           string   `json:"name,omitempty"`
	RepoName       string   `json:"repo_name,omitempty"`
	Keys           []string `json:"keys,omitempty"`
	SignatureStore string   `json:"sigstore,omitempty"`
	Type           string   `json:"type"`
	GPGId          string   `json:"gpg_id,omitempty"`
}

// PolicyDescription returns an user-focused description of the policy in policyPath and registries.d data from registriesDirPath.
func PolicyDescription(policyPath, registriesDirPath string) ([]*Policy, error) {
	return policyDescriptionWithGPGIDReader(policyPath, registriesDirPath, getGPGIdFromKeyPath)
}

// policyDescriptionWithGPGIDReader is PolicyDescription with a gpgIDReader parameter. It exists only to make testing easier.
func policyDescriptionWithGPGIDReader(policyPath, registriesDirPath string, idReader gpgIDReader) ([]*Policy, error) {
	policyContentStruct, err := getPolicy(policyPath)
	if err != nil {
		return nil, fmt.Errorf("could not read trust policies: %w", err)
	}
	res, err := getPolicyShowOutput(policyContentStruct, registriesDirPath, idReader)
	if err != nil {
		return nil, fmt.Errorf("could not show trust policies: %w", err)
	}
	return res, nil
}

func getPolicyShowOutput(policyContentStruct policyContent, systemRegistriesDirPath string, idReader gpgIDReader) ([]*Policy, error) {
	var output []*Policy

	registryConfigs, err := loadAndMergeConfig(systemRegistriesDirPath)
	if err != nil {
		return nil, err
	}

	if len(policyContentStruct.Default) > 0 {
		template := Policy{
			Transport: "all",
			Name:      "* (default)",
			RepoName:  "default",
		}
		output = append(output, descriptionsOfPolicyRequirements(policyContentStruct.Default, template, registryConfigs, "", idReader)...)
	}
	// FIXME: This should use x/exp/maps.Keys after we update to Go 1.18.
	transports := []string{}
	for t := range policyContentStruct.Transports {
		transports = append(transports, t)
	}
	sort.Strings(transports)
	for _, transport := range transports {
		transval := policyContentStruct.Transports[transport]
		if transport == "docker" {
			transport = "repository"
		}

		// FIXME: This should use x/exp/maps.Keys after we update to Go 1.18.
		scopes := []string{}
		for s := range transval {
			scopes = append(scopes, s)
		}
		sort.Strings(scopes)
		for _, repo := range scopes {
			repoval := transval[repo]
			template := Policy{
				Transport: transport,
				Name:      repo,
				RepoName:  repo,
			}
			output = append(output, descriptionsOfPolicyRequirements(repoval, template, registryConfigs, repo, idReader)...)
		}
	}
	return output, nil
}

// descriptionsOfPolicyRequirements turns reqs into user-readable policy entries, with Transport/Name/Reponame coming from template, potentially looking up scope (which may be "") in registryConfigs.
func descriptionsOfPolicyRequirements(reqs []repoContent, template Policy, registryConfigs *registryConfiguration, scope string, idReader gpgIDReader) []*Policy {
	res := []*Policy{}

	var lookasidePath string
	registryNamespace := registriesDConfigurationForScope(registryConfigs, scope)
	if registryNamespace != nil {
		if registryNamespace.Lookaside != "" {
			lookasidePath = registryNamespace.Lookaside
		} else { // incl. registryNamespace.SigStore == ""
			lookasidePath = registryNamespace.SigStore
		}
	}

	for _, repoele := range reqs {
		entry := template
		entry.Type = trustTypeDescription(repoele.Type)

		var gpgIDString string
		switch repoele.Type {
		case "signedBy":
			uids := []string{}
			if len(repoele.KeyPath) > 0 {
				uids = append(uids, idReader(repoele.KeyPath)...)
			}
			for _, path := range repoele.KeyPaths {
				uids = append(uids, idReader(path)...)
			}
			if len(repoele.KeyData) > 0 {
				uids = append(uids, getGPGIdFromKeyData(idReader, repoele.KeyData)...)
			}
			gpgIDString = strings.Join(uids, ", ")

		case "sigstoreSigned":
			gpgIDString = "N/A" // We could potentially return key fingerprints here, but they would not be _GPG_ fingerprints.
		}
		entry.GPGId = gpgIDString
		entry.SignatureStore = lookasidePath // We do this even for sigstoreSigned and things like type: reject, to show that the sigstore is being read.
		res = append(res, &entry)
	}

	return res
}