@@ -56,11 +56,43 @@ func (e *AuditMode) Type() string {
5656type auditOpts struct {
5757 branchOptions
5858 verifierOptions
59+ outputOptions
5960 auditDepth int
6061 endingCommit string
6162 auditMode AuditMode
6263}
6364
65+ // AuditCommitResultJSON represents a single commit audit result in JSON format
66+ type AuditCommitResultJSON struct {
67+ Commit string `json:"commit"`
68+ Status string `json:"status"`
69+ VerifiedLevels []string `json:"verified_levels,omitempty"`
70+ PrevCommitMatches * bool `json:"prev_commit_matches,omitempty"`
71+ ProvControls interface {} `json:"prov_controls,omitempty"`
72+ GhControls interface {} `json:"gh_controls,omitempty"`
73+ PrevCommit string `json:"prev_commit,omitempty"`
74+ GhPriorCommit string `json:"gh_prior_commit,omitempty"`
75+ Link string `json:"link,omitempty"`
76+ Error string `json:"error,omitempty"`
77+ }
78+
79+ // AuditResultJSON represents the full audit result in JSON format
80+ type AuditResultJSON struct {
81+ Owner string `json:"owner"`
82+ Repository string `json:"repository"`
83+ Branch string `json:"branch"`
84+ LatestCommit string `json:"latest_commit"`
85+ CommitResults []AuditCommitResultJSON `json:"commit_results"`
86+ Summary * AuditSummary `json:"summary,omitempty"`
87+ }
88+
89+ // AuditSummary provides summary statistics for the audit
90+ type AuditSummary struct {
91+ TotalCommits int `json:"total_commits"`
92+ PassedCommits int `json:"passed_commits"`
93+ FailedCommits int `json:"failed_commits"`
94+ }
95+
6496func (ao * auditOpts ) Validate () error {
6597 errs := []error {
6698 ao .branchOptions .Validate (),
@@ -76,6 +108,8 @@ func (ao *auditOpts) AddFlags(cmd *cobra.Command) {
76108 cmd .PersistentFlags ().StringVar (& ao .endingCommit , "ending-commit" , "" , "The commit to stop auditing at." )
77109 ao .auditMode = AuditModeBasic
78110 cmd .PersistentFlags ().Var (& ao .auditMode , "audit-mode" , "'basic' for limited details (default), 'full' for all details" )
111+ ao .format = OutputFormatText
112+ cmd .PersistentFlags ().Var (& ao .format , "format" , "Output format: 'text' (default) or 'json'" )
79113}
80114
81115func addAudit (parentCmd * cobra.Command ) {
@@ -156,6 +190,41 @@ func printResult(ghc *ghcontrol.GitHubConnection, ar *audit.AuditCommitResult, m
156190 fmt .Printf ("\t link: https://github.com/%s/%s/commit/%s\n " , ghc .Owner (), ghc .Repo (), ar .GhPriorCommit )
157191}
158192
193+ func convertAuditResultToJSON (ghc * ghcontrol.GitHubConnection , ar * audit.AuditCommitResult , mode AuditMode ) AuditCommitResultJSON {
194+ good := ar .IsGood ()
195+ status := "passed"
196+ if ! good {
197+ status = "failed"
198+ }
199+
200+ result := AuditCommitResultJSON {
201+ Commit : ar .Commit ,
202+ Status : status ,
203+ Link : fmt .Sprintf ("https://github.com/%s/%s/commit/%s" , ghc .Owner (), ghc .Repo (), ar .GhPriorCommit ),
204+ }
205+
206+ // Only include details if mode is Full or status is failed
207+ if mode == AuditModeFull || ! good {
208+ if ar .VsaPred != nil {
209+ result .VerifiedLevels = ar .VsaPred .GetVerifiedLevels ()
210+ }
211+
212+ if ar .ProvPred != nil {
213+ result .ProvControls = ar .ProvPred .GetControls ()
214+ result .PrevCommit = ar .ProvPred .GetPrevCommit ()
215+ result .GhPriorCommit = ar .GhPriorCommit
216+ matches := ar .ProvPred .GetPrevCommit () == ar .GhPriorCommit
217+ result .PrevCommitMatches = & matches
218+ }
219+
220+ if ar .GhControlStatus != nil {
221+ result .GhControls = ar .GhControlStatus .Controls
222+ }
223+ }
224+
225+ return result
226+ }
227+
159228func doAudit (auditArgs * auditOpts ) error {
160229 ghc := ghcontrol .NewGhConnection (auditArgs .owner , auditArgs .repository , ghcontrol .BranchToFullRef (auditArgs .branch )).WithAuthToken (githubToken )
161230 ctx := context .Background ()
@@ -169,23 +238,70 @@ func doAudit(auditArgs *auditOpts) error {
169238 return fmt .Errorf ("could not get latest commit for %s" , auditArgs .branch )
170239 }
171240
172- fmt .Printf ("Auditing branch %s starting from revision %s\n " , auditArgs .branch , latestCommit )
241+ // For JSON output, collect all results
242+ if auditArgs .isJSON () {
243+ jsonResult := AuditResultJSON {
244+ Owner : auditArgs .owner ,
245+ Repository : auditArgs .repository ,
246+ Branch : auditArgs .branch ,
247+ LatestCommit : latestCommit ,
248+ CommitResults : []AuditCommitResultJSON {},
249+ }
250+
251+ count := 0
252+ passed := 0
253+ failed := 0
254+
255+ for ar , err := range auditor .AuditBranch (ctx , auditArgs .branch ) {
256+ if ar == nil {
257+ return err
258+ }
259+ commitResult := convertAuditResultToJSON (ghc , ar , auditArgs .auditMode )
260+ if err != nil {
261+ commitResult .Error = err .Error ()
262+ }
263+ if commitResult .Status == "passed" {
264+ passed ++
265+ } else {
266+ failed ++
267+ }
268+ jsonResult .CommitResults = append (jsonResult .CommitResults , commitResult )
269+ if auditArgs .endingCommit != "" && auditArgs .endingCommit == ar .Commit {
270+ break
271+ }
272+ if auditArgs .auditDepth > 0 && count >= auditArgs .auditDepth {
273+ break
274+ }
275+ count ++
276+ }
277+
278+ jsonResult .Summary = & AuditSummary {
279+ TotalCommits : len (jsonResult .CommitResults ),
280+ PassedCommits : passed ,
281+ FailedCommits : failed ,
282+ }
283+
284+ return auditArgs .writeJSON (jsonResult )
285+ }
286+
287+ // Text output (original behavior)
288+ auditArgs .writeText ("Auditing branch %s starting from revision %s\n " , auditArgs .branch , latestCommit )
173289
174290 count := 0
175291 for ar , err := range auditor .AuditBranch (ctx , auditArgs .branch ) {
176292 if ar == nil {
177293 return err
178294 }
179295 if err != nil {
180- fmt . Printf ("\t error: %v\n " , err )
296+ auditArgs . writeText ("\t error: %v\n " , err )
181297 }
182298 printResult (ghc , ar , auditArgs .auditMode )
183299 if auditArgs .endingCommit != "" && auditArgs .endingCommit == ar .Commit {
184- fmt . Printf ("Found ending commit %s\n " , auditArgs .endingCommit )
300+ auditArgs . writeText ("Found ending commit %s\n " , auditArgs .endingCommit )
185301 return nil
186302 }
187303 if auditArgs .auditDepth > 0 && count >= auditArgs .auditDepth {
188- fmt . Printf ("Reached depth limit %d\n " , auditArgs .auditDepth )
304+ auditArgs . writeText ("Reached depth limit %d\n " , auditArgs .auditDepth )
189305 return nil
190306 }
191307 count ++
0 commit comments