Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d93bc8a
Add checksum storage to build context for in-flight checksumming
leodido Sep 26, 2025
1db5f59
Implement checksum helper functions for in-flight checksumming
leodido Sep 26, 2025
f5a71bb
Hook checksum recording after cache artifact creation
leodido Sep 26, 2025
4248b6b
Add end-of-build verification to complete TOCTU attack protection
leodido Sep 26, 2025
f8707bd
Add --in-flight-checksums CLI flag for TOCTU attack prevention
leodido Sep 26, 2025
ca44b1a
Add comprehensive tests for in-flight checksumming security
leodido Sep 26, 2025
ee023c6
feat: implement integrated SLSA signing architecture
leodido Sep 26, 2025
10a6cda
feat: add sign-cache plumbing command for CI/CD integration
leodido Sep 26, 2025
7193f2a
fix: replace placeholder Sigstore implementation with production API
leodido Sep 29, 2025
c003595
refactor: simplify sign-cache command interface
leodido Sep 29, 2025
c6abee2
refactor: remove unnecessary tempMu mutex from sign-cache
leodido Oct 21, 2025
7d63cfa
refactor: extract magic number to maxAcceptableFailureRate constant
leodido Oct 21, 2025
d4c01cd
refactor: use GITHUB_WORKFLOW_REF for builderID instead of hardcoded …
leodido Oct 21, 2025
b448d6c
fix: set build timestamps to nil in SLSA attestations
leodido Oct 22, 2025
39deb12
refactor: add UploadFile method to RemoteCache interface
leodido Oct 22, 2025
4878f3a
feat: add environment variable support for in-flight checksumming
leodido Sep 30, 2025
c1d94d0
test: add comprehensive environment variable test coverage
leodido Sep 30, 2025
9277f84
test: fix environment variable test implementation
leodido Sep 30, 2025
da23af1
chore: run go mod tidy
leodido Oct 23, 2025
1d3d186
fix: implement UploadFile method for cache wrapper types
leodido Oct 23, 2025
1823828
fix: resolve linting issues
leodido Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ func addBuildFlags(cmd *cobra.Command) {
cmd.Flags().StringToString("docker-build-options", nil, "Options passed to all 'docker build' commands")
cmd.Flags().Bool("slsa-cache-verification", false, "Enable SLSA verification for cached artifacts")
cmd.Flags().String("slsa-source-uri", "", "Expected source URI for SLSA verification (required when verification enabled)")
cmd.Flags().Bool("in-flight-checksums", false, "Enable checksumming of cache artifacts to prevent TOCTU attacks")
cmd.Flags().String("report", "", "Generate a HTML report after the build has finished. (e.g. --report myreport.html)")
cmd.Flags().String("report-segment", os.Getenv("LEEWAY_SEGMENT_KEY"), "Report build events to segment using the segment key (defaults to $LEEWAY_SEGMENT_KEY)")
cmd.Flags().Bool("report-github", os.Getenv("GITHUB_OUTPUT") != "", "Report package build success/failure to GitHub Actions using the GITHUB_OUTPUT environment variable")
Expand Down Expand Up @@ -318,6 +319,17 @@ 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),
leeway.WithRemoteCache(remoteCache),
Expand All @@ -332,6 +344,7 @@ func getBuildOpts(cmd *cobra.Command) ([]leeway.BuildOption, cache.LocalCache) {
leeway.WithCompressionDisabled(dontCompress),
leeway.WithFixedBuildDir(fixedBuildDir),
leeway.WithDisableCoverage(disableCoverage),
leeway.WithInFlightChecksums(inFlightChecksums),
}, localCache
}

Expand All @@ -351,6 +364,10 @@ func (c *pushOnlyRemoteCache) Upload(ctx context.Context, src cache.LocalCache,
return c.C.Upload(ctx, src, pkgs)
}

func (c *pushOnlyRemoteCache) UploadFile(ctx context.Context, filePath string, key string) error {
return c.C.UploadFile(ctx, filePath, key)
}

type pullOnlyRemoteCache struct {
C cache.RemoteCache
}
Expand All @@ -367,6 +384,10 @@ func (c *pullOnlyRemoteCache) Upload(ctx context.Context, src cache.LocalCache,
return nil
}

func (c *pullOnlyRemoteCache) UploadFile(ctx context.Context, filePath string, key string) error {
return nil
}

func getRemoteCacheFromEnv() cache.RemoteCache {
return getRemoteCache(nil)
}
Expand Down
242 changes: 242 additions & 0 deletions cmd/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package cmd

import (
"os"
"testing"

"github.com/spf13/cobra"
)

func TestBuildCommandFlags(t *testing.T) {
tests := []struct {
name string
args []string
wantFlag string
wantVal interface{}
}{
{
name: "in-flight-checksums flag default",
args: []string{},
wantFlag: "in-flight-checksums",
wantVal: false,
},
{
name: "in-flight-checksums flag enabled",
args: []string{"--in-flight-checksums"},
wantFlag: "in-flight-checksums",
wantVal: true,
},
{
name: "in-flight-checksums flag explicitly disabled",
args: []string{"--in-flight-checksums=false"},
wantFlag: "in-flight-checksums",
wantVal: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a new build command for each test
cmd := &cobra.Command{
Use: "build",
Run: func(cmd *cobra.Command, args []string) {
// 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)
}
})
}
}

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",
Run: func(cmd *cobra.Command, args []string) {
// 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)
}
}

func TestGetBuildOptsWithInFlightChecksums(t *testing.T) {
tests := []struct {
name string
inFlightChecksumsFlag bool
expectInFlightChecksums bool
}{
{
name: "in-flight checksums disabled",
inFlightChecksumsFlag: false,
expectInFlightChecksums: false,
},
{
name: "in-flight checksums enabled",
inFlightChecksumsFlag: true,
expectInFlightChecksums: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := &cobra.Command{
Use: "build",
Run: func(cmd *cobra.Command, args []string) {
// No-op for testing
},
}

addBuildFlags(cmd)

// Set the flag value
err := cmd.Flags().Set("in-flight-checksums", "false")
if tt.inFlightChecksumsFlag {
err = cmd.Flags().Set("in-flight-checksums", "true")
}
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 {
t.Error("expected build options but got nil")
}
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
})
}
}
4 changes: 4 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -99,6 +102,7 @@ variables have an effect on leeway:
<light_blue>LEEWAY_DEFAULT_CACHE_LEVEL</> Sets the default cache level for builds. Defaults to "remote".
<light_blue>LEEWAY_SLSA_CACHE_VERIFICATION</> Enables SLSA verification for cached artifacts (true/false).
<light_blue>LEEWAY_SLSA_SOURCE_URI</> Expected source URI for SLSA verification (github.com/owner/repo).
<light_blue>LEEWAY_ENABLE_IN_FLIGHT_CHECKSUMS</> Enable checksumming of cache artifacts (true/false).
<light_blue>LEEWAY_EXPERIMENTAL</> Enables experimental leeway features and commands.
`),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
Expand Down
Loading
Loading