github.com/kubeshop/testkube@v1.17.23/contrib/executor/zap/pkg/runner/zap.go (about)

     1  package runner
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"strings"
     7  
     8  	"github.com/creasty/defaults"
     9  	"gopkg.in/yaml.v3"
    10  )
    11  
    12  type ApiOptions struct {
    13  	// target API definition, OpenAPI or SOAP, local file or URL
    14  	Target string `yaml:"target"`
    15  	// config file or URL to use to INFO, IGNORE or FAIL warnings
    16  	Config string `yaml:"config"`
    17  	// show debug messages
    18  	Debug bool `default:"false" yaml:"debug"`
    19  	// short output format - dont show PASSes or example URLs
    20  	Short bool `default:"false" yaml:"short"`
    21  	// minimum level to show: PASS, IGNORE, INFO, WARN or FAIL
    22  	Level string `default:"PASS" yaml:"level"`
    23  	// context file which will be loaded prior to scanning the target
    24  	Context string `yaml:"context"`
    25  	// username to use for authenticated scans - must be defined in the given context file
    26  	User string `yaml:"user"`
    27  	// delay in seconds to wait for passive scanning
    28  	Delay int `yaml:"delay"`
    29  	// max time in minutes to wait for ZAP to start and the passive scan to run
    30  	Time int `default:"0" yaml:"time"`
    31  	// ZAP command line options
    32  	ZapOptions string `yaml:"zap_options"`
    33  	// fail the scan on WARN issues, default true
    34  	FailOnWarn bool `default:"true" yaml:"fail_on_warn"`
    35  	// openapi, soap, or graphql
    36  	Format string `yaml:"format"`
    37  	// the hostname to override in the (remote) OpenAPI spec
    38  	Hostname string `yaml:"hostname"`
    39  	// safe mode this will skip the active scan and perform a baseline scan
    40  	Safe bool `default:"false" yaml:"safe"`
    41  }
    42  
    43  type BaselineOptions struct {
    44  	// target URL including the protocol
    45  	Target string `yaml:"target"`
    46  	// config file or URL to use to INFO, IGNORE or FAIL warnings
    47  	Config string `yaml:"config"`
    48  	// show debug messages
    49  	Debug bool `default:"false" yaml:"debug"`
    50  	// short output format - dont show PASSes or example URLs
    51  	Short bool `default:"false" yaml:"short"`
    52  	// minimum level to show: PASS, IGNORE, INFO, WARN or FAIL
    53  	Level string `default:"PASS" yaml:"level"`
    54  	// context file which will be loaded prior to scanning the target
    55  	Context string `yaml:"context"`
    56  	// username to use for authenticated scans - must be defined in the given context file
    57  	User string `yaml:"user"`
    58  	// delay in seconds to wait for passive scanning
    59  	Delay int `yaml:"delay"`
    60  	// max time in minutes to wait for ZAP to start and the passive scan to run
    61  	Time int `default:"0" yaml:"time"`
    62  	// use the Ajax spider in addition to the traditional one
    63  	Ajax bool `default:"false" yaml:"ajax"`
    64  	// ZAP command line options
    65  	ZapOptions string `yaml:"zap_options"`
    66  	// fail the scan on WARN issues, default true
    67  	FailOnWarn bool `default:"true" yaml:"fail_on_warn"`
    68  	// the number of minutes to spider for (default 1)
    69  	Minutes int `default:"1" yaml:"minutes"`
    70  }
    71  
    72  type FullOptions struct {
    73  	// target URL including the protocol
    74  	Target string `yaml:"target"`
    75  	// config file or URL to use to INFO, IGNORE or FAIL warnings
    76  	Config string `yaml:"config"`
    77  	// show debug messages
    78  	Debug bool `default:"false" yaml:"debug"`
    79  	// short output format - dont show PASSes or example URLs
    80  	Short bool `default:"false" yaml:"short"`
    81  	// minimum level to show: PASS, IGNORE, INFO, WARN or FAIL
    82  	Level string `default:"PASS" yaml:"level"`
    83  	// context file which will be loaded prior to scanning the target
    84  	Context string `yaml:"context"`
    85  	// username to use for authenticated scans - must be defined in the given context file
    86  	User string `yaml:"user"`
    87  	// delay in seconds to wait for passive scanning
    88  	Delay int `yaml:"delay"`
    89  	// max time in minutes to wait for ZAP to start and the passive scan to run
    90  	Time int `default:"0" yaml:"time"`
    91  	// use the Ajax spider in addition to the traditional one
    92  	Ajax bool `default:"false" yaml:"ajax"`
    93  	// ZAP command line options
    94  	ZapOptions string `yaml:"zap_options"`
    95  	// fail the scan on WARN issues, default true
    96  	FailOnWarn bool `default:"true" yaml:"fail_on_warn"`
    97  	// the number of minutes to spider for (default 1)
    98  	Minutes int `default:"1" yaml:"minutes"`
    99  }
   100  
   101  type Options struct {
   102  	API      ApiOptions      `yaml:"api"`
   103  	Baseline BaselineOptions `yaml:"baseline"`
   104  	Full     FullOptions     `yaml:"full"`
   105  }
   106  
   107  func (a *Options) UnmarshalYAML(yamlFile string) (err error) {
   108  	bytes, err := os.ReadFile(yamlFile)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	if err := defaults.Set(a); err != nil {
   114  		return err
   115  	}
   116  
   117  	if err := yaml.Unmarshal(bytes, a); err != nil {
   118  		return err
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  func (a *Options) ToFullScanArgs(filename string) (args []string) {
   125  	args = []string{}
   126  	// don't fail on warnings
   127  	args = appendTargetArg(args, a.Full.Target)
   128  	args = appendConfigArg("full", args, a.Full.Config)
   129  	args = appendMinutesArg(args, a.Full.Minutes)
   130  	args = appendDebugArg(args, a.Full.Debug)
   131  	args = appendDelayArg(args, a.Full.Delay)
   132  	args = appendFailOnWarnArg(args, a.Full.FailOnWarn)
   133  	args = appendAjaxSpiderArg(args, a.Full.Ajax)
   134  	args = appendLevelArg(args, a.Full.Level)
   135  	args = appendContextArg(args, a.Full.Context)
   136  	args = appendShortArg(args, a.Full.Short)
   137  	args = appendTimeArg(args, a.Full.Time)
   138  	args = appendUserArg(args, a.Full.User)
   139  	args = appendZapOptionsArg(args, a.Full.ZapOptions)
   140  	args = appendReportArg(args, filename)
   141  	return args
   142  }
   143  
   144  func (a *Options) ToBaselineScanArgs(filename string) (args []string) {
   145  	args = []string{}
   146  	// don't fail on warnings
   147  	args = appendTargetArg(args, a.Baseline.Target)
   148  	args = appendConfigArg("baseline", args, a.Baseline.Config)
   149  	args = appendMinutesArg(args, a.Baseline.Minutes)
   150  	args = appendDebugArg(args, a.Baseline.Debug)
   151  	args = appendDelayArg(args, a.Baseline.Delay)
   152  	args = appendFailOnWarnArg(args, a.Baseline.FailOnWarn)
   153  	args = appendAjaxSpiderArg(args, a.Baseline.Ajax)
   154  	args = appendLevelArg(args, a.Baseline.Level)
   155  	args = appendContextArg(args, a.Baseline.Context)
   156  	args = appendShortArg(args, a.Baseline.Short)
   157  	args = appendTimeArg(args, a.Baseline.Time)
   158  	args = appendUserArg(args, a.Baseline.User)
   159  	args = appendZapOptionsArg(args, a.Baseline.ZapOptions)
   160  	args = appendReportArg(args, filename)
   161  	args = append(args, "--auto")
   162  	return args
   163  }
   164  
   165  func (a *Options) ToApiScanArgs(filename string) (args []string) {
   166  	args = []string{}
   167  	// don't fail on warnings
   168  	args = appendTargetArg(args, a.API.Target)
   169  	args = appendFormatArg(args, a.API.Format)
   170  	args = appendConfigArg("api", args, a.API.Config)
   171  	args = appendDebugArg(args, a.API.Debug)
   172  	args = appendDelayArg(args, a.API.Delay)
   173  	args = appendFailOnWarnArg(args, a.API.FailOnWarn)
   174  	args = appendLevelArg(args, a.API.Level)
   175  	args = appendContextArg(args, a.API.Context)
   176  	args = appendShortArg(args, a.API.Short)
   177  	args = appendSafeArg(args, a.API.Safe)
   178  	args = appendTimeArg(args, a.API.Time)
   179  	args = appendUserArg(args, a.API.User)
   180  	args = appendHostnameArg(args, a.API.Hostname)
   181  	args = appendZapOptionsArg(args, a.API.ZapOptions)
   182  	args = appendReportArg(args, filename)
   183  	return args
   184  }
   185  
   186  func appendTargetArg(args []string, target string) []string {
   187  	return appendStringArg(args, "-t", target)
   188  }
   189  
   190  func appendFormatArg(args []string, format string) []string {
   191  	return appendStringArg(args, "-f", format)
   192  }
   193  
   194  func appendContextArg(args []string, context string) []string {
   195  	return appendStringArg(args, "-n", context)
   196  }
   197  
   198  func appendUserArg(args []string, user string) []string {
   199  	return appendStringArg(args, "-U", user)
   200  }
   201  
   202  func appendLevelArg(args []string, level string) []string {
   203  	return appendStringArg(args, "-l", level)
   204  }
   205  
   206  func appendHostnameArg(args []string, hostname string) []string {
   207  	return appendStringArg(args, "-O", hostname)
   208  }
   209  
   210  func appendReportArg(args []string, filename string) []string {
   211  	return appendStringArg(args, "-r", filename)
   212  }
   213  
   214  func appendZapOptionsArg(args []string, options string) []string {
   215  	return appendStringArg(args, "-z", options)
   216  }
   217  
   218  func appendStringArg(args []string, arg string, value string) []string {
   219  	if len(value) > 0 {
   220  		return append(args, arg, value)
   221  	} else {
   222  		return args
   223  	}
   224  }
   225  
   226  func appendConfigArg(t string, args []string, format string) []string {
   227  	if len(format) == 0 {
   228  		return args
   229  	}
   230  
   231  	if strings.Index(format, "http") == 0 {
   232  		return append(args, "-u", format)
   233  	}
   234  
   235  	if t == "api" {
   236  		return append(args, "-z", format)
   237  	}
   238  	return append(args, "-c", format)
   239  }
   240  
   241  func appendDebugArg(args []string, debug bool) []string {
   242  	return appendBoolArg(args, "-d", debug)
   243  }
   244  
   245  func appendShortArg(args []string, short bool) []string {
   246  	return appendBoolArg(args, "-s", short)
   247  }
   248  
   249  func appendSafeArg(args []string, safe bool) []string {
   250  	return appendBoolArg(args, "-S", safe)
   251  }
   252  
   253  func appendFailOnWarnArg(args []string, failOnWarn bool) []string {
   254  	return appendBoolArg(args, "-I", !failOnWarn)
   255  }
   256  
   257  func appendAjaxSpiderArg(args []string, ajax bool) []string {
   258  	return appendBoolArg(args, "-j", ajax)
   259  }
   260  
   261  func appendBoolArg(args []string, arg string, flag bool) []string {
   262  	if flag {
   263  		return append(args, arg)
   264  	} else {
   265  		return args
   266  	}
   267  }
   268  
   269  func appendMinutesArg(args []string, minutes int) []string {
   270  	return appendIntArg(args, "-m", minutes)
   271  }
   272  
   273  func appendTimeArg(args []string, time int) []string {
   274  	return appendIntArg(args, "-T", time)
   275  }
   276  
   277  func appendDelayArg(args []string, delay int) []string {
   278  	return appendIntArg(args, "-D", delay)
   279  }
   280  
   281  func appendIntArg(args []string, arg string, value int) []string {
   282  	if value > 0 {
   283  		return append(args, arg, fmt.Sprint(value))
   284  	} else {
   285  		return args
   286  	}
   287  }