Skip to content
Snippets Groups Projects
runner.go 3.88 KiB
Newer Older
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
package core

import (
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"errors"
	"fmt"
	"github.com/panjf2000/ants"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"leviathan/libs"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	"leviathan/utils"
	"os"
	"path"
	"sync"
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
)

type Runner struct {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	Target    string
	Workspace string
	Workflow  libs.Workflow
	Options   *libs.Options
	Reports   []string
	DSL       map[string]interface{}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

func InitRunner(target string, options *libs.Options) (*Runner, error) {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	var runner Runner
	runner.Target = target
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	runner.Options = options

	runner.Workspace = path.Join(options.Environment.Workspaces, utils.CleanPath(target))
	if _, err := os.Stat(runner.Workspace); os.IsNotExist(err) {
		utils.Logger.Debug().Str("workspace", runner.Workspace).Msg("Create workspace folder")
		if err = os.MkdirAll(runner.Workspace, 0700); err != nil {
			return &runner, err
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		}
	}

	if err := runner.readWorkflow(); err != nil {
		return &runner, err
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	}

	runner.initDSL()
	return &runner, nil
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

func (r *Runner) readWorkflow() error {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// check if workflow exists
	utils.Logger.Debug().Str("workflow", r.Options.Scan.Flow).Msg("Check if workflow exists")
	if !WorkflowExists(r.Options.Scan.Flow, r.Options.Environment.Workflows) {
		return errors.New(fmt.Sprintf("%s workflow was not found", r.Options.Scan.Flow))
	}

	// parse workflow
	utils.Logger.Debug().Str("workflow", r.Options.Scan.Flow).Msg("Try to parse workflow")
	workflow, err := WorkflowParse(r)
	if err != nil {
		return err
	}
	r.Workflow = workflow

	// retrieve all modules used by the workflow
	for id, routine := range workflow.Routines {
		var modules []libs.Module
		for _, moduleName := range routine.Modules {
			utils.Logger.Debug().Str("module", moduleName).Msg("Try to parse module")
			module, err := ModuleParse(moduleName, r)
			if err != nil {
				return err
			}

			modules = append(modules, module)
		}
		workflow.Routines[id].ModulesParsed = modules
	}

	return nil
}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
func (r *Runner) Start() {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	utils.Logger.Info().
		Str("runner", r.Target).
		Str("workflow", r.Workflow.Name).
		Msg("Start runner")

	for _, routine := range r.Workflow.Routines {
		var wg sync.WaitGroup
		p, _ := ants.NewPoolWithFunc(r.Options.Scan.Threads*10, func(m interface{}) {
			module := m.(libs.Module)
			r.RunModule(module)
			wg.Done()
		}, ants.WithPreAlloc(true))
		defer p.Release()

		for _, module := range routine.ModulesParsed {
			p.Invoke(module)
			wg.Add(1)
		}

		wg.Wait()
	}

	if !r.Options.Scan.NoClean {
		utils.Logger.Info().Str("workspace", r.Workspace).Msg("Clean workspace")
		CleanWorkspace(r.Workspace, r.Reports)
	}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
}

func (r *Runner) RunModule(m libs.Module) {
	utils.Logger.Info().Str("module", m.Name).Msg("Run module")

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Save the reports generated by the module
	for _, report := range m.Reports {
		utils.Logger.Debug().Str("module", m.Name).Str("report", report).Msg("Add report to the list")
		r.Reports = append(r.Reports, utils.NormalizePath(report))
	}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	// Check if module has to be skipped
	if r.Options.Scan.Resume {
		skipModule := true
		for _, report := range m.Reports {
			if !utils.FileExists(report) {
				skipModule = false
			}
		}
		if skipModule {
			utils.Logger.Info().Str("module", m.Name).Msg("skip module (--resume flag used)")
			return
		}
	}

	// Execute pre_run scripts
	if m.PreRun != nil {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		utils.Logger.Debug().Str("module", m.Name).Msg("Execute Pre_run scripts")
		r.runScripts(m.PreRun)
	}
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	utils.Logger.Debug().Str("module", m.Name).Msg("Execute step")
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
	for _, step := range m.Steps {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		// Check requirements, skip step if requirements not met
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		if !r.CheckRequirements(step.Requirements) {
			continue
		}

Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		// If conditions are validated
		if r.CheckConditions(step.Conditions) {
			// Execute commands
			r.runCommands(step.Commands)

			// Execute scripts
			r.runScripts(step.Scripts)
		} else {
			// Execute commands
			r.runCommands(step.RCommands)

			// Execute scripts
			r.runScripts(step.RScripts)
		}
	if m.PostRun != nil {
Yoann Ono Dit Biot's avatar
Yoann Ono Dit Biot committed
		utils.Logger.Debug().Str("module", m.Name).Msg("Execute post_run scripts")