mirror of
https://github.com/safedep/vet.git
synced 2025-12-10 13:43:01 -06:00
* Implemented code scan command for building sqlite storage with code analysis data Signed-off-by: Omkar Phansopkar <omkarphansopkar@gmail.com> * Added E2E test for code scan command Signed-off-by: Omkar Phansopkar <omkarphansopkar@gmail.com> * refactor: Migrate pkg/command to internal/command since we use pkg as a independent concern --------- Signed-off-by: Omkar Phansopkar <omkarphansopkar@gmail.com> Co-authored-by: abhisek <abhisek.datta@gmail.com>
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
package code
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
|
|
"github.com/safedep/code/core"
|
|
"github.com/safedep/code/fs"
|
|
"github.com/safedep/code/parser"
|
|
"github.com/safedep/code/plugin"
|
|
"github.com/safedep/code/plugin/depsusage"
|
|
"github.com/safedep/vet/ent"
|
|
"github.com/safedep/vet/pkg/storage"
|
|
)
|
|
|
|
// User define configuration for the scanner
|
|
type ScannerConfig struct {
|
|
// First party application code directories
|
|
AppDirectories []string
|
|
|
|
// 3rd party imported code directories (e.g. Python virtual env, `node_modules` etc.)
|
|
ImportDirectories []string
|
|
|
|
// Regular expressions to exclude files or directories
|
|
// from traversal
|
|
ExcludePatterns []*regexp.Regexp
|
|
|
|
// Languages to scan
|
|
Languages []core.Language
|
|
|
|
// Define callbacks if required
|
|
Callbacks *ScannerCallbackRegistry
|
|
|
|
// Plugin specific configuration
|
|
SkipDependencyUsagePlugin bool
|
|
}
|
|
|
|
type ScannerCallbackRegistry struct {
|
|
// On start of scan
|
|
OnScanStart func() error
|
|
|
|
// On end of scan
|
|
OnScanEnd func() error
|
|
}
|
|
|
|
// Scanner defines the contract for implementing a code scanner. The purpose
|
|
// of code scanner is to scan configured directories for code files,
|
|
// parse them, process them with plugins, persist the plugin results. It
|
|
// should also expose the necessary callbacks for interactive applications
|
|
// to show progress to user.
|
|
type Scanner interface {
|
|
Scan(context.Context) error
|
|
}
|
|
|
|
type scanner struct {
|
|
config ScannerConfig
|
|
storage storage.Storage[*ent.Client]
|
|
writer writerRepository
|
|
}
|
|
|
|
func NewScanner(config ScannerConfig, storage storage.Storage[*ent.Client]) (Scanner, error) {
|
|
client, err := storage.Client()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get ent client: %w", err)
|
|
}
|
|
|
|
writer, err := newWriterRepository(client)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create writer repository: %w", err)
|
|
}
|
|
|
|
return &scanner{
|
|
config: config,
|
|
storage: storage,
|
|
writer: writer,
|
|
}, nil
|
|
}
|
|
|
|
func (s *scanner) Scan(ctx context.Context) error {
|
|
err := s.config.Callbacks.OnScanStart()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute OnScanStart callback: %w", err)
|
|
}
|
|
|
|
fileSystem, err := fs.NewLocalFileSystem(fs.LocalFileSystemConfig{
|
|
AppDirectories: s.config.AppDirectories,
|
|
ImportDirectories: s.config.ImportDirectories,
|
|
ExcludePatterns: s.config.ExcludePatterns,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create file system: %w", err)
|
|
}
|
|
|
|
walker, err := fs.NewSourceWalker(fs.SourceWalkerConfig{}, s.config.Languages)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create source walker: %w", err)
|
|
}
|
|
|
|
treeWalker, err := parser.NewWalkingParser(walker, s.config.Languages)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create tree walker: %w", err)
|
|
}
|
|
|
|
// Configure plugins
|
|
plugins := []core.Plugin{}
|
|
|
|
if !s.config.SkipDependencyUsagePlugin {
|
|
var usageCallback depsusage.DependencyUsageCallback = func(ctx context.Context, evidence *depsusage.UsageEvidence) error {
|
|
_, err := s.writer.SaveDependencyUsage(ctx, evidence)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to save dependency usage: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
plugins = append(plugins, depsusage.NewDependencyUsagePlugin(usageCallback))
|
|
}
|
|
|
|
// Execute plugins
|
|
pluginExecutor, err := plugin.NewTreeWalkPluginExecutor(treeWalker, plugins)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create plugin executor: %w", err)
|
|
}
|
|
|
|
err = pluginExecutor.Execute(ctx, fileSystem)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute plugin: %w", err)
|
|
}
|
|
|
|
err = s.config.Callbacks.OnScanEnd()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute OnScanEnd callback: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|