Skip to content

Commit c14c4fc

Browse files
committed
Enable users to ignore envars in signatures
1 parent 5759dc0 commit c14c4fc

2 files changed

Lines changed: 123 additions & 6 deletions

File tree

signature/sign.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ type SignedFielder interface {
4343
type Logger interface{ Debug(f string, v ...any) }
4444

4545
type options struct {
46-
env map[string]string
47-
logger Logger
48-
debugSigning bool
46+
env map[string]string
47+
logger Logger
48+
debugSigning bool
49+
ignoredEnvVars []string
4950
}
5051

5152
type Option interface {
@@ -55,12 +56,15 @@ type Option interface {
5556
type envOption struct{ env map[string]string }
5657
type loggerOption struct{ logger Logger }
5758
type debugSigningOption struct{ debugSigning bool }
59+
type ignoringEnvVarsOption struct{ varKeys []string }
5860

59-
func (o envOption) apply(opts *options) { opts.env = o.env }
60-
func (o loggerOption) apply(opts *options) { opts.logger = o.logger }
61-
func (o debugSigningOption) apply(opts *options) { opts.debugSigning = o.debugSigning }
61+
func (o envOption) apply(opts *options) { opts.env = o.env }
62+
func (o loggerOption) apply(opts *options) { opts.logger = o.logger }
63+
func (o debugSigningOption) apply(opts *options) { opts.debugSigning = o.debugSigning }
64+
func (o ignoringEnvVarsOption) apply(opts *options) { opts.ignoredEnvVars = o.varKeys }
6265

6366
func WithEnv(env map[string]string) Option { return envOption{env} }
67+
func IgnoringEnvVars(varKeys ...string) Option { return ignoringEnvVarsOption{varKeys} }
6468
func WithLogger(logger Logger) Option { return loggerOption{logger} }
6569
func WithDebugSigning(debugSigning bool) Option { return debugSigningOption{debugSigning} }
6670

@@ -99,6 +103,15 @@ func Sign(_ context.Context, key Key, sf SignedFielder, opts ...Option) (*pipeli
99103
// vars from signing.
100104
objEnv, _ := values["env"].(map[string]string)
101105

106+
for _, ignored := range options.ignoredEnvVars {
107+
delete(objEnv, ignored)
108+
delete(options.env, ignored)
109+
}
110+
111+
// We may have deleted the only entry in the env map, and we canonicalise empty maps to nil, so if the map is empty,
112+
// set it to nil.
113+
values["env"] = EmptyToNilMap(objEnv)
114+
102115
// Namespace the env values and include them in the values to sign.
103116
for k, v := range options.env {
104117
if _, has := objEnv[k]; has {
@@ -182,6 +195,15 @@ func Verify(ctx context.Context, s *pipeline.Signature, keySet any, sf SignedFie
182195
// See Sign above for why we need special handling for step env.
183196
objEnv, _ := values["env"].(map[string]string)
184197

198+
for _, ignored := range options.ignoredEnvVars {
199+
delete(objEnv, ignored)
200+
delete(options.env, ignored)
201+
}
202+
203+
// We may have deleted the only entry in the env map, and we canonicalise empty maps to nil, so if the map is empty,
204+
// set it to nil.
205+
values["env"] = EmptyToNilMap(objEnv)
206+
185207
// Namespace the env values and include them in the values to sign.
186208
for k, v := range options.env {
187209
if _, has := objEnv[k]; has {

signature/sign_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,101 @@ func TestSignVerifyEnv(t *testing.T) {
516516
}
517517
}
518518

519+
func TestSignVerify_IgnoredEnvVars(t *testing.T) {
520+
t.Parallel()
521+
ctx := context.Background()
522+
523+
keyStr, keyAlg := "alpacas", jwa.HS256
524+
signer, verifier, err := jwkutil.NewSymmetricKeyPairFromString(keyID, keyStr, keyAlg)
525+
if err != nil {
526+
t.Fatalf("jwkutil.NewSymmetricKeyPairFromString(%q, %q, %q) error = %v", keyID, keyStr, keyAlg, err)
527+
}
528+
529+
key, ok := signer.Key(0)
530+
if !ok {
531+
t.Fatalf("signer.Key(0) = _, false, want true")
532+
}
533+
534+
toSign := &CommandStepWithInvariants{
535+
CommandStep: pipeline.CommandStep{
536+
Command: "llamas",
537+
Env: map[string]string{
538+
"CONTEXT": "cats",
539+
// "DEPLOY": "0",
540+
},
541+
},
542+
RepositoryURL: fakeRepositoryURL,
543+
}
544+
545+
toVerify := &CommandStepWithInvariants{
546+
CommandStep: pipeline.CommandStep{
547+
Command: "llamas",
548+
Env: map[string]string{
549+
"CONTEXT": "dogs", // Changed from "cats"
550+
// "DEPLOY": "0",
551+
},
552+
},
553+
RepositoryURL: fakeRepositoryURL,
554+
}
555+
556+
sig, err := Sign(ctx, key, toSign, IgnoringEnvVars("CONTEXT"))
557+
if err != nil {
558+
t.Fatalf("Sign(ctx, key, %v) error = %v", toSign, err)
559+
}
560+
561+
if err := Verify(ctx, sig, verifier, toVerify, IgnoringEnvVars("CONTEXT")); err != nil {
562+
t.Errorf("Verify(ctx, %v, verifier, %v) = %v", sig, toVerify, err)
563+
}
564+
}
565+
566+
// In this test, we have a step with no env, then sign it with a pipeline env that has an ignored env var.
567+
// Then, we verify the step having changed the ignored env var in the step env. It should verify successfully,
568+
// because the ignored env var is not included in the signature.
569+
func TestSignVerify_IgnoredEnvVars_WithEnv(t *testing.T) {
570+
t.Parallel()
571+
ctx := context.Background()
572+
573+
keyStr, keyAlg := "alpacas", jwa.HS256
574+
signer, verifier, err := jwkutil.NewSymmetricKeyPairFromString(keyID, keyStr, keyAlg)
575+
if err != nil {
576+
t.Fatalf("jwkutil.NewSymmetricKeyPairFromString(%q, %q, %q) error = %v", keyID, keyStr, keyAlg, err)
577+
}
578+
579+
key, ok := signer.Key(0)
580+
if !ok {
581+
t.Fatalf("signer.Key(0) = _, false, want true")
582+
}
583+
584+
toSign := &CommandStepWithInvariants{
585+
CommandStep: pipeline.CommandStep{Command: "llamas"},
586+
RepositoryURL: fakeRepositoryURL,
587+
}
588+
589+
ignored := "ENV_VAR_TO_CHANGE"
590+
pipelineEnv := map[string]string{
591+
ignored: "cats",
592+
}
593+
594+
toVerify := &CommandStepWithInvariants{
595+
CommandStep: pipeline.CommandStep{
596+
Command: "llamas",
597+
Env: map[string]string{
598+
ignored: "dogs",
599+
},
600+
},
601+
RepositoryURL: fakeRepositoryURL,
602+
}
603+
604+
sig, err := Sign(ctx, key, toSign, WithEnv(pipelineEnv), IgnoringEnvVars(ignored))
605+
if err != nil {
606+
t.Fatalf("Sign(ctx, key, %v) error = %v", toSign, err)
607+
}
608+
609+
if err := Verify(ctx, sig, verifier, toVerify, WithEnv(pipelineEnv), IgnoringEnvVars(ignored)); err != nil {
610+
t.Errorf("Verify(ctx, %v, verifier, %v) = %v", sig, toVerify, err)
611+
}
612+
}
613+
519614
func TestSignVerify_NilVsEmpty(t *testing.T) {
520615
t.Parallel()
521616
ctx := context.Background()

0 commit comments

Comments
 (0)