@@ -3,6 +3,7 @@ package commands
33import (
44 "archive/zip"
55 "encoding/json"
6+ "encoding/xml"
67 "fmt"
78 "io"
89 "io/fs"
@@ -118,7 +119,11 @@ const (
118119 ScsRepoWarningMsg = "SCS scan warning: Unable to start Scorecard scan due to missing required flags, please include in the ast-cli arguments: " +
119120 "--scs-repo-url your_repo_url --scs-repo-token your_repo_token"
120121 ScsScorecardUnsupportedHostWarningMsg = "SCS scan warning: Unable to run Scorecard scanner due to unsupported repo host. Currently, Scorecard can only run on GitHub Cloud repos."
121- BranchPrimaryPrefix = "--branch-primary="
122+
123+ jsonExt = ".json"
124+ xmlExt = ".xml"
125+ sbomScanTypeErrMsg = "The --sbom-only flag can only be used when the scan type is sca"
126+ BranchPrimaryPrefix = "--branch-primary="
122127)
123128
124129var (
@@ -857,6 +862,9 @@ func scanCreateSubCommand(
857862 createScanCmd .PersistentFlags ().Bool (commonParams .ContainersExcludeNonFinalStagesFlag , false , "Scan only the final deployable image" )
858863 createScanCmd .PersistentFlags ().String (commonParams .ContainersImageTagFilterFlag , "" , "Exclude images by image name and/or tag, ex: \" *dev\" " )
859864
865+ // reading sbom-only flag
866+ createScanCmd .PersistentFlags ().Bool (commonParams .SbomFlag , false , "Scan only the specified SBOM file (supported formats xml or json)" )
867+
860868 return createScanCmd
861869}
862870
@@ -1139,6 +1147,7 @@ func addScaScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasContain
11391147 scaMapConfig := make (map [string ]interface {})
11401148 scaConfig := wrappers.ScaConfig {}
11411149 scaMapConfig [resultsMapType ] = commonParams .ScaType
1150+ isSbom , _ := cmd .PersistentFlags ().GetBool (commonParams .SbomFlag )
11421151 scaConfig .Filter , _ = cmd .Flags ().GetString (commonParams .ScaFilterFlag )
11431152 scaConfig .LastSastScanTime , _ = cmd .Flags ().GetString (commonParams .LastSastScanTime )
11441153 scaConfig .PrivatePackageVersion , _ = cmd .Flags ().GetString (commonParams .ScaPrivatePackageVersionFlag )
@@ -1157,6 +1166,7 @@ func addScaScan(cmd *cobra.Command, resubmitConfig []wrappers.Config, hasContain
11571166 }
11581167 }
11591168 }
1169+ scaConfig .SBom = strconv .FormatBool (isSbom )
11601170 scaMapConfig [resultsMapValue ] = & scaConfig
11611171 return scaMapConfig
11621172 }
@@ -1372,6 +1382,8 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
13721382 var scanTypes []string
13731383 var SCSScanTypes []string
13741384
1385+ isSbomScan , _ := cmd .PersistentFlags ().GetBool (commonParams .SbomFlag )
1386+
13751387 allowedEngines , err := jwtWrapper .GetAllowedEngines (featureFlagsWrapper )
13761388 if err != nil {
13771389 err = errors .Errorf ("Error validating scan types: %v" , err )
@@ -1387,6 +1399,20 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
13871399 userSCSScanTypes = strings .Replace (strings .ToLower (userSCSScanTypes ), commonParams .SCSEnginesFlag , commonParams .ScsType , 1 )
13881400
13891401 scanTypes = strings .Split (userScanTypes , "," )
1402+
1403+ // check scan-types, when sbom-only flag is used
1404+ if isSbomScan {
1405+ if len (scanTypes ) > 1 {
1406+ err = errors .Errorf (sbomScanTypeErrMsg )
1407+ return err
1408+ }
1409+
1410+ if scanTypes [0 ] != "sca" {
1411+ err = errors .Errorf (sbomScanTypeErrMsg )
1412+ return err
1413+ }
1414+ }
1415+
13901416 for _ , scanType := range scanTypes {
13911417 if ! allowedEngines [scanType ] {
13921418 keys := reflect .ValueOf (allowedEngines ).MapKeys ()
@@ -1402,11 +1428,19 @@ func validateScanTypes(cmd *cobra.Command, jwtWrapper wrappers.JWTWrapper, featu
14021428 return err
14031429 }
14041430 } else {
1405- for k := range allowedEngines {
1406- scanTypes = append (scanTypes , k )
1431+ if isSbomScan {
1432+ if allowedEngines ["sca" ] {
1433+ // for sbom-flag, setting scan-type as only "sca"
1434+ scanTypes = append (scanTypes , "sca" )
1435+ } else {
1436+ return errors .Errorf ("sbom needs sca engine to be allowed" )
1437+ }
1438+ } else {
1439+ for k := range allowedEngines {
1440+ scanTypes = append (scanTypes , k )
1441+ }
14071442 }
14081443 }
1409-
14101444 actualScanTypes = strings .Join (scanTypes , "," )
14111445 actualScanTypes = strings .Replace (strings .ToLower (actualScanTypes ), commonParams .IacType , commonParams .KicsType , 1 )
14121446
@@ -1705,8 +1739,24 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
17051739 scaResolverPath , _ := cmd .Flags ().GetString (commonParams .ScaResolverFlag )
17061740
17071741 scaResolverParams , scaResolver := getScaResolverFlags (cmd )
1708-
1709- zipFilePath , directoryPath , err := definePathForZipFileOrDirectory (cmd )
1742+ isSbom , _ := cmd .PersistentFlags ().GetBool (commonParams .SbomFlag )
1743+ var directoryPath string
1744+ if isSbom {
1745+ sbomFile , _ := cmd .Flags ().GetString (commonParams .SourcesFlag )
1746+ isValid , err := isValidJSONOrXML (sbomFile )
1747+ if err != nil {
1748+ return "" , "" , errors .Wrapf (err , "%s: Input in bad format" , failedCreating )
1749+ }
1750+ if ! isValid {
1751+ return "" , "" , errors .Wrapf (err , "%s: Input in bad format" , failedCreating )
1752+ }
1753+ zipFilePath , err = util .CompressFile (sbomFile , "sbomFileCompress" , directoryCreationPrefix )
1754+ if err != nil {
1755+ return "" , "" , errors .Wrapf (err , "%s: Input in bad format" , failedCreating )
1756+ }
1757+ } else {
1758+ zipFilePath , directoryPath , err = definePathForZipFileOrDirectory (cmd )
1759+ }
17101760
17111761 if zipFilePath != "" && scaResolverPath != "" {
17121762 return "" , "" , errors .New ("Scanning Zip files is not supported by ScaResolver.Please use non-zip source" )
@@ -1766,7 +1816,9 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
17661816 }
17671817 }
17681818 } else {
1769- zipFilePath , dirPathErr = compressFolder (directoryPath , sourceDirFilter , userIncludeFilter , scaResolver )
1819+ if ! isSbom {
1820+ zipFilePath , dirPathErr = compressFolder (directoryPath , sourceDirFilter , userIncludeFilter , scaResolver )
1821+ }
17701822 }
17711823 if dirPathErr != nil {
17721824 return "" , "" , dirPathErr
@@ -1780,8 +1832,10 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
17801832 }
17811833 }
17821834
1783- if zipFilePath != "" {
1835+ if zipFilePath != "" && ! isSbom {
17841836 return uploadZip (uploadsWrapper , zipFilePath , unzip , userProvidedZip , featureFlagsWrapper )
1837+ } else if zipFilePath != "" && isSbom {
1838+ return uploadZip (uploadsWrapper , zipFilePath , unzip , false , featureFlagsWrapper )
17851839 }
17861840 return preSignedURL , zipFilePath , nil
17871841}
@@ -3027,8 +3081,9 @@ func deprecatedFlagValue(cmd *cobra.Command, deprecatedFlagKey, inUseFlagKey str
30273081}
30283082
30293083func validateCreateScanFlags (cmd * cobra.Command ) error {
3084+ isSbomScan , _ := cmd .PersistentFlags ().GetBool (commonParams .SbomFlag )
30303085 branch := strings .TrimSpace (viper .GetString (commonParams .BranchKey ))
3031- if branch == "" {
3086+ if branch == "" && ! isSbomScan {
30323087 return errors .Errorf ("%s: Please provide a branch" , failedCreating )
30333088 }
30343089 exploitablePath , _ := cmd .Flags ().GetString (commonParams .ExploitablePathFlag )
@@ -3158,3 +3213,32 @@ func createMinimalZipFile() (string, error) {
31583213
31593214 return outputFile .Name (), nil
31603215}
3216+
3217+ func isValidJSONOrXML (path string ) (bool , error ) {
3218+ ext := strings .ToLower (filepath .Ext (path ))
3219+ if ext != jsonExt && ext != xmlExt {
3220+ return false , fmt .Errorf ("not a JSON/XML file, provide valid JSON/XMl file" )
3221+ }
3222+
3223+ data , err := ioutil .ReadFile (path )
3224+ if err != nil {
3225+ return false , fmt .Errorf ("failed to read file: %w" , err )
3226+ }
3227+
3228+ switch ext {
3229+ case jsonExt :
3230+ var js interface {}
3231+ if err := json .Unmarshal (data , & js ); err != nil {
3232+ return false , fmt .Errorf ("invalid JSON format. %w" , err ) // Invalid JSON
3233+ }
3234+ case xmlExt :
3235+ var x interface {}
3236+ if err := xml .Unmarshal (data , & x ); err != nil {
3237+ return false , fmt .Errorf ("invalid XML format.%w" , err ) // Invalid XML
3238+ }
3239+ default :
3240+ return false , nil
3241+ }
3242+
3243+ return true , nil
3244+ }
0 commit comments