diff --git a/cmd/build.go b/cmd/build.go index e6a1dd7..9222c84 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -319,10 +319,16 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) { log.Fatal(err) } + // Get in-flight checksums setting (env var as default, CLI flag overrides) + inFlightChecksumsDefault := os.Getenv(EnvvarEnableInFlightChecksums) == "true" inFlightChecksums, err := cmd.Flags().GetBool("in-flight-checksums") if err != nil { log.Fatal(err) } + // If flag wasn't explicitly set, use environment variable + if !cmd.Flags().Changed("in-flight-checksums") { + inFlightChecksums = inFlightChecksumsDefault + } return []leeway.BuildOption{ leeway.WithLocalCache(localCache), diff --git a/cmd/build_test.go b/cmd/build_test.go index 98f1a80..e8a419c 100644 --- a/cmd/build_test.go +++ b/cmd/build_test.go @@ -1,6 +1,7 @@ package cmd import ( + "os" "testing" "github.com/spf13/cobra" @@ -42,29 +43,29 @@ func TestBuildCommandFlags(t *testing.T) { // No-op for testing }, } - + // Add the build flags addBuildFlags(cmd) - + // Set the args and parse cmd.SetArgs(tt.args) err := cmd.Execute() if err != nil { t.Fatalf("failed to execute command: %v", err) } - + // Check if the flag exists flag := cmd.Flags().Lookup(tt.wantFlag) if flag == nil { t.Fatalf("flag %s not found", tt.wantFlag) } - + // Get the flag value val, err := cmd.Flags().GetBool(tt.wantFlag) if err != nil { t.Fatalf("failed to get flag value: %v", err) } - + if val != tt.wantVal { t.Errorf("expected flag %s to be %v, got %v", tt.wantFlag, tt.wantVal, val) } @@ -72,6 +73,86 @@ func TestBuildCommandFlags(t *testing.T) { } } +func TestInFlightChecksumsEnvironmentVariable(t *testing.T) { + tests := []struct { + name string + envValue string + flagValue string + flagSet bool + expected bool + }{ + { + name: "env var enabled, no flag", + envValue: "true", + expected: true, + }, + { + name: "env var disabled, no flag", + envValue: "false", + expected: false, + }, + { + name: "no env var, no flag", + envValue: "", + expected: false, + }, + { + name: "env var enabled, flag explicitly disabled", + envValue: "true", + flagValue: "false", + flagSet: true, + expected: false, // Flag should override + }, + { + name: "env var disabled, flag explicitly enabled", + envValue: "false", + flagValue: "true", + flagSet: true, + expected: true, // Flag should override + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set environment variable using t.Setenv for proper cleanup + if tt.envValue != "" { + t.Setenv("LEEWAY_ENABLE_IN_FLIGHT_CHECKSUMS", tt.envValue) + } + + // Create test command + cmd := &cobra.Command{ + Use: "build", + Run: func(cmd *cobra.Command, args []string) {}, + } + + addBuildFlags(cmd) + + // Set flag if specified + if tt.flagSet { + err := cmd.Flags().Set("in-flight-checksums", tt.flagValue) + if err != nil { + t.Fatalf("failed to set flag: %v", err) + } + } + + // Test the actual logic from getBuildOpts + inFlightChecksumsDefault := os.Getenv("LEEWAY_ENABLE_IN_FLIGHT_CHECKSUMS") == "true" + inFlightChecksums, err := cmd.Flags().GetBool("in-flight-checksums") + if err != nil { + t.Fatalf("failed to get flag: %v", err) + } + // If flag wasn't explicitly set, use environment variable + if !cmd.Flags().Changed("in-flight-checksums") { + inFlightChecksums = inFlightChecksumsDefault + } + + if inFlightChecksums != tt.expected { + t.Errorf("expected in-flight checksums to be %v, got %v", tt.expected, inFlightChecksums) + } + }) + } +} + func TestBuildCommandHelpText(t *testing.T) { cmd := &cobra.Command{ Use: "build", @@ -79,25 +160,25 @@ func TestBuildCommandHelpText(t *testing.T) { // No-op for testing }, } - + addBuildFlags(cmd) - + // Check that the in-flight-checksums flag is documented flag := cmd.Flags().Lookup("in-flight-checksums") if flag == nil { t.Fatal("in-flight-checksums flag not found") } - + expectedUsage := "Enable checksumming of cache artifacts to prevent TOCTU attacks" if flag.Usage != expectedUsage { t.Errorf("expected flag usage to be %q, got %q", expectedUsage, flag.Usage) } - + // Verify it's a boolean flag if flag.Value.Type() != "bool" { t.Errorf("expected flag type to be bool, got %s", flag.Value.Type()) } - + // Verify default value if flag.DefValue != "false" { t.Errorf("expected default value to be false, got %s", flag.DefValue) @@ -130,9 +211,9 @@ func TestGetBuildOptsWithInFlightChecksums(t *testing.T) { // No-op for testing }, } - + addBuildFlags(cmd) - + // Set the flag value err := cmd.Flags().Set("in-flight-checksums", "false") if tt.inFlightChecksumsFlag { @@ -141,10 +222,10 @@ func TestGetBuildOptsWithInFlightChecksums(t *testing.T) { if err != nil { t.Fatalf("failed to set flag: %v", err) } - + // Test getBuildOpts function opts, localCache := getBuildOpts(cmd) - + // We can't directly test the WithInFlightChecksums option since it's internal, // but we can verify the function doesn't error and returns options if opts == nil { @@ -153,9 +234,9 @@ func TestGetBuildOptsWithInFlightChecksums(t *testing.T) { if localCache == nil { t.Error("expected local cache but got nil") } - + // The actual verification of the in-flight checksums option would need // to be done through integration tests or by exposing the option state }) } -} \ No newline at end of file +} diff --git a/cmd/root.go b/cmd/root.go index 35d772b..4664781 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -30,6 +30,9 @@ const ( // EnvvarSLSASourceURI configures the expected source URI for SLSA verification EnvvarSLSASourceURI = "LEEWAY_SLSA_SOURCE_URI" + + // EnvvarEnableInFlightChecksums enables in-flight checksumming of cache artifacts + EnvvarEnableInFlightChecksums = "LEEWAY_ENABLE_IN_FLIGHT_CHECKSUMS" ) const ( @@ -99,6 +102,7 @@ variables have an effect on leeway: LEEWAY_DEFAULT_CACHE_LEVEL Sets the default cache level for builds. Defaults to "remote". LEEWAY_SLSA_CACHE_VERIFICATION Enables SLSA verification for cached artifacts (true/false). LEEWAY_SLSA_SOURCE_URI Expected source URI for SLSA verification (github.com/owner/repo). +LEEWAY_ENABLE_IN_FLIGHT_CHECKSUMS Enable checksumming of cache artifacts (true/false). LEEWAY_EXPERIMENTAL Enables experimental leeway features and commands. `), PersistentPreRun: func(cmd *cobra.Command, args []string) {