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 recordStepName string
13 recordMaterialsPaths []string
14 recordProductsPaths []string
15 )
16
17 var recordCmd = &cobra.Command{
18 Use: "record",
19 Short: `Creates a signed link metadata file in two steps, in order to provide
20 evidence for supply chain steps that cannot be carried out by a single command`,
21 Long: `Creates a signed link metadata file in two steps, in order to provide
22 evidence for supply chain steps that cannot be carried out by a single command
23 (for which ‘in-toto-run’ should be used). It returns a non-zero value on
24 failure and zero otherwise.`,
25 PersistentPreRunE: getKeyCert,
26 }
27
28 var recordStartCmd = &cobra.Command{
29 Use: "start",
30 Short: `Creates a preliminary link file recording the paths and hashes of the
31 passed materials and signs it with the passed functionary’s key.`,
32 Long: `Creates a preliminary link file recording the paths and hashes of the
33 passed materials and signs it with the passed functionary’s key.
34 The resulting link file is stored as ‘.<name>.<keyid prefix>.link-unfinished’.`,
35 RunE: recordStart,
36 }
37
38 var recordStopCmd = &cobra.Command{
39 Use: "stop",
40 Short: `Records and adds the paths and hashes of the passed products to the link metadata file and updates the signature.`,
41 Long: `Expects preliminary link file ‘.<name>.<keyid prefix>.link-unfinished’ in the CWD,
42 signed by the passed functionary’s key. If found, it records
43 and adds the paths and hashes of the passed products to the
44 link metadata file, updates the signature and renames the
45 file to ‘<name>.<keyid prefix>.link’.`,
46 RunE: recordStop,
47 }
48
49 func init() {
50 rootCmd.AddCommand(recordCmd)
51
52 recordCmd.PersistentFlags().StringVarP(
53 &recordStepName,
54 "name",
55 "n",
56 "",
57 `Name for the resulting link metadata file.
58 It is also used to associate the link with a step defined
59 in an in-toto layout.`,
60 )
61
62 recordCmd.PersistentFlags().StringVarP(
63 &keyPath,
64 "key",
65 "k",
66 "",
67 `Path to a private key file to sign the resulting link metadata.
68 The keyid prefix is used as an infix for the link metadata filename,
69 i.e. ‘<name>.<keyid prefix>.link’. See ‘–key-type’ for available
70 formats. Passing one of ‘–key’ or ‘–gpg’ is required.`,
71 )
72
73 recordCmd.PersistentFlags().StringVarP(
74 &certPath,
75 "cert",
76 "c",
77 "",
78 `Path to a PEM formatted certificate that corresponds
79 with the provided key.`,
80 )
81
82 recordCmd.PersistentFlags().StringVarP(
83 &outDir,
84 "metadata-directory",
85 "d",
86 "./",
87 `Directory to store link metadata`,
88 )
89
90 recordCmd.PersistentFlags().StringArrayVarP(
91 &lStripPaths,
92 "lstrip-paths",
93 "l",
94 []string{},
95 `Path prefixes used to left-strip artifact paths before storing
96 them to the resulting link metadata. If multiple prefixes
97 are specified, only a single prefix can match the path of
98 any artifact and that is then left-stripped. All prefixes
99 are checked to ensure none of them are a left substring
100 of another.`,
101 )
102
103 recordCmd.PersistentFlags().StringArrayVarP(
104 &exclude,
105 "exclude",
106 "e",
107 []string{},
108 `Path patterns to match paths that should not be recorded as
109 ‘materials’ or ‘products’. Passed patterns override patterns defined
110 in environment variables or config files. See Config docs for details.`,
111 )
112
113 recordCmd.PersistentFlags().StringVar(
114 &spiffeUDS,
115 "spiffe-workload-api-path",
116 "",
117 "UDS path for SPIFFE workload API",
118 )
119
120 recordCmd.PersistentFlags().BoolVar(
121 &lineNormalization,
122 "normalize-line-endings",
123 false,
124 `Enable line normalization in order to support different
125 operating systems. It is done by replacing all line separators
126 with a new line character.`,
127 )
128
129 recordCmd.PersistentFlags().BoolVar(
130 &useDSSE,
131 "use-dsse",
132 false,
133 "Create metadata using DSSE instead of the legacy signature wrapper.",
134 )
135
136 recordCmd.PersistentFlags().BoolVar(
137 &followSymlinkDirs,
138 "follow-symlink-dirs",
139 false,
140 `Follow symlinked directories to their targets. Note: this parameter
141 toggles following linked directories only, linked files are always
142 recorded independently of this parameter.`,
143 )
144
145 recordCmd.MarkPersistentFlagRequired("name")
146
147
148 recordCmd.AddCommand(recordStartCmd)
149
150 recordStartCmd.Flags().StringArrayVarP(
151 &recordMaterialsPaths,
152 "materials",
153 "m",
154 []string{},
155 `Paths to files or directories, whose paths and hashes
156 are stored in the resulting link metadata before the
157 command is executed. Symlinks are followed.`,
158 )
159
160
161 recordCmd.AddCommand(recordStopCmd)
162
163 recordStopCmd.Flags().StringArrayVarP(
164 &recordProductsPaths,
165 "products",
166 "p",
167 []string{},
168 `Paths to files or directories, whose paths and hashes
169 are stored in the resulting link metadata after the
170 command is executed. Symlinks are followed.`,
171 )
172 }
173
174 func recordStart(cmd *cobra.Command, args []string) error {
175 block, err := intoto.InTotoRecordStart(recordStepName, recordMaterialsPaths, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization, followSymlinkDirs, useDSSE)
176 if err != nil {
177 return fmt.Errorf("failed to create start link file: %w", err)
178 }
179
180 prelimLinkName := fmt.Sprintf(intoto.PreliminaryLinkNameFormat, recordStepName, key.KeyID)
181 prelimLinkPath := filepath.Join(outDir, prelimLinkName)
182 err = block.Dump(prelimLinkPath)
183 if err != nil {
184 return fmt.Errorf("failed to write start link file to %s: %w", prelimLinkName, err)
185 }
186
187 return nil
188 }
189
190 func recordStop(cmd *cobra.Command, args []string) error {
191 prelimLinkName := fmt.Sprintf(intoto.PreliminaryLinkNameFormat, recordStepName, key.KeyID)
192 prelimLinkPath := filepath.Join(outDir, prelimLinkName)
193 prelimLinkMb, err := intoto.LoadMetadata(prelimLinkPath)
194 if err != nil {
195 return fmt.Errorf("failed to load start link file at %s: %w", prelimLinkName, err)
196 }
197
198 linkMb, err := intoto.InTotoRecordStop(prelimLinkMb, recordProductsPaths, key, []string{"sha256"}, exclude, lStripPaths, lineNormalization, followSymlinkDirs, useDSSE)
199 if err != nil {
200 return fmt.Errorf("failed to create stop link file: %w", err)
201 }
202
203 linkName := fmt.Sprintf(intoto.LinkNameFormat, recordStepName, key.KeyID)
204 linkPath := filepath.Join(outDir, linkName)
205 err = linkMb.Dump(linkPath)
206 if err != nil {
207 return fmt.Errorf("failed to write stop link file to %s: %w", prelimLinkName, err)
208 }
209
210 return nil
211 }
212
View as plain text