github.com/crowdsecurity/crowdsec@v1.6.1/pkg/appsec/appsec.go (about)

     1  package appsec
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"os"
     7  	"regexp"
     8  
     9  	"github.com/antonmedv/expr"
    10  	"github.com/antonmedv/expr/vm"
    11  	"github.com/crowdsecurity/crowdsec/pkg/cwhub"
    12  	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
    13  	"github.com/crowdsecurity/crowdsec/pkg/types"
    14  	log "github.com/sirupsen/logrus"
    15  	"gopkg.in/yaml.v2"
    16  )
    17  
    18  type Hook struct {
    19  	Filter     string      `yaml:"filter"`
    20  	FilterExpr *vm.Program `yaml:"-"`
    21  
    22  	OnSuccess string        `yaml:"on_success"`
    23  	Apply     []string      `yaml:"apply"`
    24  	ApplyExpr []*vm.Program `yaml:"-"`
    25  }
    26  
    27  const (
    28  	hookOnLoad = iota
    29  	hookPreEval
    30  	hookPostEval
    31  	hookOnMatch
    32  )
    33  
    34  const (
    35  	BanRemediation     = "ban"
    36  	CaptchaRemediation = "captcha"
    37  	AllowRemediation   = "allow"
    38  )
    39  
    40  func (h *Hook) Build(hookStage int) error {
    41  
    42  	ctx := map[string]interface{}{}
    43  	switch hookStage {
    44  	case hookOnLoad:
    45  		ctx = GetOnLoadEnv(&AppsecRuntimeConfig{})
    46  	case hookPreEval:
    47  		ctx = GetPreEvalEnv(&AppsecRuntimeConfig{}, &ParsedRequest{})
    48  	case hookPostEval:
    49  		ctx = GetPostEvalEnv(&AppsecRuntimeConfig{}, &ParsedRequest{})
    50  	case hookOnMatch:
    51  		ctx = GetOnMatchEnv(&AppsecRuntimeConfig{}, &ParsedRequest{}, types.Event{})
    52  	}
    53  	opts := exprhelpers.GetExprOptions(ctx)
    54  	if h.Filter != "" {
    55  		program, err := expr.Compile(h.Filter, opts...) //FIXME: opts
    56  		if err != nil {
    57  			return fmt.Errorf("unable to compile filter %s : %w", h.Filter, err)
    58  		}
    59  		h.FilterExpr = program
    60  	}
    61  	for _, apply := range h.Apply {
    62  		program, err := expr.Compile(apply, opts...)
    63  		if err != nil {
    64  			return fmt.Errorf("unable to compile apply %s : %w", apply, err)
    65  		}
    66  		h.ApplyExpr = append(h.ApplyExpr, program)
    67  	}
    68  	return nil
    69  }
    70  
    71  type AppsecTempResponse struct {
    72  	InBandInterrupt         bool
    73  	OutOfBandInterrupt      bool
    74  	Action                  string //allow, deny, captcha, log
    75  	UserHTTPResponseCode    int    //The response code to send to the user
    76  	BouncerHTTPResponseCode int    //The response code to send to the remediation component
    77  	SendEvent               bool   //do we send an internal event on rule match
    78  	SendAlert               bool   //do we send an alert on rule match
    79  }
    80  
    81  type AppsecSubEngineOpts struct {
    82  	DisableBodyInspection    bool `yaml:"disable_body_inspection"`
    83  	RequestBodyInMemoryLimit *int `yaml:"request_body_in_memory_limit"`
    84  }
    85  
    86  // runtime version of AppsecConfig
    87  type AppsecRuntimeConfig struct {
    88  	Name           string
    89  	OutOfBandRules []AppsecCollection
    90  
    91  	InBandRules []AppsecCollection
    92  
    93  	DefaultRemediation        string
    94  	RemediationByTag          map[string]string //Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME
    95  	RemediationById           map[int]string
    96  	CompiledOnLoad            []Hook
    97  	CompiledPreEval           []Hook
    98  	CompiledPostEval          []Hook
    99  	CompiledOnMatch           []Hook
   100  	CompiledVariablesTracking []*regexp.Regexp
   101  	Config                    *AppsecConfig
   102  	//CorazaLogger              debuglog.Logger
   103  
   104  	//those are ephemeral, created/destroyed with every req
   105  	OutOfBandTx ExtendedTransaction //is it a good idea ?
   106  	InBandTx    ExtendedTransaction //is it a good idea ?
   107  	Response    AppsecTempResponse
   108  	//should we store matched rules here ?
   109  
   110  	Logger *log.Entry
   111  
   112  	//Set by on_load to ignore some rules on loading
   113  	DisabledInBandRuleIds   []int
   114  	DisabledInBandRulesTags []string //Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME
   115  
   116  	DisabledOutOfBandRuleIds   []int
   117  	DisabledOutOfBandRulesTags []string //Also used for ByName, as the name (for modsec rules) is a tag crowdsec-NAME
   118  }
   119  
   120  type AppsecConfig struct {
   121  	Name                   string   `yaml:"name"`
   122  	OutOfBandRules         []string `yaml:"outofband_rules"`
   123  	InBandRules            []string `yaml:"inband_rules"`
   124  	DefaultRemediation     string   `yaml:"default_remediation"`
   125  	DefaultPassAction      string   `yaml:"default_pass_action"`
   126  	BouncerBlockedHTTPCode int      `yaml:"blocked_http_code"`      //returned to the bouncer
   127  	BouncerPassedHTTPCode  int      `yaml:"passed_http_code"`       //returned to the bouncer
   128  	UserBlockedHTTPCode    int      `yaml:"user_blocked_http_code"` //returned to the user
   129  	UserPassedHTTPCode     int      `yaml:"user_passed_http_code"`  //returned to the user
   130  
   131  	OnLoad            []Hook              `yaml:"on_load"`
   132  	PreEval           []Hook              `yaml:"pre_eval"`
   133  	PostEval          []Hook              `yaml:"post_eval"`
   134  	OnMatch           []Hook              `yaml:"on_match"`
   135  	VariablesTracking []string            `yaml:"variables_tracking"`
   136  	InbandOptions     AppsecSubEngineOpts `yaml:"inband_options"`
   137  	OutOfBandOptions  AppsecSubEngineOpts `yaml:"outofband_options"`
   138  
   139  	LogLevel *log.Level `yaml:"log_level"`
   140  	Logger   *log.Entry `yaml:"-"`
   141  }
   142  
   143  func (w *AppsecRuntimeConfig) ClearResponse() {
   144  	w.Response = AppsecTempResponse{}
   145  	w.Response.Action = w.Config.DefaultPassAction
   146  	w.Response.BouncerHTTPResponseCode = w.Config.BouncerPassedHTTPCode
   147  	w.Response.UserHTTPResponseCode = w.Config.UserPassedHTTPCode
   148  	w.Response.SendEvent = true
   149  	w.Response.SendAlert = true
   150  }
   151  
   152  func (wc *AppsecConfig) LoadByPath(file string) error {
   153  
   154  	wc.Logger.Debugf("loading config %s", file)
   155  
   156  	yamlFile, err := os.ReadFile(file)
   157  	if err != nil {
   158  		return fmt.Errorf("unable to read file %s : %s", file, err)
   159  	}
   160  	err = yaml.UnmarshalStrict(yamlFile, wc)
   161  	if err != nil {
   162  		return fmt.Errorf("unable to parse yaml file %s : %s", file, err)
   163  	}
   164  
   165  	if wc.Name == "" {
   166  		return fmt.Errorf("name cannot be empty")
   167  	}
   168  	if wc.LogLevel == nil {
   169  		lvl := wc.Logger.Logger.GetLevel()
   170  		wc.LogLevel = &lvl
   171  	}
   172  	wc.Logger = wc.Logger.Dup().WithField("name", wc.Name)
   173  	wc.Logger.Logger.SetLevel(*wc.LogLevel)
   174  	return nil
   175  }
   176  
   177  func (wc *AppsecConfig) Load(configName string) error {
   178  	appsecConfigs := hub.GetItemMap(cwhub.APPSEC_CONFIGS)
   179  
   180  	for _, hubAppsecConfigItem := range appsecConfigs {
   181  		if !hubAppsecConfigItem.State.Installed {
   182  			continue
   183  		}
   184  		if hubAppsecConfigItem.Name != configName {
   185  			continue
   186  		}
   187  		wc.Logger.Infof("loading %s", hubAppsecConfigItem.State.LocalPath)
   188  		err := wc.LoadByPath(hubAppsecConfigItem.State.LocalPath)
   189  		if err != nil {
   190  			return fmt.Errorf("unable to load appsec-config %s : %s", hubAppsecConfigItem.State.LocalPath, err)
   191  		}
   192  		return nil
   193  	}
   194  
   195  	return fmt.Errorf("no appsec-config found for %s", configName)
   196  }
   197  
   198  func (wc *AppsecConfig) GetDataDir() string {
   199  	return hub.GetDataDir()
   200  }
   201  
   202  func (wc *AppsecConfig) Build() (*AppsecRuntimeConfig, error) {
   203  	ret := &AppsecRuntimeConfig{Logger: wc.Logger.WithField("component", "appsec_runtime_config")}
   204  
   205  	if wc.BouncerBlockedHTTPCode == 0 {
   206  		wc.BouncerBlockedHTTPCode = http.StatusForbidden
   207  	}
   208  	if wc.BouncerPassedHTTPCode == 0 {
   209  		wc.BouncerPassedHTTPCode = http.StatusOK
   210  	}
   211  
   212  	if wc.UserBlockedHTTPCode == 0 {
   213  		wc.UserBlockedHTTPCode = http.StatusForbidden
   214  	}
   215  	if wc.UserPassedHTTPCode == 0 {
   216  		wc.UserPassedHTTPCode = http.StatusOK
   217  	}
   218  	if wc.DefaultPassAction == "" {
   219  		wc.DefaultPassAction = AllowRemediation
   220  	}
   221  	if wc.DefaultRemediation == "" {
   222  		wc.DefaultRemediation = BanRemediation
   223  	}
   224  
   225  	//set the defaults
   226  	switch wc.DefaultRemediation {
   227  	case BanRemediation, CaptchaRemediation, AllowRemediation:
   228  		//those are the officially supported remediation(s)
   229  	default:
   230  		wc.Logger.Warningf("default '%s' remediation of %s is none of [%s,%s,%s] ensure bouncer compatbility!", wc.DefaultRemediation, wc.Name, BanRemediation, CaptchaRemediation, AllowRemediation)
   231  	}
   232  
   233  	ret.Name = wc.Name
   234  	ret.Config = wc
   235  	ret.DefaultRemediation = wc.DefaultRemediation
   236  
   237  	wc.Logger.Tracef("Loading config %+v", wc)
   238  	//load rules
   239  	for _, rule := range wc.OutOfBandRules {
   240  		wc.Logger.Infof("loading outofband rule %s", rule)
   241  		collections, err := LoadCollection(rule, wc.Logger.WithField("component", "appsec_collection_loader"))
   242  		if err != nil {
   243  			return nil, fmt.Errorf("unable to load outofband rule %s : %s", rule, err)
   244  		}
   245  		ret.OutOfBandRules = append(ret.OutOfBandRules, collections...)
   246  	}
   247  
   248  	wc.Logger.Infof("Loaded %d outofband rules", len(ret.OutOfBandRules))
   249  	for _, rule := range wc.InBandRules {
   250  		wc.Logger.Infof("loading inband rule %s", rule)
   251  		collections, err := LoadCollection(rule, wc.Logger.WithField("component", "appsec_collection_loader"))
   252  		if err != nil {
   253  			return nil, fmt.Errorf("unable to load inband rule %s : %s", rule, err)
   254  		}
   255  		ret.InBandRules = append(ret.InBandRules, collections...)
   256  	}
   257  
   258  	wc.Logger.Infof("Loaded %d inband rules", len(ret.InBandRules))
   259  
   260  	//load hooks
   261  	for _, hook := range wc.OnLoad {
   262  		err := hook.Build(hookOnLoad)
   263  		if err != nil {
   264  			return nil, fmt.Errorf("unable to build on_load hook : %s", err)
   265  		}
   266  		ret.CompiledOnLoad = append(ret.CompiledOnLoad, hook)
   267  	}
   268  
   269  	for _, hook := range wc.PreEval {
   270  		err := hook.Build(hookPreEval)
   271  		if err != nil {
   272  			return nil, fmt.Errorf("unable to build pre_eval hook : %s", err)
   273  		}
   274  		ret.CompiledPreEval = append(ret.CompiledPreEval, hook)
   275  	}
   276  
   277  	for _, hook := range wc.PostEval {
   278  		err := hook.Build(hookPostEval)
   279  		if err != nil {
   280  			return nil, fmt.Errorf("unable to build post_eval hook : %s", err)
   281  		}
   282  		ret.CompiledPostEval = append(ret.CompiledPostEval, hook)
   283  	}
   284  
   285  	for _, hook := range wc.OnMatch {
   286  		err := hook.Build(hookOnMatch)
   287  		if err != nil {
   288  			return nil, fmt.Errorf("unable to build on_match hook : %s", err)
   289  		}
   290  		ret.CompiledOnMatch = append(ret.CompiledOnMatch, hook)
   291  	}
   292  
   293  	//variable tracking
   294  	for _, variable := range wc.VariablesTracking {
   295  		compiledVariableRule, err := regexp.Compile(variable)
   296  		if err != nil {
   297  			return nil, fmt.Errorf("cannot compile variable regexp %s: %w", variable, err)
   298  		}
   299  		ret.CompiledVariablesTracking = append(ret.CompiledVariablesTracking, compiledVariableRule)
   300  	}
   301  	return ret, nil
   302  }
   303  
   304  func (w *AppsecRuntimeConfig) ProcessOnLoadRules() error {
   305  	for _, rule := range w.CompiledOnLoad {
   306  		if rule.FilterExpr != nil {
   307  			output, err := exprhelpers.Run(rule.FilterExpr, GetOnLoadEnv(w), w.Logger, w.Logger.Level >= log.DebugLevel)
   308  			if err != nil {
   309  				return fmt.Errorf("unable to run appsec on_load filter %s : %w", rule.Filter, err)
   310  			}
   311  			switch t := output.(type) {
   312  			case bool:
   313  				if !t {
   314  					w.Logger.Debugf("filter didnt match")
   315  					continue
   316  				}
   317  			default:
   318  				w.Logger.Errorf("Filter must return a boolean, can't filter")
   319  				continue
   320  			}
   321  		}
   322  		for _, applyExpr := range rule.ApplyExpr {
   323  			o, err := exprhelpers.Run(applyExpr, GetOnLoadEnv(w), w.Logger, w.Logger.Level >= log.DebugLevel)
   324  			if err != nil {
   325  				w.Logger.Errorf("unable to apply appsec on_load expr: %s", err)
   326  				continue
   327  			}
   328  			switch t := o.(type) {
   329  			case error:
   330  				w.Logger.Errorf("unable to apply appsec on_load expr: %s", t)
   331  				continue
   332  			default:
   333  			}
   334  		}
   335  	}
   336  	return nil
   337  }
   338  
   339  func (w *AppsecRuntimeConfig) ProcessOnMatchRules(request *ParsedRequest, evt types.Event) error {
   340  
   341  	for _, rule := range w.CompiledOnMatch {
   342  		if rule.FilterExpr != nil {
   343  			output, err := exprhelpers.Run(rule.FilterExpr, GetOnMatchEnv(w, request, evt), w.Logger, w.Logger.Level >= log.DebugLevel)
   344  			if err != nil {
   345  				return fmt.Errorf("unable to run appsec on_match filter %s : %w", rule.Filter, err)
   346  			}
   347  			switch t := output.(type) {
   348  			case bool:
   349  				if !t {
   350  					w.Logger.Debugf("filter didnt match")
   351  					continue
   352  				}
   353  			default:
   354  				w.Logger.Errorf("Filter must return a boolean, can't filter")
   355  				continue
   356  			}
   357  		}
   358  		for _, applyExpr := range rule.ApplyExpr {
   359  			o, err := exprhelpers.Run(applyExpr, GetOnMatchEnv(w, request, evt), w.Logger, w.Logger.Level >= log.DebugLevel)
   360  			if err != nil {
   361  				w.Logger.Errorf("unable to apply appsec on_match expr: %s", err)
   362  				continue
   363  			}
   364  			switch t := o.(type) {
   365  			case error:
   366  				w.Logger.Errorf("unable to apply appsec on_match expr: %s", t)
   367  				continue
   368  			default:
   369  			}
   370  		}
   371  	}
   372  	return nil
   373  }
   374  
   375  func (w *AppsecRuntimeConfig) ProcessPreEvalRules(request *ParsedRequest) error {
   376  	w.Logger.Debugf("processing %d pre_eval rules", len(w.CompiledPreEval))
   377  	for _, rule := range w.CompiledPreEval {
   378  		if rule.FilterExpr != nil {
   379  			output, err := exprhelpers.Run(rule.FilterExpr, GetPreEvalEnv(w, request), w.Logger, w.Logger.Level >= log.DebugLevel)
   380  			if err != nil {
   381  				return fmt.Errorf("unable to run appsec pre_eval filter %s : %w", rule.Filter, err)
   382  			}
   383  			switch t := output.(type) {
   384  			case bool:
   385  				if !t {
   386  					w.Logger.Debugf("filter didnt match")
   387  					continue
   388  				}
   389  			default:
   390  				w.Logger.Errorf("Filter must return a boolean, can't filter")
   391  				continue
   392  			}
   393  		}
   394  		// here means there is no filter or the filter matched
   395  		for _, applyExpr := range rule.ApplyExpr {
   396  			o, err := exprhelpers.Run(applyExpr, GetPreEvalEnv(w, request), w.Logger, w.Logger.Level >= log.DebugLevel)
   397  			if err != nil {
   398  				w.Logger.Errorf("unable to apply appsec pre_eval expr: %s", err)
   399  				continue
   400  			}
   401  			switch t := o.(type) {
   402  			case error:
   403  				w.Logger.Errorf("unable to apply appsec pre_eval expr: %s", t)
   404  				continue
   405  			default:
   406  			}
   407  		}
   408  	}
   409  
   410  	return nil
   411  }
   412  
   413  func (w *AppsecRuntimeConfig) ProcessPostEvalRules(request *ParsedRequest) error {
   414  	for _, rule := range w.CompiledPostEval {
   415  		if rule.FilterExpr != nil {
   416  			output, err := exprhelpers.Run(rule.FilterExpr, GetPostEvalEnv(w, request), w.Logger, w.Logger.Level >= log.DebugLevel)
   417  			if err != nil {
   418  				return fmt.Errorf("unable to run appsec post_eval filter %s : %w", rule.Filter, err)
   419  			}
   420  			switch t := output.(type) {
   421  			case bool:
   422  				if !t {
   423  					w.Logger.Debugf("filter didnt match")
   424  					continue
   425  				}
   426  			default:
   427  				w.Logger.Errorf("Filter must return a boolean, can't filter")
   428  				continue
   429  			}
   430  		}
   431  		// here means there is no filter or the filter matched
   432  		for _, applyExpr := range rule.ApplyExpr {
   433  			o, err := exprhelpers.Run(applyExpr, GetPostEvalEnv(w, request), w.Logger, w.Logger.Level >= log.DebugLevel)
   434  
   435  			if err != nil {
   436  				w.Logger.Errorf("unable to apply appsec post_eval expr: %s", err)
   437  				continue
   438  			}
   439  
   440  			switch t := o.(type) {
   441  			case error:
   442  				w.Logger.Errorf("unable to apply appsec post_eval expr: %s", t)
   443  				continue
   444  			default:
   445  			}
   446  		}
   447  	}
   448  
   449  	return nil
   450  }
   451  
   452  func (w *AppsecRuntimeConfig) RemoveInbandRuleByID(id int) error {
   453  	w.Logger.Debugf("removing inband rule %d", id)
   454  	return w.InBandTx.RemoveRuleByIDWithError(id)
   455  }
   456  
   457  func (w *AppsecRuntimeConfig) RemoveOutbandRuleByID(id int) error {
   458  	w.Logger.Debugf("removing outband rule %d", id)
   459  	return w.OutOfBandTx.RemoveRuleByIDWithError(id)
   460  }
   461  
   462  func (w *AppsecRuntimeConfig) RemoveInbandRuleByTag(tag string) error {
   463  	w.Logger.Debugf("removing inband rule with tag %s", tag)
   464  	return w.InBandTx.RemoveRuleByTagWithError(tag)
   465  }
   466  
   467  func (w *AppsecRuntimeConfig) RemoveOutbandRuleByTag(tag string) error {
   468  	w.Logger.Debugf("removing outband rule with tag %s", tag)
   469  	return w.OutOfBandTx.RemoveRuleByTagWithError(tag)
   470  }
   471  
   472  func (w *AppsecRuntimeConfig) RemoveInbandRuleByName(name string) error {
   473  	tag := fmt.Sprintf("crowdsec-%s", name)
   474  	w.Logger.Debugf("removing inband rule %s", tag)
   475  	return w.InBandTx.RemoveRuleByTagWithError(tag)
   476  }
   477  
   478  func (w *AppsecRuntimeConfig) RemoveOutbandRuleByName(name string) error {
   479  	tag := fmt.Sprintf("crowdsec-%s", name)
   480  	w.Logger.Debugf("removing outband rule %s", tag)
   481  	return w.OutOfBandTx.RemoveRuleByTagWithError(tag)
   482  }
   483  
   484  func (w *AppsecRuntimeConfig) CancelEvent() error {
   485  	w.Logger.Debugf("canceling event")
   486  	w.Response.SendEvent = false
   487  	return nil
   488  }
   489  
   490  // Disable a rule at load time, meaning it will not run for any request
   491  func (w *AppsecRuntimeConfig) DisableInBandRuleByID(id int) error {
   492  	w.DisabledInBandRuleIds = append(w.DisabledInBandRuleIds, id)
   493  	return nil
   494  }
   495  
   496  // Disable a rule at load time, meaning it will not run for any request
   497  func (w *AppsecRuntimeConfig) DisableInBandRuleByName(name string) error {
   498  	tagValue := fmt.Sprintf("crowdsec-%s", name)
   499  	w.DisabledInBandRulesTags = append(w.DisabledInBandRulesTags, tagValue)
   500  	return nil
   501  }
   502  
   503  // Disable a rule at load time, meaning it will not run for any request
   504  func (w *AppsecRuntimeConfig) DisableInBandRuleByTag(tag string) error {
   505  	w.DisabledInBandRulesTags = append(w.DisabledInBandRulesTags, tag)
   506  	return nil
   507  }
   508  
   509  // Disable a rule at load time, meaning it will not run for any request
   510  func (w *AppsecRuntimeConfig) DisableOutBandRuleByID(id int) error {
   511  	w.DisabledOutOfBandRuleIds = append(w.DisabledOutOfBandRuleIds, id)
   512  	return nil
   513  }
   514  
   515  // Disable a rule at load time, meaning it will not run for any request
   516  func (w *AppsecRuntimeConfig) DisableOutBandRuleByName(name string) error {
   517  	tagValue := fmt.Sprintf("crowdsec-%s", name)
   518  	w.DisabledOutOfBandRulesTags = append(w.DisabledOutOfBandRulesTags, tagValue)
   519  	return nil
   520  }
   521  
   522  // Disable a rule at load time, meaning it will not run for any request
   523  func (w *AppsecRuntimeConfig) DisableOutBandRuleByTag(tag string) error {
   524  	w.DisabledOutOfBandRulesTags = append(w.DisabledOutOfBandRulesTags, tag)
   525  	return nil
   526  }
   527  
   528  func (w *AppsecRuntimeConfig) SendEvent() error {
   529  	w.Logger.Debugf("sending event")
   530  	w.Response.SendEvent = true
   531  	return nil
   532  }
   533  
   534  func (w *AppsecRuntimeConfig) SendAlert() error {
   535  	w.Logger.Debugf("sending alert")
   536  	w.Response.SendAlert = true
   537  	return nil
   538  }
   539  
   540  func (w *AppsecRuntimeConfig) CancelAlert() error {
   541  	w.Logger.Debugf("canceling alert")
   542  	w.Response.SendAlert = false
   543  	return nil
   544  }
   545  
   546  func (w *AppsecRuntimeConfig) SetActionByTag(tag string, action string) error {
   547  	if w.RemediationByTag == nil {
   548  		w.RemediationByTag = make(map[string]string)
   549  	}
   550  	w.Logger.Debugf("setting action of %s to %s", tag, action)
   551  	w.RemediationByTag[tag] = action
   552  	return nil
   553  }
   554  
   555  func (w *AppsecRuntimeConfig) SetActionByID(id int, action string) error {
   556  	if w.RemediationById == nil {
   557  		w.RemediationById = make(map[int]string)
   558  	}
   559  	w.Logger.Debugf("setting action of %d to %s", id, action)
   560  	w.RemediationById[id] = action
   561  	return nil
   562  }
   563  
   564  func (w *AppsecRuntimeConfig) SetActionByName(name string, action string) error {
   565  	if w.RemediationByTag == nil {
   566  		w.RemediationByTag = make(map[string]string)
   567  	}
   568  	tag := fmt.Sprintf("crowdsec-%s", name)
   569  	w.Logger.Debugf("setting action of %s to %s", tag, action)
   570  	w.RemediationByTag[tag] = action
   571  	return nil
   572  }
   573  
   574  func (w *AppsecRuntimeConfig) SetAction(action string) error {
   575  	//log.Infof("setting to %s", action)
   576  	w.Logger.Debugf("setting action to %s", action)
   577  	w.Response.Action = action
   578  	return nil
   579  }
   580  
   581  func (w *AppsecRuntimeConfig) SetHTTPCode(code int) error {
   582  	w.Logger.Debugf("setting http code to %d", code)
   583  	w.Response.UserHTTPResponseCode = code
   584  	return nil
   585  }
   586  
   587  type BodyResponse struct {
   588  	Action     string `json:"action"`
   589  	HTTPStatus int    `json:"http_status"`
   590  }
   591  
   592  func (w *AppsecRuntimeConfig) GenerateResponse(response AppsecTempResponse, logger *log.Entry) (int, BodyResponse) {
   593  	var bouncerStatusCode int
   594  
   595  	resp := BodyResponse{Action: response.Action}
   596  	if response.Action == AllowRemediation {
   597  		resp.HTTPStatus = w.Config.UserPassedHTTPCode
   598  		bouncerStatusCode = w.Config.BouncerPassedHTTPCode
   599  	} else { //ban, captcha and anything else
   600  		resp.HTTPStatus = response.UserHTTPResponseCode
   601  		if resp.HTTPStatus == 0 {
   602  			resp.HTTPStatus = w.Config.UserBlockedHTTPCode
   603  		}
   604  		bouncerStatusCode = response.BouncerHTTPResponseCode
   605  		if bouncerStatusCode == 0 {
   606  			bouncerStatusCode = w.Config.BouncerBlockedHTTPCode
   607  		}
   608  	}
   609  
   610  	return bouncerStatusCode, resp
   611  }