github.com/google/osv-scalibr@v0.4.1/binary/scalibr/scalibr.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // The scalibr command wraps around the SCALIBR library to create a standalone
    16  // CLI for extraction + detection with direct access to the local machine's filesystem.
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"os"
    22  
    23  	"github.com/google/osv-scalibr/binary/cli"
    24  	"github.com/google/osv-scalibr/binary/scanrunner"
    25  	"github.com/google/osv-scalibr/log"
    26  )
    27  
    28  func main() {
    29  	os.Exit(run(os.Args))
    30  }
    31  
    32  func run(args []string) int {
    33  	var subcommand string
    34  	if len(args) >= 2 {
    35  		subcommand = args[1]
    36  	}
    37  	switch subcommand {
    38  	case "scan":
    39  		flags, err := parseFlags(args[2:])
    40  		if err != nil {
    41  			log.Errorf("Error parsing CLI args: %v", err)
    42  			return 1
    43  		}
    44  		return scanrunner.RunScan(flags)
    45  	default:
    46  		// Assume 'scan' if subcommand is not recognized/specified.
    47  		flags, err := parseFlags(args[1:])
    48  		if err != nil {
    49  			log.Errorf("Error parsing CLI args: %v", err)
    50  			return 1
    51  		}
    52  		return scanrunner.RunScan(flags)
    53  	}
    54  }
    55  
    56  func parseFlags(args []string) (*cli.Flags, error) {
    57  	fs := flag.NewFlagSet("scalibr", flag.ExitOnError)
    58  	printVersion := fs.Bool("version", false, `Prints the version of the scanner`)
    59  	root := fs.String("root", "", `The root dir used by detectors and by file walking during extraction (e.g.: "/", "c:\" or ".")`)
    60  	resultFile := fs.String("result", "", "The path of the output scan result file")
    61  	var output cli.Array
    62  	fs.Var(&output, "o", "The path of the scanner outputs in various formats, e.g. -o textproto=result.textproto -o spdx23-json=result.spdx.json -o cdx-json=result.cyclonedx.json")
    63  	pluginsToRun := cli.NewStringListFlag(nil)
    64  	fs.Var(&pluginsToRun, "plugins", "Comma-separated list of plugin to run")
    65  	var extractorOverride cli.Array
    66  	fs.Var(&extractorOverride, "extractor-override", `Override extractor for files matching a glob pattern. Format: <plugin-name>:<glob-pattern>. Can be specified multiple times.`)
    67  	extractorsToRun := cli.NewStringListFlag(nil)
    68  	fs.Var(&extractorsToRun, "extractors", "[Legacy field, prefer using --plugins instead] Comma-separated list of extractor plugins to run")
    69  	detectorsToRun := cli.NewStringListFlag(nil)
    70  	fs.Var(&detectorsToRun, "detectors", "[Legacy field, prefer using --plugins instead] Comma-separated list of detectors plugins to run")
    71  	annotatorsToRun := cli.NewStringListFlag(nil)
    72  	// TODO(b/400910349): Remove once integrators stop using this CLI arg.
    73  	fs.Var(&annotatorsToRun, "annotators", "[Legacy field, prefer using --plugins instead] Comma-separated list of annotators plugins to run")
    74  	var pluginCFG cli.Array
    75  	fs.Var(&pluginCFG, "plugin-config", "Plugin-specific config values. Example: --plugin-config=\"max_file_size_bytes:10000000 plugin_specific: {go_binary: {version_from_content: true} }\", or --plugin-config=go_binary:{version_from_content:true}. See binary/proto/config.proto for more settings")
    76  	ignoreSubDirs := fs.Bool("ignore-sub-dirs", false, "Non-recursive mode: Extract only the files in the top-level directory and skip sub-directories")
    77  	var dirsToSkip cli.StringListFlag
    78  	fs.Var(&dirsToSkip, "skip-dirs", "Comma-separated list of file paths to avoid traversing")
    79  	skipDirRegex := fs.String("skip-dir-regex", "", "If the regex matches a directory, it will be skipped. The regex is matched against the absolute file path.")
    80  	skipDirGlob := fs.String("skip-dir-glob", "", "If the glob matches a directory, it will be skipped. The glob is matched against the absolute file path.")
    81  	maxFileSize := fs.Int("max-file-size", 0, "Files larger than this size in bytes are skipped. If 0, no limit is applied.")
    82  	useGitignore := fs.Bool("use-gitignore", false, "Skip files declared in .gitignore files in source repos.")
    83  	remoteImage := fs.String("remote-image", "", "The remote image to scan. If specified, SCALIBR pulls and scans this image instead of the local filesystem.")
    84  	imageTarball := fs.String("image-tarball", "", "The path to a tarball containing a container image. These are commonly procuded using `docker save`. If specified, SCALIBR scans this image instead of the local filesystem.")
    85  	imageDockerLocal := fs.String("image-local-docker", "", "The docker image that is available in the local filesystem. These are the images from the output of \"docker image ls\". If specified, SCALIBR scans this image. The name of the image MUST also include the tag of the image <image_name>:<image_tag>.")
    86  	imagePlatform := fs.String("image-platform", "", "The platform of the remote image to scan. If not specified, the platform of the client is used. Format is os/arch (e.g. linux/arm64)")
    87  	spdxDocumentName := fs.String("spdx-document-name", "", "The 'name' field for the output SPDX document")
    88  	spdxDocumentNamespace := fs.String("spdx-document-namespace", "", "The 'documentNamespace' field for the output SPDX document")
    89  	spdxCreators := fs.String("spdx-creators", "", "The 'creators' field for the output SPDX document. Format is --spdx-creators=creatortype1:creator1,creatortype2:creator2")
    90  	cdxComponentName := fs.String("cdx-component-name", "", "The 'metadata.component.name' field for the output CDX document")
    91  	cdxComponentType := fs.String("cdx-component-type", "", "The 'metadata.component.type' field for the output CDX document")
    92  	cdxComponentVersion := fs.String("cdx-component-version", "", "The 'metadata.component.version' field for the output CDX document")
    93  	cdxAuthors := fs.String("cdx-authors", "", "The 'authors' field for the output CDX document. Format is --cdx-authors=author1,author2")
    94  	verbose := fs.Bool("verbose", false, "Enable this to print debug logs")
    95  	explicitPlugins := fs.Bool("explicit-plugins", false, "If set, the program will exit with an error if not all plugins' required plugins are explicitly enabled.")
    96  	filterByCapabilities := fs.Bool("filter-by-capabilities", true, "If set, plugins whose requirements (network access, OS, etc.) aren't satisfied by the scanning environment will be silently disabled instead of throwing a validation error.")
    97  	windowsAllDrives := fs.Bool("windows-all-drives", false, "Scan all drives on Windows")
    98  	offline := fs.Bool("offline", false, "Offline mode: Run only plugins that don't require network access")
    99  
   100  	if err := fs.Parse(args); err != nil {
   101  		return nil, err
   102  	}
   103  	pathsToExtract := fs.Args()
   104  
   105  	flags := &cli.Flags{
   106  		PrintVersion:          *printVersion,
   107  		Root:                  *root,
   108  		ResultFile:            *resultFile,
   109  		Output:                output,
   110  		PluginsToRun:          pluginsToRun.GetSlice(),
   111  		ExtractorOverride:     extractorOverride,
   112  		ExtractorsToRun:       extractorsToRun.GetSlice(),
   113  		DetectorsToRun:        detectorsToRun.GetSlice(),
   114  		AnnotatorsToRun:       annotatorsToRun.GetSlice(),
   115  		PluginCFG:             pluginCFG,
   116  		PathsToExtract:        pathsToExtract,
   117  		IgnoreSubDirs:         *ignoreSubDirs,
   118  		DirsToSkip:            dirsToSkip.GetSlice(),
   119  		SkipDirRegex:          *skipDirRegex,
   120  		SkipDirGlob:           *skipDirGlob,
   121  		MaxFileSize:           *maxFileSize,
   122  		UseGitignore:          *useGitignore,
   123  		RemoteImage:           *remoteImage,
   124  		ImageLocal:            *imageDockerLocal,
   125  		ImageTarball:          *imageTarball,
   126  		ImagePlatform:         *imagePlatform,
   127  		SPDXDocumentName:      *spdxDocumentName,
   128  		SPDXDocumentNamespace: *spdxDocumentNamespace,
   129  		SPDXCreators:          *spdxCreators,
   130  		CDXComponentName:      *cdxComponentName,
   131  		CDXComponentType:      *cdxComponentType,
   132  		CDXComponentVersion:   *cdxComponentVersion,
   133  		CDXAuthors:            *cdxAuthors,
   134  		Verbose:               *verbose,
   135  		ExplicitPlugins:       *explicitPlugins,
   136  		FilterByCapabilities:  *filterByCapabilities,
   137  		WindowsAllDrives:      *windowsAllDrives,
   138  		Offline:               *offline,
   139  	}
   140  	if err := cli.ValidateFlags(flags); err != nil {
   141  		return nil, err
   142  	}
   143  	return flags, nil
   144  }