Skip to content

Commit 95c5b62

Browse files
authored
Fix the slsa source loop of doom (#401)
* policy: downgrade to actual SLSA level instead of error Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * Surface Shortfall in new AttestRevision result Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * support exit codes in subcommands Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * Add --silent-downgrade and exit 2 on shortfall Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * update checklevel and checktag for new return Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * Test wrapped error codes Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> * Fix misspel flagged by the linter Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev> --------- Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev>
1 parent 947d2b1 commit 95c5b62

8 files changed

Lines changed: 449 additions & 173 deletions

File tree

internal/cmd/checklevel.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,19 +93,26 @@ This is meant to be run within the corresponding GitHub Actions workflow.`,
9393

9494
pe := policy.NewPolicyEvaluator()
9595
pe.UseLocalPolicy = opts.useLocalPolicy
96-
verifiedLevels, policyPath, err := pe.EvaluateControl(cmd.Context(), opts.GetRepository(), opts.GetBranch(), controlStatus)
96+
result, err := pe.EvaluateControl(cmd.Context(), opts.GetRepository(), opts.GetBranch(), controlStatus)
9797
if err != nil {
9898
return err
9999
}
100-
for _, level := range verifiedLevels {
100+
for _, level := range result.VerifiedLevels {
101101
if slsa.IsSlsaSourceLevel(level) {
102102
fmt.Print(level)
103103
break
104104
}
105105
}
106106

107+
// The achieved level can be below the policy target, surface it as a
108+
// warning here (checklevel only reports, it does not gate on it).
109+
if result.Shortfall != nil {
110+
fmt.Fprintf(os.Stderr, "\nwarning: policy target level %s not met; achieved %s: %s\n",
111+
result.Shortfall.TargetLevel, result.Shortfall.AchievedLevel, result.Shortfall.Reason)
112+
}
113+
107114
unsignedVsa, err := attest.CreateUnsignedSourceVsa(
108-
opts.GetBranch(), opts.GetCommit(), verifiedLevels, policyPath,
115+
opts.GetBranch(), opts.GetCommit(), result.VerifiedLevels, result.PolicyPath,
109116
)
110117
if err != nil {
111118
return err

internal/cmd/checklevelprov.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package cmd
66
import (
77
"errors"
88
"fmt"
9+
"os"
910
"slices"
1011
"strings"
1112

@@ -60,6 +61,7 @@ type checkLevelProvOpts struct {
6061
outputUnsignedBundle string
6162
outputSignedBundle string
6263
useLocalPolicy string
64+
silentDowngrade bool
6365
}
6466

6567
func (clp *checkLevelProvOpts) Validate() error {
@@ -78,6 +80,7 @@ func (clp *checkLevelProvOpts) AddFlags(cmd *cobra.Command) {
7880
cmd.PersistentFlags().StringVar(&clp.outputUnsignedBundle, "output_unsigned_bundle", "", "The path to write a bundle of unsigned attestations.")
7981
cmd.PersistentFlags().StringVar(&clp.outputSignedBundle, "output_signed_bundle", "", "The path to write a bundle of signed attestations.")
8082
cmd.PersistentFlags().StringVar(&clp.useLocalPolicy, "use_local_policy", "", "UNSAFE: Use the policy at this local path instead of the official one.")
83+
cmd.PersistentFlags().BoolVar(&clp.silentDowngrade, "silent-downgrade", false, "Exit 0 when the SLSA level is below the policy target (still attests).")
8184
}
8285

8386
func addCheckLevelProv(parentCmd *cobra.Command) {
@@ -162,7 +165,7 @@ and pushed to its remote (--push=note).
162165
}
163166

164167
// Attest the commit passing the options
165-
verifiedLevels, err := srctool.AttestRevision(
168+
result, err := srctool.AttestRevision(
166169
cmd.Context(), opts.GetBranch(), opts.GetRevision(),
167170
sourcetool.WithLocalPolicy(opts.useLocalPolicy),
168171
sourcetool.WithOutputPath(outputPath),
@@ -174,7 +177,24 @@ and pushed to its remote (--push=note).
174177
return fmt.Errorf("attesting commit: %w", err)
175178
}
176179

177-
fmt.Print(verifiedLevels.Levels())
180+
fmt.Print(result.VerifiedLevels.Levels())
181+
182+
// The attestations are generated (and optionally pushed) regardless
183+
// of the policy outcome. If the achieved level is below the policy
184+
// target return exit code 2 or just a warning when --silent-downgrade
185+
// is set.
186+
if result.Shortfall != nil {
187+
msg := fmt.Sprintf(
188+
"policy target level %s not met; achieved %s: %s",
189+
result.Shortfall.TargetLevel, result.Shortfall.AchievedLevel, result.Shortfall.Reason,
190+
)
191+
if opts.silentDowngrade {
192+
fmt.Fprintf(os.Stderr, "warning: %s\n", msg)
193+
return nil
194+
}
195+
return &exitError{code: 2, err: errors.New(msg)}
196+
}
197+
178198
return nil
179199
},
180200
}

internal/cmd/checktag.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ func addCheckTag(parentCmd *cobra.Command) {
126126
}
127127

128128
// Attest the commit passing the options
129-
verifiedLevels, err := srctool.AttestRevision(
129+
result, err := srctool.AttestRevision(
130130
cmd.Context(), opts.GetBranch(), opts.GetRevision(),
131131
sourcetool.WithLocalPolicy(opts.useLocalPolicy),
132132
sourcetool.WithOutputPath(outputPath),
@@ -138,7 +138,7 @@ func addCheckTag(parentCmd *cobra.Command) {
138138
return fmt.Errorf("attesting commit: %w", err)
139139
}
140140

141-
fmt.Print(verifiedLevels.Levels())
141+
fmt.Print(result.VerifiedLevels.Levels())
142142
return nil
143143
},
144144
}

internal/cmd/exiterror_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-FileCopyrightText: Copyright 2026 The SLSA Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package cmd
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"testing"
10+
)
11+
12+
func TestExitErrorErrorAndUnwrap(t *testing.T) {
13+
wrapped := errors.New("policy target level SLSA_SOURCE_LEVEL_3 not met")
14+
ee := &exitError{code: 2, err: wrapped}
15+
16+
if ee.Error() != wrapped.Error() {
17+
t.Errorf("Error() = %q, want %q", ee.Error(), wrapped.Error())
18+
}
19+
if !errors.Is(ee, wrapped) {
20+
t.Errorf("errors.Is(exitError, wrapped) = false, want true (Unwrap should expose the wrapped error)")
21+
}
22+
}
23+
24+
func TestExitErrorExtraction(t *testing.T) {
25+
t.Parallel()
26+
for _, tt := range []struct {
27+
name string
28+
err error
29+
wantMatch bool
30+
wantCode int
31+
}{
32+
{
33+
name: "direct exit error",
34+
err: &exitError{code: 2, err: errors.New("shortfall")},
35+
wantMatch: true,
36+
wantCode: 2,
37+
},
38+
{
39+
name: "wrapped exit error",
40+
err: fmt.Errorf("attesting commit: %w", &exitError{code: 2, err: errors.New("shortfall")}),
41+
wantMatch: true,
42+
wantCode: 2,
43+
},
44+
{
45+
name: "plain error falls through",
46+
err: errors.New("boom"),
47+
wantMatch: false,
48+
},
49+
} {
50+
t.Run(tt.name, func(t *testing.T) {
51+
t.Parallel()
52+
var ee *exitError
53+
gotMatch := errors.As(tt.err, &ee)
54+
if gotMatch != tt.wantMatch {
55+
t.Fatalf("errors.As() = %v, want %v", gotMatch, tt.wantMatch)
56+
}
57+
if gotMatch && ee.code != tt.wantCode {
58+
t.Errorf("exitError.code = %d, want %d", ee.code, tt.wantCode)
59+
}
60+
})
61+
}
62+
}

internal/cmd/root.go

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,23 @@ var githubToken string
2626
// return attest.NewBndVerifier(options)
2727
// }
2828

29+
// exitError wraps an error together with an exit code so commands can
30+
// signal other failuresdistinct from a generic failure (exit 1).
31+
type exitError struct {
32+
code int
33+
err error
34+
}
35+
36+
func (e *exitError) Error() string { return e.err.Error() }
37+
func (e *exitError) Unwrap() error { return e.err }
38+
2939
func buildRootCommand() *cobra.Command {
3040
// rootCmd represents the base command when called without any subcommands
3141
rootCmd := &cobra.Command{
32-
Use: "sourcetool",
33-
Short: "A tool to manage SLSA Source in code repositories",
42+
Use: "sourcetool",
43+
SilenceUsage: true,
44+
SilenceErrors: true,
45+
Short: "A tool to manage SLSA Source in code repositories",
3446
Long: `
3547
SLSA sourcetool: Manage SLSA Source controls and data
3648
@@ -89,10 +101,21 @@ controls and much more.
89101
// This is called by main.main(). It only needs to happen once to the rootCmd.
90102
func Execute() {
91103
rootCmd := buildRootCommand()
92-
if err := rootCmd.Execute(); err != nil {
93-
fmt.Printf("Error: %v\n", err)
94-
os.Exit(1)
104+
err := rootCmd.Execute()
105+
if err == nil {
106+
return
95107
}
108+
109+
// Subcommands may send a specific exit code by returning an *exitError.
110+
// Everything else is a generic failure (exit 1).
111+
var ee *exitError
112+
if errors.As(err, &ee) {
113+
fmt.Println(ee.Error())
114+
os.Exit(ee.code)
115+
}
116+
117+
fmt.Printf("Error: %v\n", err)
118+
os.Exit(1)
96119
}
97120

98121
func CheckAuth() (*auth.Authenticator, error) {

0 commit comments

Comments
 (0)