From db3da7da2d6ca451505546ca37f439e4e7afc331 Mon Sep 17 00:00:00 2001 From: Max Fisher <112151114+maxfisher-g@users.noreply.github.com> Date: Fri, 9 Jun 2023 11:13:19 +0900 Subject: [PATCH] Support custom sandbox and analysis script path for dynamic analysis (#744) * plumbing to support custom dynamic analysis sandbox and command Signed-off-by: Max Fisher * support custom sandbox and analysis command in local analysis Signed-off-by: Max Fisher * address review comments Signed-off-by: Max Fisher * fix static analysis args generation Signed-off-by: Max Fisher * fix nil pointer dereference in download Signed-off-by: Max Fisher * sandbox.Sandbox: add argument names to Run() and improve documentation comments Signed-off-by: Max Fisher --------- Signed-off-by: Max Fisher --- cmd/analyze/main.go | 16 +++-- cmd/worker/main.go | 6 +- internal/dynamicanalysis/analysis.go | 6 +- internal/dynamicanalysis/sandbox_args.go | 48 ++++++++++++++ internal/pkgmanager/crates.io.go | 4 -- internal/pkgmanager/ecosystem.go | 18 ++--- internal/pkgmanager/npm.go | 4 -- internal/pkgmanager/package.go | 23 ------- internal/pkgmanager/packagist.go | 4 -- internal/pkgmanager/pypi.go | 4 -- internal/pkgmanager/rubygems.go | 4 -- internal/sandbox/sandbox.go | 56 ++++++++-------- internal/worker/rundynamic.go | 84 +++++++++++++++--------- internal/worker/runstatic.go | 9 ++- internal/worker/sandbox_options.go | 2 + 15 files changed, 158 insertions(+), 130 deletions(-) create mode 100644 internal/dynamicanalysis/sandbox_args.go diff --git a/cmd/analyze/main.go b/cmd/analyze/main.go index 8d48fe12..4e987dd8 100644 --- a/cmd/analyze/main.go +++ b/cmd/analyze/main.go @@ -30,6 +30,8 @@ var ( staticUpload = flag.String("upload-static", "", "bucket path for uploading static analysis results") uploadFileWriteInfo = flag.String("upload-file-write-info", "", "bucket path for uploading information from file writes") offline = flag.Bool("offline", false, "disables sandbox network access") + customSandbox = flag.String("sandbox-image", "", "override default dynamic analysis sandbox with custom image") + customAnalysisCmd = flag.String("analysis-command", "", "override default dynamic analysis script path (use with custom sandbox image)") listModes = flag.Bool("list-modes", false, "prints out a list of available analysis modes") help = flag.Bool("help", false, "print help on available options") analysisMode = utils.CommaSeparatedFlags("mode", []string{"static", "dynamic"}, @@ -103,21 +105,25 @@ func dynamicAnalysis(pkg *pkgmanager.Pkg, resultStores worker.ResultStores) { sbOpts := append(worker.DynamicSandboxOptions(), makeSandboxOptions()...) - data, lastRunPhase, lastStatus, err := worker.RunDynamicAnalysis(pkg, sbOpts) + if *customSandbox != "" { + sbOpts = append(sbOpts, sandbox.Image(*customSandbox)) + } + + result, err := worker.RunDynamicAnalysis(pkg, sbOpts, *customAnalysisCmd) if err != nil { log.Error("Dynamic analysis aborted (run error)", "error", err) return } // this is only valid if RunDynamicAnalysis() returns nil err - if lastStatus != analysis.StatusCompleted { + if result.LastStatus != analysis.StatusCompleted { log.Warn("Dynamic analysis phase did not complete successfully", - "lastRunPhase", lastRunPhase, - "status", lastStatus) + "lastRunPhase", result.LastRunPhase, + "status", result.LastStatus) } ctx := context.Background() - if err := worker.SaveDynamicAnalysisData(ctx, pkg, resultStores, data); err != nil { + if err := worker.SaveDynamicAnalysisData(ctx, pkg, resultStores, result.AnalysisData); err != nil { log.Error("Upload error", "error", err) } } diff --git a/cmd/worker/main.go b/cmd/worker/main.go index f67d0139..d99c9957 100644 --- a/cmd/worker/main.go +++ b/cmd/worker/main.go @@ -153,13 +153,14 @@ func handleMessage(ctx context.Context, msg *pubsub.Message, packagesBucket *blo } dynamicSandboxOpts := append(worker.DynamicSandboxOptions(), sandboxOpts...) - results, _, _, err := worker.RunDynamicAnalysis(pkg, dynamicSandboxOpts) + result, err := worker.RunDynamicAnalysis(pkg, dynamicSandboxOpts, "") if err != nil { return err } staticSandboxOpts := append(worker.StaticSandboxOptions(), sandboxOpts...) var staticResults analysisrun.StaticAnalysisResults + // TODO run static analysis first and remove the if statement below if resultStores.StaticAnalysis != nil { staticResults, _, err = worker.RunStaticAnalysis(pkg, staticSandboxOpts, staticanalysis.All) if err != nil { @@ -170,8 +171,7 @@ func handleMessage(ctx context.Context, msg *pubsub.Message, packagesBucket *blo if err := worker.SaveStaticAnalysisData(ctx, pkg, resultStores, staticResults); err != nil { return err } - - if err := worker.SaveDynamicAnalysisData(ctx, pkg, resultStores, results); err != nil { + if err := worker.SaveDynamicAnalysisData(ctx, pkg, resultStores, result.AnalysisData); err != nil { return err } diff --git a/internal/dynamicanalysis/analysis.go b/internal/dynamicanalysis/analysis.go index fed9f313..fae4f475 100644 --- a/internal/dynamicanalysis/analysis.go +++ b/internal/dynamicanalysis/analysis.go @@ -30,7 +30,7 @@ var resultError = &Result{ }, } -func Run(sb sandbox.Sandbox, args []string) (*Result, error) { +func Run(sb sandbox.Sandbox, command string, args []string) (*Result, error) { log.Info("Running dynamic analysis", "args", args) @@ -46,8 +46,8 @@ func Run(sb sandbox.Sandbox, args []string) (*Result, error) { // Run the command log.Debug("Running dynamic analysis command", - "args", args) - r, err := sb.Run(args...) + "command", command, "args", args) + r, err := sb.Run(command, args...) if err != nil { return resultError, fmt.Errorf("sandbox failed (%w)", err) } diff --git a/internal/dynamicanalysis/sandbox_args.go b/internal/dynamicanalysis/sandbox_args.go new file mode 100644 index 00000000..409c7187 --- /dev/null +++ b/internal/dynamicanalysis/sandbox_args.go @@ -0,0 +1,48 @@ +package dynamicanalysis + +import ( + "github.com/ossf/package-analysis/internal/pkgmanager" + "github.com/ossf/package-analysis/pkg/api/analysisrun" + "github.com/ossf/package-analysis/pkg/api/pkgecosystem" +) + +// defaultCommand returns the path (in the default sandbox image) +// of the default dynamic analysis command for the ecosystem +var defaultCommand = map[pkgecosystem.Ecosystem]string{ + pkgecosystem.CratesIO: "/usr/local/bin/analyze-rust.py", + pkgecosystem.NPM: "/usr/local/bin/analyze-node.js", + pkgecosystem.Packagist: "/usr/local/bin/analyze-php.php", + pkgecosystem.PyPI: "/usr/local/bin/analyze-python.py", + pkgecosystem.RubyGems: "/usr/local/bin/analyze-ruby.rb", +} + +func DefaultCommand(ecosystem pkgecosystem.Ecosystem) string { + cmd := defaultCommand[ecosystem] + if cmd == "" { + panic("unsupported ecosystem: " + ecosystem) + } + return cmd +} + +// MakeAnalysisArgs returns the arguments to pass to the dynamic analysis command in the sandbox +// for the given phase of dynamic analysis on a package. The actual analysis command +// depends on the ecosystem, see pkgmanager.PkgManager.DynamicAnalysisCommand() +func MakeAnalysisArgs(p *pkgmanager.Pkg, phase analysisrun.DynamicPhase) []string { + args := make([]string, 0) + + if p.IsLocal() { + args = append(args, "--local", p.LocalPath()) + } else if p.Version() != "" { + args = append(args, "--version", p.Version()) + } + + if phase == "" { + args = append(args, "all") + } else { + args = append(args, string(phase)) + } + + args = append(args, p.Name()) + + return args +} diff --git a/internal/pkgmanager/crates.io.go b/internal/pkgmanager/crates.io.go index f8f3f4cf..f0a21796 100644 --- a/internal/pkgmanager/crates.io.go +++ b/internal/pkgmanager/crates.io.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -34,8 +33,5 @@ func getCratesLatest(pkg string) (string, error) { var cratesPkgManager = PkgManager{ ecosystem: pkgecosystem.CratesIO, - image: combinedDynamicAnalysisImage, - command: "/usr/local/bin/analyze-rust.py", latestVersion: getCratesLatest, - dynamicPhases: analysisrun.DefaultDynamicPhases(), } diff --git a/internal/pkgmanager/ecosystem.go b/internal/pkgmanager/ecosystem.go index 698552c8..0280dff7 100644 --- a/internal/pkgmanager/ecosystem.go +++ b/internal/pkgmanager/ecosystem.go @@ -4,23 +4,17 @@ import ( "fmt" "strings" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) // PkgManager represents how packages from a common ecosystem are accessed. type PkgManager struct { ecosystem pkgecosystem.Ecosystem - image string - command string latestVersion func(string) (string, error) archiveURL func(string, string) (string, error) extractArchive func(string, string) error - dynamicPhases []analysisrun.DynamicPhase } -const combinedDynamicAnalysisImage = "gcr.io/ossf-malware-analysis/dynamic-analysis" - var ( supportedPkgManagers = map[pkgecosystem.Ecosystem]*PkgManager{ npmPkgManager.ecosystem: &npmPkgManager, @@ -40,12 +34,8 @@ func (p *PkgManager) String() string { return string(p.ecosystem) } -func (p *PkgManager) DynamicAnalysisImage() string { - return p.image -} - -func (p *PkgManager) DynamicPhases() []analysisrun.DynamicPhase { - return p.dynamicPhases +func (p *PkgManager) Ecosystem() pkgecosystem.Ecosystem { + return p.ecosystem } func (p *PkgManager) Latest(name string) (*Pkg, error) { @@ -83,6 +73,10 @@ func (p *PkgManager) DownloadArchive(name, version, directory string) (string, e return "", fmt.Errorf("no directory specified") } + if p.archiveURL == nil { + return "", fmt.Errorf("not yet implemented for %s", p.Ecosystem()) + } + downloadURL, err := p.archiveURL(name, version) if err != nil { return "", err diff --git a/internal/pkgmanager/npm.go b/internal/pkgmanager/npm.go index 29a00be2..6d0e39bc 100644 --- a/internal/pkgmanager/npm.go +++ b/internal/pkgmanager/npm.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/ossf/package-analysis/internal/utils" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -73,10 +72,7 @@ func getNPMArchiveURL(pkgName, version string) (string, error) { var npmPkgManager = PkgManager{ ecosystem: pkgecosystem.NPM, - image: combinedDynamicAnalysisImage, - command: "/usr/local/bin/analyze-node.js", latestVersion: getNPMLatest, archiveURL: getNPMArchiveURL, extractArchive: utils.ExtractTarGzFile, - dynamicPhases: analysisrun.DefaultDynamicPhases(), } diff --git a/internal/pkgmanager/package.go b/internal/pkgmanager/package.go index c1911b3e..28936083 100644 --- a/internal/pkgmanager/package.go +++ b/internal/pkgmanager/package.go @@ -1,7 +1,6 @@ package pkgmanager import ( - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -39,25 +38,3 @@ func (p *Pkg) Manager() *PkgManager { func (p *Pkg) LocalPath() string { return p.local } - -// Command returns the analysis command for the package. -func (p *Pkg) Command(phase analysisrun.DynamicPhase) []string { - args := make([]string, 0) - args = append(args, p.manager.command) - - if p.local != "" { - args = append(args, "--local", p.local) - } else if p.version != "" { - args = append(args, "--version", p.version) - } - - if phase == "" { - args = append(args, "all") - } else { - args = append(args, string(phase)) - } - - args = append(args, p.name) - - return args -} diff --git a/internal/pkgmanager/packagist.go b/internal/pkgmanager/packagist.go index ffb2fb90..08941cb1 100644 --- a/internal/pkgmanager/packagist.go +++ b/internal/pkgmanager/packagist.go @@ -6,7 +6,6 @@ import ( "net/http" "time" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -51,8 +50,5 @@ func getPackagistLatest(pkg string) (string, error) { var packagistPkgManager = PkgManager{ ecosystem: pkgecosystem.Packagist, - image: combinedDynamicAnalysisImage, - command: "/usr/local/bin/analyze-php.php", latestVersion: getPackagistLatest, - dynamicPhases: analysisrun.DefaultDynamicPhases(), } diff --git a/internal/pkgmanager/pypi.go b/internal/pkgmanager/pypi.go index 4715571e..8a20f51d 100644 --- a/internal/pkgmanager/pypi.go +++ b/internal/pkgmanager/pypi.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/ossf/package-analysis/internal/utils" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -80,10 +79,7 @@ func getPyPIArchiveURL(pkgName, version string) (string, error) { var pypiPkgManager = PkgManager{ ecosystem: pkgecosystem.PyPI, - image: combinedDynamicAnalysisImage, - command: "/usr/local/bin/analyze-python.py", latestVersion: getPyPILatest, archiveURL: getPyPIArchiveURL, extractArchive: utils.ExtractTarGzFile, - dynamicPhases: analysisrun.DefaultDynamicPhases(), } diff --git a/internal/pkgmanager/rubygems.go b/internal/pkgmanager/rubygems.go index 26c22028..b158eda7 100644 --- a/internal/pkgmanager/rubygems.go +++ b/internal/pkgmanager/rubygems.go @@ -5,7 +5,6 @@ import ( "fmt" "net/http" - "github.com/ossf/package-analysis/pkg/api/analysisrun" "github.com/ossf/package-analysis/pkg/api/pkgecosystem" ) @@ -32,8 +31,5 @@ func getRubyGemsLatest(pkg string) (string, error) { var rubygemsPkgManager = PkgManager{ ecosystem: pkgecosystem.RubyGems, - image: combinedDynamicAnalysisImage, - command: "/usr/local/bin/analyze-ruby.rb", latestVersion: getRubyGemsLatest, - dynamicPhases: analysisrun.DefaultDynamicPhases(), } diff --git a/internal/sandbox/sandbox.go b/internal/sandbox/sandbox.go index bcd1c2f3..5f7690d1 100644 --- a/internal/sandbox/sandbox.go +++ b/internal/sandbox/sandbox.go @@ -80,26 +80,20 @@ func (r *RunResult) Stderr() []byte { } type Sandbox interface { - // Run will execute the supplied command and args in the sandbox. - // - // The container used to execute the command is reused until Clean() is - // called. - // - // If there is an error while using the sandbox an error will be returned. - // - // The result of the supplied command will be returned in an instance of - // RunResult. - Run(...string) (*RunResult, error) - - // Clean cleans up a Sandbox. - // - // Once called the Sandbox cannot be used again. + // Run executes the supplied command and args in the sandbox. + // Multiple calls to Run will reuse the same container state, + // until Clean() is called. + // The returned RunResult stores information about the execution. + // If any error occurs, it is returned with a partial RunResult. + Run(command string, args ...string) (*RunResult, error) + + // Clean cleans up the Sandbox. Once called, the Sandbox cannot be used again. Clean() error // CopyToHost copies a path in the sandbox (src) to a path in the host (dest). // It will fail if the src path does not exist in the sandbox. - // See https://docs.podman.io/en/latest/markdown/podman-cp.1.html for - // semantics of src and dest paths. + // See https://docs.podman.io/en/latest/markdown/podman-cp.1.html + // for semantics of src and dest paths. // Caution: files coming out of the sandbox are untrusted and proper validation // should be performed on the file before use. CopyToHost(src, dest string) error @@ -175,9 +169,9 @@ type ( func (o option) set(sb *podmanSandbox) { o(sb) } -func New(image string, options ...Option) Sandbox { +func New(options ...Option) Sandbox { sb := &podmanSandbox{ - image: image, + image: "", tag: "", container: "", noPull: false, @@ -194,9 +188,18 @@ func New(image string, options ...Option) Sandbox { for _, o := range options { o.set(sb) } + + if sb.image == "" { + log.Fatal("image is required") + } return sb } +// Image sets the image to be used by the sandbox. It is a required option. +func Image(image string) Option { + return option(func(sb *podmanSandbox) { sb.image = image }) +} + // EnableRawSockets allows use of raw sockets in the sandbox. func EnableRawSockets() Option { return option(func(sb *podmanSandbox) { sb.rawSockets = true }) @@ -245,7 +248,6 @@ func NoPull() Option { } // Volume can be used to specify an additional volume map into the container. -// // src is the path in the host that will be mapped to the dest path. func Volume(src, dest string) Option { return option(func(sb *podmanSandbox) { @@ -375,12 +377,8 @@ func (s *podmanSandbox) forceStopContainer() error { s.container) } -func (s *podmanSandbox) execContainerCmd(execArgs []string) *exec.Cmd { - args := []string{ - "exec", - s.container, - } - args = append(args, execArgs...) +func (s *podmanSandbox) execContainerCmd(execCmd string, execArgs []string) *exec.Cmd { + args := append([]string{"exec", s.container, execCmd}, execArgs...) return podman(args...) } @@ -436,7 +434,7 @@ func (s *podmanSandbox) init() error { } // Run implements the Sandbox interface. -func (s *podmanSandbox) Run(args ...string) (*RunResult, error) { +func (s *podmanSandbox) Run(command string, args ...string) (*RunResult, error) { if err := s.init(); err != nil { return &RunResult{}, err } @@ -467,10 +465,10 @@ func (s *podmanSandbox) Run(args ...string) (*RunResult, error) { // Prepare stdout and stderr writers logOut := log.Writer(log.InfoLevel, - "args", args) + "command", command, "args", args) defer logOut.Close() logErr := log.Writer(log.WarnLevel, - "args", args) + "command", command, "args", args) defer logErr.Close() outWriters := []io.Writer{&stdout} @@ -500,7 +498,7 @@ func (s *podmanSandbox) Run(args ...string) (*RunResult, error) { } // Run the command in the sandbox - cmd := s.execContainerCmd(args) + cmd := s.execContainerCmd(command, args) cmd.Stdout = outWriter cmd.Stderr = errWriter diff --git a/internal/worker/rundynamic.go b/internal/worker/rundynamic.go index 2f84d7dd..599fc22d 100644 --- a/internal/worker/rundynamic.go +++ b/internal/worker/rundynamic.go @@ -16,8 +16,35 @@ import ( "github.com/ossf/package-analysis/pkg/api/analysisrun" ) +// defaultDynamicAnalysisImage is container image name of the default dynamic analysis sandbox +const defaultDynamicAnalysisImage = "gcr.io/ossf-malware-analysis/dynamic-analysis" + var nonSpaceControlChars = regexp.MustCompile("[\x00-\x08\x0b-\x1f\x7f]") +/* +DynamicAnalysisResult holds all the results from RunDynamicAnalysis + +AnalysisData: Map of each successfully run phase to a summary of +the corresponding dynamic analysis result. This summary has two parts: +1. StraceSummary: information about system calls performed by the process +2. FileWrites: list of files which were written to and counts of bytes written + +Note, if error is not nil, then results[lastRunPhase] is nil. + +LastRunPhase: the last phase that was run. If error is non-nil, this phase did not +successfully complete, and the results for this phase are not recorded. +Otherwise, the results contain data for this phase, even in cases where the +sandboxed process terminated abnormally. + +Status: the status of the last run phase if it completed without error, else empty +*/ + +type DynamicAnalysisResult struct { + AnalysisData analysisrun.DynamicAnalysisResults + LastRunPhase analysisrun.DynamicPhase + LastStatus analysis.Status +} + func retrieveExecutionLog(sb sandbox.Sandbox) (string, error) { // retrieve execution log back to host executionLogDir, err := os.MkdirTemp("", "") @@ -48,30 +75,22 @@ func retrieveExecutionLog(sb sandbox.Sandbox) (string, error) { } /* -RunDynamicAnalysis runs dynamic analysis on the given package in the sandbox -provided, across all phases (e.g. import, install) valid in the package ecosystem. -Status and errors are logged to stdout. There are 4 return values: - -DynamicAnalysisResults: Map of each successfully run phase to a summary of -the corresponding dynamic analysis result. This summary has two parts: -1. StraceSummary: information about system calls performed by the process -2. FileWrites: list of files which were written to and counts of bytes written - -Note, if error is not nil, then results[lastRunPhase] is nil. +RunDynamicAnalysis runs dynamic analysis on the given package across the phases +valid in the package ecosystem (e.g. import, install), in a sandbox created +using the provided options. The options must specify the sandbox image to use. -RunPhase: the last phase that was run. If error is non-nil, this phase did not -successfully complete, and the results for this phase are not recorded. -Otherwise, the results contain data for this phase, even in cases where the -sandboxed process terminated abnormally. +analysisCmd is an optional argument used to override the default command run +inside the sandbox to perform the analysis. It must support the interface +described under "Adding a new Runtime Analysis script" in sandboxes/README.md -Status: the status of the last run phase if it completed without error, else empty +All data and status relating to analysis (including errors produced by invalid packages) +is returned in the DynamicAnalysisResult struct. Status and errors are also logged to stdout. -error: Any error that occurred in the runtime/sandbox infrastructure. -This does not include errors caused by the package under analysis. +The returned error holds any error that occurred in the runtime/sandbox infrastructure, +excluding from within the analysis itself. In other words, it does not include errors +produced by the package under analysis. */ - -func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisrun.DynamicAnalysisResults, analysisrun.DynamicPhase, analysis.Status, error) { - +func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option, analysisCmd string) (DynamicAnalysisResult, error) { var beforeDynamic runtime.MemStats runtime.ReadMemStats(&beforeDynamic) log.Info("Memory Stats, heap usage before dynamic analysis", @@ -81,13 +100,17 @@ func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisr "heap_usage_before_dynamic_analysis", strconv.FormatUint(beforeDynamic.Alloc, 10), ) - results := analysisrun.DynamicAnalysisResults{ + if analysisCmd == "" { + analysisCmd = dynamicanalysis.DefaultCommand(pkg.Ecosystem()) + } + + data := analysisrun.DynamicAnalysisResults{ StraceSummary: make(analysisrun.DynamicAnalysisStraceSummary), FileWritesSummary: make(analysisrun.DynamicAnalysisFileWritesSummary), FileWriteBufferIds: make(analysisrun.DynamicAnalysisFileWriteBufferIds), } - sb := sandbox.New(pkg.Manager().DynamicAnalysisImage(), sbOpts...) + sb := sandbox.New(sbOpts...) defer func() { if err := sb.Clean(); err != nil { @@ -98,9 +121,10 @@ func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisr var lastRunPhase analysisrun.DynamicPhase var lastStatus analysis.Status var lastError error - for _, phase := range pkg.Manager().DynamicPhases() { + for _, phase := range analysisrun.DefaultDynamicPhases() { startTime := time.Now() - phaseResult, err := dynamicanalysis.Run(sb, pkg.Command(phase)) + args := dynamicanalysis.MakeAnalysisArgs(pkg, phase) + phaseResult, err := dynamicanalysis.Run(sb, analysisCmd, args) lastRunPhase = phase runDuration := time.Since(startTime) @@ -121,10 +145,10 @@ func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisr break } - results.StraceSummary[phase] = &phaseResult.StraceSummary - results.FileWritesSummary[phase] = &phaseResult.FileWritesSummary + data.StraceSummary[phase] = &phaseResult.StraceSummary + data.FileWritesSummary[phase] = &phaseResult.FileWritesSummary lastStatus = phaseResult.StraceSummary.Status - results.FileWriteBufferIds[phase] = phaseResult.FileWriteBufferIds + data.FileWriteBufferIds[phase] = phaseResult.FileWriteBufferIds if lastStatus != analysis.StatusCompleted { // Error caused by an issue with the package (probably). @@ -143,7 +167,7 @@ func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisr if lastError != nil { LogDynamicAnalysisError(pkg, lastRunPhase, lastError) - return results, lastRunPhase, lastStatus, lastError + return DynamicAnalysisResult{data, lastRunPhase, lastStatus}, lastError } LogDynamicAnalysisResult(pkg, lastRunPhase, lastStatus) @@ -153,8 +177,8 @@ func RunDynamicAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option) (analysisr // don't return this error, just log it log.Error("Error retrieving execution log", "error", err) } else { - results.ExecutionLog = analysisrun.DynamicAnalysisExecutionLog(executionLog) + data.ExecutionLog = analysisrun.DynamicAnalysisExecutionLog(executionLog) } - return results, lastRunPhase, lastStatus, nil + return DynamicAnalysisResult{data, lastRunPhase, lastStatus}, nil } diff --git a/internal/worker/runstatic.go b/internal/worker/runstatic.go index 4f73b4b4..f07766e2 100644 --- a/internal/worker/runstatic.go +++ b/internal/worker/runstatic.go @@ -15,8 +15,8 @@ import ( "github.com/ossf/package-analysis/pkg/api/analysisrun" ) -// staticAnalysisImage is the Docker image for the static analysis sandbox. -const staticAnalysisImage = "gcr.io/ossf-malware-analysis/static-analysis" +// defaultStaticAnalysisImage is the default Docker image for the static analysis sandbox. +const defaultStaticAnalysisImage = "gcr.io/ossf-malware-analysis/static-analysis" // staticAnalyzeBinary is the absolute path to the compiled staticanalyze.go binary // inside the static analysis sandbox (see sandboxes/staticanalysis/Dockerfile). @@ -39,7 +39,6 @@ func RunStaticAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option, tasks ...st analyses := utils.Transform(tasks, func(t staticanalysis.Task) string { return string(t) }) args := []string{ - staticAnalyzeBinary, "-ecosystem", pkg.EcosystemName(), "-package", pkg.Name(), "-version", pkg.Version(), @@ -61,14 +60,14 @@ func RunStaticAnalysis(pkg *pkgmanager.Pkg, sbOpts []sandbox.Option, tasks ...st // for saving static analysis results inside the sandbox sbOpts = append(sbOpts, sandbox.Volume(resultsJSONFile, resultsJSONFile)) - sb := sandbox.New(staticAnalysisImage, sbOpts...) + sb := sandbox.New(sbOpts...) defer func() { if err := sb.Clean(); err != nil { log.Error("error cleaning up sandbox", "error", err) } }() - runResult, err := sb.Run(args...) + runResult, err := sb.Run(staticAnalyzeBinary, args...) if err != nil { return nil, "", fmt.Errorf("sandbox failed (%w)", err) } diff --git a/internal/worker/sandbox_options.go b/internal/worker/sandbox_options.go index a048cab9..1b4bc720 100644 --- a/internal/worker/sandbox_options.go +++ b/internal/worker/sandbox_options.go @@ -8,6 +8,7 @@ import ( // static analysis sandboxes. func StaticSandboxOptions() []sandbox.Option { return []sandbox.Option{ + sandbox.Image(defaultStaticAnalysisImage), sandbox.EchoStdErr(), } } @@ -16,6 +17,7 @@ func StaticSandboxOptions() []sandbox.Option { // dynamic analysis sandboxes. func DynamicSandboxOptions() []sandbox.Option { return []sandbox.Option{ + sandbox.Image(defaultDynamicAnalysisImage), sandbox.EnableStrace(), sandbox.EnableRawSockets(), sandbox.EnablePacketLogging(),