Skip to content

Commit 6dec6d5

Browse files
committed
Add policy subcommand
Signed-off-by: Adolfo García Veytia (Puerco) <puerco@carabiner.dev>
1 parent 5be9a5a commit 6dec6d5

2 files changed

Lines changed: 223 additions & 0 deletions

File tree

sourcetool/internal/cmd/policy.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
9+
"github.com/spf13/cobra"
10+
11+
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/policy"
12+
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/sourcetool"
13+
"github.com/slsa-framework/slsa-source-poc/sourcetool/pkg/sourcetool/models"
14+
)
15+
16+
type policyViewOpts struct {
17+
repoOptions
18+
}
19+
20+
type policyCreateOpts struct {
21+
branchOptions
22+
openPullRequest bool
23+
}
24+
25+
func (pco *policyCreateOpts) AddFlags(cmd *cobra.Command) {
26+
pco.branchOptions.AddFlags(cmd)
27+
cmd.PersistentFlags().BoolVar(&pco.openPullRequest, "pr", true, "Open a pull request to check-in the policy")
28+
}
29+
30+
func addPolicy(parentCmd *cobra.Command) {
31+
policyCmd := &cobra.Command{
32+
Short: "tools to work with source policies",
33+
Long: fmt.Sprintf(`
34+
%s %s
35+
36+
The policy subcommands can be used to view, create and update the source
37+
policy for a repository. The policy family has two subcommands:
38+
39+
%s
40+
Shows current repository policy for a repository checjed into the SLSA community
41+
policy repo.
42+
43+
%s
44+
Creates a new policy for a repository and, optionally, check it into the
45+
SLSA community repository.
46+
47+
`, w("sourcetool policy:"), w2("configure SLSA source policies for a repo"),
48+
w("sourcetool policy view"), w("sourcetool policy create")),
49+
Use: "policy",
50+
SilenceUsage: true,
51+
SilenceErrors: true,
52+
}
53+
54+
addPolicyView(policyCmd)
55+
addPolicyCreate(policyCmd)
56+
parentCmd.AddCommand(policyCmd)
57+
}
58+
59+
func addPolicyView(parent *cobra.Command) {
60+
opts := &policyViewOpts{}
61+
policyViewCmd := &cobra.Command{
62+
Short: "view the policy of a repository",
63+
Long: `The view subcommand retrieves the policy stored in the SLSA community
64+
repository for a repository and displays it.
65+
`,
66+
Use: "view owner/repo",
67+
SilenceUsage: false,
68+
SilenceErrors: true,
69+
PreRunE: func(_ *cobra.Command, args []string) error {
70+
if len(args) > 0 {
71+
if err := opts.ParseSlug(args[0]); err != nil {
72+
return err
73+
}
74+
}
75+
76+
// Validate early the repository options to provide a more
77+
// useful message to the user
78+
if err := opts.Validate(); err != nil {
79+
return err
80+
}
81+
82+
return nil
83+
},
84+
RunE: func(cmd *cobra.Command, args []string) (err error) {
85+
if err := opts.Validate(); err != nil {
86+
return err
87+
}
88+
89+
// At this point options are valid, no help needed.
90+
cmd.SilenceUsage = true
91+
92+
authenticator, err := CheckAuth()
93+
if err != nil {
94+
return err
95+
}
96+
97+
// Create a new sourcetool object
98+
srctool, err := sourcetool.New(
99+
sourcetool.WithAuthenticator(authenticator),
100+
// sourcetool.WithPolicyRepo(opts.policyRepo),
101+
)
102+
if err != nil {
103+
return err
104+
}
105+
106+
pcy, err := srctool.GetRepositoryPolicy(context.Background(), opts.GetRepository())
107+
if err != nil {
108+
return err
109+
}
110+
111+
if err := displayPolicy(opts.repoOptions, pcy); err != nil {
112+
return err
113+
}
114+
115+
return nil
116+
},
117+
}
118+
opts.AddFlags(policyViewCmd)
119+
parent.AddCommand(policyViewCmd)
120+
}
121+
122+
// addPolicyCreate adds the create subcreate
123+
func addPolicyCreate(parent *cobra.Command) {
124+
opts := &policyCreateOpts{}
125+
policyViewCmd := &cobra.Command{
126+
Short: "creates a source policy for a repository",
127+
Long: `The create subcommand inspects the controls in place for a repo
128+
and creates a new policy for it.
129+
`,
130+
Use: "create owner/repo@branch",
131+
SilenceUsage: false,
132+
SilenceErrors: true,
133+
PreRunE: func(_ *cobra.Command, args []string) error {
134+
if len(args) > 0 {
135+
if err := opts.ParseLocator(args[0]); err != nil {
136+
return err
137+
}
138+
}
139+
140+
// Validate early the repository options to provide a more
141+
// useful message to the user
142+
if err := opts.repoOptions.Validate(); err != nil {
143+
return err
144+
}
145+
146+
return nil
147+
},
148+
RunE: func(cmd *cobra.Command, args []string) (err error) {
149+
if err := opts.Validate(); err != nil {
150+
return err
151+
}
152+
153+
// At this point options are valid, no help needed.
154+
cmd.SilenceUsage = true
155+
156+
authenticator, err := CheckAuth()
157+
if err != nil {
158+
return err
159+
}
160+
161+
// Create a new sourcetool object
162+
srctool, err := sourcetool.New(
163+
sourcetool.WithAuthenticator(authenticator),
164+
// sourcetool.WithPolicyRepo(opts.policyRepo),
165+
)
166+
if err != nil {
167+
return err
168+
}
169+
170+
epcy, err := srctool.GetRepositoryPolicy(context.Background(), opts.GetRepository())
171+
if err != nil {
172+
return fmt.Errorf("checking for existing policy: %w", err)
173+
}
174+
if epcy != nil {
175+
return fmt.Errorf("repository already has a policy checked into the community repo")
176+
}
177+
178+
// Create the policy, this will open the pull request in the community
179+
// repo if the options say so.
180+
pcy, pr, err := srctool.CreateRepositoryPolicy(
181+
context.Background(), opts.GetRepository(), []*models.Branch{opts.GetBranch()},
182+
)
183+
if err != nil {
184+
return err
185+
}
186+
187+
if err := displayPolicy(opts.repoOptions, pcy); err != nil {
188+
return err
189+
}
190+
191+
if opts.openPullRequest && pr != nil {
192+
fmt.Fprintf(os.Stderr, "\n")
193+
fmt.Fprintf(os.Stderr, "Opened pull request: https://github.com/%s/pulls/%d\n\n", pr.Repo.Path, pr.Number)
194+
}
195+
196+
return nil
197+
},
198+
}
199+
opts.AddFlags(policyViewCmd)
200+
parent.AddCommand(policyViewCmd)
201+
}
202+
203+
func displayPolicy(opts repoOptions, pcy *policy.RepoPolicy) error {
204+
if pcy == nil {
205+
fmt.Println("\n" + w(fmt.Sprintf("✖️ No source policy found for %s/%s", opts.owner, opts.repository)))
206+
fmt.Println("To create and check-in a policy for the repository run:")
207+
fmt.Println()
208+
fmt.Printf(" sourcetool policy create %s/%s\n", opts.owner, opts.repository)
209+
fmt.Println()
210+
return nil
211+
}
212+
213+
data, err := json.MarshalIndent(pcy, "", " ")
214+
if err != nil {
215+
return fmt.Errorf("marshaling policy data: %w", err)
216+
}
217+
218+
fmt.Fprint(os.Stderr, w(fmt.Sprintf("\n🛡️ Source policy for %s/%s:\n\n", opts.owner, opts.repository)))
219+
fmt.Println(string(data))
220+
fmt.Println()
221+
return nil
222+
}

sourcetool/internal/cmd/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ controls and much more.
5454
addCheckTag(rootCmd)
5555
addCreatePolicy(rootCmd)
5656
addAuth(rootCmd)
57+
addPolicy(rootCmd)
5758
return rootCmd
5859
}
5960

0 commit comments

Comments
 (0)