88 "strings"
99 "time"
1010
11+ cyclonedx "github.com/Vulnetix/vdb-cyclonedx"
1112 "github.com/spf13/cobra"
1213 "github.com/vulnetix/cli/v3/internal/aibom"
1314 "github.com/vulnetix/cli/v3/internal/cdx"
@@ -120,18 +121,15 @@ func runAIBOM(cmd *cobra.Command, args []string) error {
120121 }
121122
122123 // Build the CycloneDX AIBOM once — used both for cyclonedx-json output and
123- // for the backend submission below.
124- ctx := & cdx. ScanContext {
125- Git : gitctx .Collect (rootPath ),
126- System : gitctx . CollectSystemInfo (),
127- ToolVersion : version ,
124+ // for the backend submission below. The format (build + validate) lives in
125+ // the shared vdb-cyclonedx module so producers and consumers agree.
126+ gitCtx := gitctx .Collect (rootPath )
127+ bomData , err := cyclonedx . BuildAIBOM ( det , cyclonedx. AIBOMOptions {
128+ SpecVersion : specVersion ,
128129 ToolName : "vulnetix-aibom" ,
129- }
130- bom , err := cdx .BuildAIBOM (det , specVersion , ctx )
131- if err != nil {
132- return err
133- }
134- bomData , err := bom .MarshalValidatedJSON ()
130+ ToolVersion : version ,
131+ Project : aibomProject (gitCtx , gitctx .CollectSystemInfo ()),
132+ })
135133 if err != nil {
136134 return err
137135 }
@@ -140,7 +138,7 @@ func runAIBOM(cmd *cobra.Command, args []string) error {
140138 // fails the command, and community/unauthenticated callers are skipped (the
141139 // server would not persist their data anyway).
142140 if ! noUpload {
143- uploadAIBOM (specVersion , det , bomData , ctx . Git )
141+ uploadAIBOM (specVersion , det , bomData , gitCtx )
144142 }
145143
146144 switch outputFmt {
@@ -184,27 +182,52 @@ func detectAndUploadAIBOM(rootPath string, gitCtx *gitctx.GitContext) {
184182 if err != nil || len (det .Tools )+ len (det .Libraries )+ len (det .Models ) == 0 {
185183 return
186184 }
187- ctx := & cdx.ScanContext {
188- Git : gitCtx ,
189- System : gitctx .CollectSystemInfo (),
190- ToolVersion : version ,
185+ data , err := cyclonedx .BuildAIBOM (det , cyclonedx.AIBOMOptions {
186+ SpecVersion : "1.7" ,
191187 ToolName : "vulnetix-aibom" ,
192- }
193- bom , err := cdx .BuildAIBOM (det , "1.7" , ctx )
194- if err != nil {
195- return
196- }
197- data , err := bom .MarshalValidatedJSON ()
188+ ToolVersion : version ,
189+ Project : aibomProject (gitCtx , gitctx .CollectSystemInfo ()),
190+ })
198191 if err != nil {
199192 return
200193 }
201194 uploadAIBOM ("1.7" , det , data , gitCtx )
202195}
203196
197+ // aibomProject maps the CLI's git/system context to the shared AIBOMProject the
198+ // vdb-cyclonedx builder consumes for metadata.component and git/env properties.
199+ func aibomProject (g * gitctx.GitContext , sys * gitctx.SystemInfo ) * cyclonedx.AIBOMProject {
200+ p := & cyclonedx.AIBOMProject {}
201+ if g != nil {
202+ p .Name = cdx .GitProjectName (g )
203+ p .Version = cdx .GitProjectVersion (g )
204+ p .Branch = g .CurrentBranch
205+ p .Commit = g .CurrentCommit
206+ p .CommitTimestamp = g .HeadCommitTimestamp
207+ p .CommitMessage = g .HeadCommitMessage
208+ p .CommitAuthor = g .HeadCommitAuthor
209+ p .CommitEmail = g .HeadCommitEmail
210+ p .Tags = g .HeadTags
211+ p .IsDirty = g .IsDirty
212+ p .IsWorktree = g .IsWorktree
213+ p .RepoRoot = g .RepoRootPath
214+ p .RemoteURLs = g .RemoteURLs
215+ for _ , c := range g .RecentCommitters {
216+ p .RecentCommitters = append (p .RecentCommitters , cyclonedx.AIBOMContact {Name : c .Name , Email : c .Email })
217+ }
218+ }
219+ if sys != nil {
220+ p .System = & cyclonedx.AIBOMSystem {
221+ Hostname : sys .Hostname , Shell : sys .Shell , OS : sys .OS , Arch : sys .Arch , Username : sys .Username ,
222+ }
223+ }
224+ return p
225+ }
226+
204227// uploadAIBOM submits the AIBOM to POST /v2/cli.ai-bom. It is best-effort:
205228// community/unauthenticated callers are skipped (the server does not persist
206229// their data — see the community no-persist gate) and any error is non-fatal.
207- func uploadAIBOM (specVersion string , det cdx .AIDetections , bomData []byte , git * gitctx.GitContext ) {
230+ func uploadAIBOM (specVersion string , det cyclonedx .AIDetections , bomData []byte , git * gitctx.GitContext ) {
208231 creds , err := auth .LoadCredentials ()
209232 if err != nil || creds == nil || auth .IsCommunity (creds ) {
210233 return
@@ -258,7 +281,7 @@ func writeAIBOMOutput(path string, data []byte) error {
258281 return nil
259282}
260283
261- func renderAIBOMTable (cmd * cobra.Command , det cdx .AIDetections ) error {
284+ func renderAIBOMTable (cmd * cobra.Command , det cyclonedx .AIDetections ) error {
262285 dctx := display .FromCommand (cmd )
263286 if dctx .IsJSON () {
264287 data , err := json .MarshalIndent (det , "" , " " )
0 commit comments