1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package predicate
16
17 import (
18 "context"
19 "fmt"
20
21 "github.com/pkg/errors"
22
23 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/pull"
24
25 "edge-infra.dev/pkg/f8n/devinfra/repo/owners/policybot/policy/common"
26 )
27
28 type HasValidSignatures bool
29
30 var _ Predicate = HasValidSignatures(false)
31
32 func (pred HasValidSignatures) Evaluate(ctx context.Context, prctx pull.Context) (*common.PredicateResult, error) {
33 commits, err := prctx.Commits()
34
35 predicateResult := common.PredicateResult{
36 ConditionPhrase: "have",
37 ConditionValues: []string{"valid signatures"},
38 }
39
40 if err != nil {
41 return nil, errors.Wrap(err, "failed to get commits")
42 }
43
44 var commitHashes []string
45
46 for _, c := range commits {
47 valid, desc := hasValidSignature(ctx, c)
48 commitHashes = append(commitHashes, c.SHA)
49 if !valid {
50 predicateResult.Values = []string{c.SHA}
51 if pred {
52 predicateResult.Description = desc
53 predicateResult.Satisfied = false
54 return &predicateResult, nil
55 }
56 predicateResult.Satisfied = true
57 return &predicateResult, nil
58 }
59 }
60 predicateResult.Values = commitHashes
61 if pred {
62 predicateResult.Satisfied = true
63 return &predicateResult, nil
64 }
65 predicateResult.Satisfied = false
66 predicateResult.Description = "All commits are signed and have valid signatures"
67 return &predicateResult, nil
68 }
69
70 func (pred HasValidSignatures) Trigger() common.Trigger {
71 return common.TriggerCommit
72 }
73
74 type HasValidSignaturesBy struct {
75 common.Actors `yaml:",inline"`
76 }
77
78 var _ Predicate = &HasValidSignaturesBy{}
79
80 func (pred *HasValidSignaturesBy) Evaluate(ctx context.Context, prctx pull.Context) (*common.PredicateResult, error) {
81 commits, err := prctx.Commits()
82
83 predicateResult := common.PredicateResult{
84 ConditionsMap: map[string][]string{
85 "Organizations": pred.Organizations,
86 "Teams": pred.Teams,
87 "Users": pred.Users,
88 },
89 }
90
91 if err != nil {
92 return nil, errors.Wrap(err, "failed to get commits")
93 }
94
95 signers := make(map[string]string)
96 var commitHashes []string
97
98 for _, c := range commits {
99 valid, desc := hasValidSignature(ctx, c)
100 if !valid {
101 predicateResult.ConditionPhrase = "have valid signatures by members of"
102 predicateResult.ValuePhrase = "commits"
103 predicateResult.Values = []string{c.SHA}
104 predicateResult.Description = desc
105 predicateResult.Satisfied = false
106 return &predicateResult, nil
107 }
108 signers[c.Signature.Signer] = c.SHA
109 commitHashes = append(commitHashes, c.SHA)
110 }
111
112 var signerList []string
113
114 for signer := range signers {
115 signerList = append(signerList, signer)
116 member, err := pred.IsActor(ctx, prctx, signer)
117 if err != nil {
118 return nil, err
119 }
120 if !member {
121 predicateResult.ConditionPhrase = "satisfy the required membership conditions"
122 predicateResult.Values = []string{signer}
123 predicateResult.ValuePhrase = "signers"
124 predicateResult.Description = fmt.Sprintf("Contributor %q does not meet the required membership conditions for signing", signer)
125 predicateResult.Satisfied = false
126 return &predicateResult, nil
127 }
128 }
129 predicateResult.ConditionPhrase = "have valid signatures by members of"
130 predicateResult.Values = commitHashes
131 predicateResult.ValuePhrase = "commits"
132 predicateResult.Satisfied = true
133 return &predicateResult, nil
134 }
135
136 func (pred *HasValidSignaturesBy) Trigger() common.Trigger {
137 return common.TriggerCommit
138 }
139
140 type HasValidSignaturesByKeys struct {
141 KeyIDs []string `yaml:"key_ids"`
142 }
143
144 var _ Predicate = &HasValidSignaturesByKeys{}
145
146 func (pred *HasValidSignaturesByKeys) Evaluate(ctx context.Context, prctx pull.Context) (*common.PredicateResult, error) {
147 commits, err := prctx.Commits()
148
149 predicateResult := common.PredicateResult{
150 ConditionPhrase: "have valid signatures by keys",
151 ConditionValues: pred.KeyIDs,
152 }
153
154 if err != nil {
155 return nil, errors.Wrap(err, "failed to get commits")
156 }
157
158 keys := make(map[string][]string)
159
160 var commitHashes []string
161
162 for _, c := range commits {
163 valid, desc := hasValidSignature(ctx, c)
164 if !valid {
165 predicateResult.Values = []string{c.SHA}
166 predicateResult.Description = desc
167 predicateResult.ValuePhrase = "commits"
168 predicateResult.Satisfied = false
169 return &predicateResult, nil
170 }
171 commitHashes = append(commitHashes, c.SHA)
172
173 switch c.Signature.Type {
174 case pull.SignatureGpg:
175 commitSHAs, ok := keys[c.Signature.KeyID]
176 if ok {
177 keys[c.Signature.KeyID] = append(commitSHAs, c.SHA)
178 } else {
179 keys[c.Signature.KeyID] = []string{c.SHA}
180 }
181 default:
182 predicateResult.Values = []string{c.SHA}
183 predicateResult.ValuePhrase = "commits"
184 predicateResult.ConditionPhrase = "have GPG signatures"
185 predicateResult.Description = fmt.Sprintf("Commit %.10s signature is not a GPG signature", c.SHA)
186 predicateResult.Satisfied = false
187 return &predicateResult, nil
188 }
189 }
190
191 for key := range keys {
192 isValidKey := false
193 for _, acceptedKey := range pred.KeyIDs {
194 if key == acceptedKey {
195 isValidKey = true
196 break
197 }
198 }
199 if !isValidKey {
200 predicateResult.ConditionPhrase = "exist in the set of allowed keys"
201 predicateResult.Values = []string{key}
202 predicateResult.ValuePhrase = "keys"
203 predicateResult.Description = fmt.Sprintf("Key %q does not meet the required key conditions for signing", key)
204 predicateResult.Satisfied = false
205 return &predicateResult, nil
206 }
207 }
208 predicateResult.Values = commitHashes
209 predicateResult.ValuePhrase = "commits"
210 predicateResult.Satisfied = true
211
212 return &predicateResult, nil
213 }
214
215 func (pred *HasValidSignaturesByKeys) Trigger() common.Trigger {
216 return common.TriggerCommit
217 }
218
219 func hasValidSignature(ctx context.Context, commit *pull.Commit) (bool, string) {
220 if commit.Signature == nil {
221 return false, fmt.Sprintf("Commit %.10s has no signature", commit.SHA)
222 }
223 if !commit.Signature.IsValid {
224 reason := commit.Signature.State
225 return false, fmt.Sprintf("Commit %.10s has an invalid signature due to %s", commit.SHA, reason)
226 }
227 return true, ""
228 }
229
View as plain text