Gitea setup: Core Rule Set blocks attacks, not ordinary Gitea traffic (closes #198)
check / check (push) Waiting to run
check / check (push) Waiting to run
The Gitea setup runs the Core Rule Set in block mode. Rules that refused ordinary Gitea requests are switched off only where they misfired: - deploy/gitea/crs-exclusions.conf, loaded through the new setting SWWAF_CORE_RULE_SET_EXCLUSIONS_FILE before the Core Rule Set's rules, covers Gitea's search, file-name and branch-name fields, Referer, file paths, API attachment names and Actions artifact uploads; - smallwebwaf's defaults gain a seventh change: the registry upload path's from parameter skips 932260. A file that cannot be read, does not parse or names an id from 900000 to 900999 stops servewaf at start, in every mode. internal/waf's tests and script/gitea-setup send the recorded requests in internal/waf/testdata/gitea-requests.txt and three attacks. Model: opus-5-5
This commit is contained in:
@@ -221,12 +221,15 @@ type Config struct {
|
||||
// WAFExemptPaths the path prefixes it does not inspect
|
||||
// (SWWAF_WAF_EXEMPT_PATHS), and WAFBodyLimit the most of a request body
|
||||
// it reads (SWWAF_WAF_BODY_LIMIT), 0 while it is off and it reads none.
|
||||
WAFMode string
|
||||
WAFParanoiaLevel int
|
||||
WAFAnomalyThreshold int
|
||||
WAFDisabledRules []int
|
||||
WAFExemptPaths []string
|
||||
WAFBodyLimit int64
|
||||
// CoreRuleSetExclusionsFile is the path of the file of its exclusions
|
||||
// (SWWAF_CORE_RULE_SET_EXCLUSIONS_FILE), "" while there is none.
|
||||
WAFMode string
|
||||
WAFParanoiaLevel int
|
||||
WAFAnomalyThreshold int
|
||||
WAFDisabledRules []int
|
||||
WAFExemptPaths []string
|
||||
WAFBodyLimit int64
|
||||
CoreRuleSetExclusionsFile string
|
||||
// TrapPaths are the paths a request for which is a clear sign of
|
||||
// attack (SWWAF_TRAP_PATHS), each starting with / and without a ?.
|
||||
TrapPaths []string
|
||||
@@ -523,8 +526,10 @@ func FromEnvironment(lookupEnv func(string) (string, bool)) (*Config, error) {
|
||||
WAFAnomalyThreshold: env.numberOrOff("SWWAF_WAF_ANOMALY_THRESHOLD", "5"),
|
||||
WAFDisabledRules: env.ruleIDs("SWWAF_WAF_DISABLED_RULES",
|
||||
"920340,920420,920440,920640,930130,930140"),
|
||||
WAFExemptPaths: env.pathPrefixes("SWWAF_WAF_EXEMPT_PATHS", ""),
|
||||
WAFBodyLimit: env.wafBodyLimit("SWWAF_WAF_BODY_LIMIT", off),
|
||||
WAFExemptPaths: env.pathPrefixes("SWWAF_WAF_EXEMPT_PATHS", ""),
|
||||
WAFBodyLimit: env.wafBodyLimit("SWWAF_WAF_BODY_LIMIT", off),
|
||||
CoreRuleSetExclusionsFile: env.filePath(
|
||||
"SWWAF_CORE_RULE_SET_EXCLUSIONS_FILE"),
|
||||
TrapPaths: env.trapPaths("SWWAF_TRAP_PATHS"),
|
||||
ErrorBurstThreshold: env.count("SWWAF_ERROR_BURST_THRESHOLD", "30"),
|
||||
LogRemoteURL: env.logRemoteURL("SWWAF_LOG_REMOTE_URL"),
|
||||
@@ -1189,6 +1194,16 @@ func (e *environment) logRemoteURL(name string) *url.URL {
|
||||
return remote
|
||||
}
|
||||
|
||||
// filePath reads a setting that is the path of a file, "" while it is
|
||||
// unset. Its value names a file already, so, unlike the other settings,
|
||||
// it has no _FILE form. Whoever uses the file reads it.
|
||||
func (e *environment) filePath(name string) string {
|
||||
path, _ := e.lookupEnv(name)
|
||||
e.settings = append(e.settings, slog.String(name, path))
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// certificates reads a setting that is the path of a file of PEM
|
||||
// certificates. Unset or empty, it is nil. Its value names a file
|
||||
// already, so, unlike the other settings, it has no _FILE form.
|
||||
|
||||
@@ -87,6 +87,7 @@ const (
|
||||
wafDisabledRules = "SWWAF_WAF_DISABLED_RULES"
|
||||
wafExemptPaths = "SWWAF_WAF_EXEMPT_PATHS"
|
||||
wafBodyLimit = "SWWAF_WAF_BODY_LIMIT"
|
||||
exclusionsFile = "SWWAF_CORE_RULE_SET_EXCLUSIONS_FILE"
|
||||
trapPaths = "SWWAF_TRAP_PATHS"
|
||||
errorBurstThreshold = "SWWAF_ERROR_BURST_THRESHOLD"
|
||||
logRemoteURL = "SWWAF_LOG_REMOTE_URL"
|
||||
@@ -539,13 +540,14 @@ func TestCoreRuleSetSettings(t *testing.T) {
|
||||
environment{
|
||||
wafMode: config.WAFModeDetect, wafParanoiaLevel: "4", wafAnomalyThreshold: "10",
|
||||
wafDisabledRules: "942100, 920350", wafExemptPaths: "/api/, /static/",
|
||||
wafBodyLimit: "32K",
|
||||
wafBodyLimit: "32K", exclusionsFile: "/etc/smallwebwaf/crs-exclusions.conf",
|
||||
},
|
||||
config.Config{
|
||||
WAFMode: config.WAFModeDetect, WAFParanoiaLevel: 4, WAFAnomalyThreshold: 10,
|
||||
WAFDisabledRules: []int{942100, 920350},
|
||||
WAFExemptPaths: []string{"/api/", "/static/"},
|
||||
WAFBodyLimit: 32 << 10,
|
||||
WAFDisabledRules: []int{942100, 920350},
|
||||
WAFExemptPaths: []string{"/api/", "/static/"},
|
||||
WAFBodyLimit: 32 << 10,
|
||||
CoreRuleSetExclusionsFile: "/etc/smallwebwaf/crs-exclusions.conf",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -565,7 +567,8 @@ func TestCoreRuleSetSettings(t *testing.T) {
|
||||
WAFMode: cfg.WAFMode, WAFParanoiaLevel: cfg.WAFParanoiaLevel,
|
||||
WAFAnomalyThreshold: cfg.WAFAnomalyThreshold,
|
||||
WAFDisabledRules: cfg.WAFDisabledRules, WAFExemptPaths: cfg.WAFExemptPaths,
|
||||
WAFBodyLimit: cfg.WAFBodyLimit,
|
||||
WAFBodyLimit: cfg.WAFBodyLimit,
|
||||
CoreRuleSetExclusionsFile: cfg.CoreRuleSetExclusionsFile,
|
||||
}
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("%v gave\n%+v\nwant\n%+v", tc.env, got, tc.want)
|
||||
@@ -2327,6 +2330,7 @@ func TestLogsEachSettingWithItsValue(t *testing.T) {
|
||||
wafDisabledRules: defaultWAFDisabledRules,
|
||||
wafExemptPaths: "",
|
||||
wafBodyLimit: off,
|
||||
exclusionsFile: "",
|
||||
trapPaths: "",
|
||||
errorBurstThreshold: "30",
|
||||
logRemoteURL: "",
|
||||
|
||||
Reference in New Issue
Block a user