1 package cmd
2
3 import (
4 "fmt"
5 "path/filepath"
6
7 intoto "github.com/in-toto/in-toto-golang/in_toto"
8 "github.com/spf13/cobra"
9 )
10
11 var (
12 stepName string
13 runDir string
14 materialsPaths []string
15 productsPaths []string
16 noCommand bool
17 )
18
19 var runCmd = &cobra.Command{
20 Use: "run",
21 Short: "Executes the passed command and records paths and hashes of 'materials'",
22 Long: `Executes the passed command and records paths and hashes of 'materials' (i.e.
23 files before command execution) and 'products' (i.e. files after command
24 execution) and stores them together with other information (executed command,
25 return value, stdout, stderr, ...) to a link metadata file, which is signed
26 with the passed key. Returns nonzero value on failure and zero otherwise.`,
27 Args: cobra.MinimumNArgs(0),
28 PreRunE: getKeyCert,
29 RunE: run,
30 }
31
32 func init() {
33 rootCmd.AddCommand(runCmd)
34
35 runCmd.Flags().StringVarP(
36 &stepName,
37 "name",
38 "n",
39 "",
40 `Name used to associate the resulting link metadata
41 with the corresponding step defined in an in-toto layout.`,
42 )
43
44 runCmd.Flags().StringVarP(
45 &runDir,
46 "run-dir",
47 "r",
48 "",
49 `runDir specifies the working directory of the command.
50 If runDir is the empty string, the command will run in the
51 calling process's current directory. The runDir directory must
52 exist, be writable, and not be a symlink.`,
53 )
54
55 runCmd.Flags().StringVarP(
56 &keyPath,
57 "key",
58 "k",
59 "",
60 `Path to a PEM formatted private key file used to sign
61 the resulting link metadata.`,
62 )
63
64 runCmd.Flags().StringVarP(
65 &certPath,
66 "cert",
67 "c",
68 "",
69 `Path to a PEM formatted certificate that corresponds with
70 the provided key.`,
71 )
72
73 runCmd.Flags().StringArrayVarP(
74 &materialsPaths,
75 "materials",
76 "m",
77 []string{},
78 `Paths to files or directories, whose paths and hashes
79 are stored in the resulting link metadata before the
80 command is executed. Symlinks are followed.`,
81 )
82
83 runCmd.Flags().StringArrayVarP(
84 &productsPaths,
85 "products",
86 "p",
87 []string{},
88 `Paths to files or directories, whose paths and hashes
89 are stored in the resulting link metadata after the
90 command is executed. Symlinks are followed.`,
91 )
92
93 runCmd.Flags().StringVarP(
94 &outDir,
95 "metadata-directory",
96 "d",
97 "./",
98 `Directory to store link metadata`,
99 )
100
101 runCmd.Flags().StringArrayVarP(
102 &lStripPaths,
103 "lstrip-paths",
104 "l",
105 []string{},
106 `Path prefixes used to left-strip artifact paths before storing
107 them to the resulting link metadata. If multiple prefixes
108 are specified, only a single prefix can match the path of
109 any artifact and that is then left-stripped. All prefixes
110 are checked to ensure none of them are a left substring
111 of another.`,
112 )
113
114 runCmd.Flags().StringArrayVarP(
115 &exclude,
116 "exclude",
117 "e",
118 []string{},
119 `Path patterns to match paths that should not be recorded as 0
120 ‘materials’ or ‘products’. Passed patterns override patterns defined
121 in environment variables or config files. See Config docs for details.`,
122 )
123
124 runCmd.MarkFlagRequired("name")
125
126 runCmd.Flags().BoolVar(
127 &lineNormalization,
128 "normalize-line-endings",
129 false,
130 `Enable line normalization in order to support different
131 operating systems. It is done by replacing all line separators
132 with a new line character.`,
133 )
134
135 runCmd.Flags().BoolVarP(
136 &noCommand,
137 "no-command",
138 "x",
139 false,
140 `Indicate that there is no command to be executed for the step.`,
141 )
142
143 runCmd.PersistentFlags().BoolVar(
144 &followSymlinkDirs,
145 "follow-symlink-dirs",
146 false,
147 `Follow symlinked directories to their targets. Note: this parameter
148 toggles following linked directories only, linked files are always
149 recorded independently of this parameter.`,
150 )
151
152 runCmd.PersistentFlags().BoolVar(
153 &useDSSE,
154 "use-dsse",
155 false,
156 "Create metadata using DSSE instead of the legacy signature wrapper.",
157 )
158
159 runCmd.Flags().StringVar(
160 &spiffeUDS,
161 "spiffe-workload-api-path",
162 "",
163 "UDS path for SPIFFE workload API",
164 )
165
166 }
167
168 func run(cmd *cobra.Command, args []string) error {
169 if noCommand && len(args) > 0 {
170 return fmt.Errorf("command arguments passed with --no-command/-x flag")
171 }
172
173 if !noCommand && len(args) == 0 {
174 return fmt.Errorf("no command arguments passed, please specify or use --no-command option")
175 }
176
177 metadata, err := intoto.InTotoRun(stepName, runDir, materialsPaths, productsPaths, args, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization, followSymlinkDirs, useDSSE)
178 if err != nil {
179 return fmt.Errorf("failed to create link metadata: %w", err)
180 }
181
182 linkName := fmt.Sprintf(intoto.LinkNameFormat, metadata.GetPayload().(intoto.Link).Name, key.KeyID)
183
184 linkPath := filepath.Join(outDir, linkName)
185 err = metadata.Dump(linkPath)
186 if err != nil {
187 return fmt.Errorf("failed to write link metadata to %s: %w", linkPath, err)
188 }
189
190 return nil
191 }
192
View as plain text