Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
766cfceac4 |
No files matched your search
@@ -681,12 +681,8 @@ through. An app's fixes hold for the hostnames `SWW_APPS` lists the app for:
|
|||||||
Set empty, `SWW_APPS` lists no hostname.
|
Set empty, `SWW_APPS` lists no hostname.
|
||||||
- Unset, every app's fixes hold for every hostname. An app's fixes then weaken
|
- Unset, every app's fixes hold for every hostname. An app's fixes then weaken
|
||||||
the check of any other app that serves the same paths or fields.
|
the check of any other app that serves the same paths or fields.
|
||||||
- Any of these mistakes stops the start:
|
- The start stops on an unknown app, a hostname listed twice, an app listed
|
||||||
- an unknown app;
|
twice for one hostname, or an entry without a colon.
|
||||||
- a hostname that is not a DNS name, such as `*.example.com`;
|
|
||||||
- a hostname listed twice;
|
|
||||||
- an app listed twice for one hostname;
|
|
||||||
- an entry without a colon.
|
|
||||||
|
|
||||||
`gitea` is the only app supported. Each app's fixes are in one file, such as
|
`gitea` is the only app supported. Each app's fixes are in one file, such as
|
||||||
`internal/waf/gitea.conf`, and one entry of `apps` in `internal/waf/waf.go`.
|
`internal/waf/gitea.conf`, and one entry of `apps` in `internal/waf/waf.go`.
|
||||||
|
|||||||
+2
-6
@@ -210,12 +210,8 @@ between the two (see "The image's command" below).
|
|||||||
- A hostname not listed gets no app's fixes, so every rule checks it in
|
- A hostname not listed gets no app's fixes, so every rule checks it in
|
||||||
full. Set empty, it lists no hostname.
|
full. Set empty, it lists no hostname.
|
||||||
- Unset, every app's fixes apply to every hostname.
|
- Unset, every app's fixes apply to every hostname.
|
||||||
- Any of these mistakes stops the start:
|
- An unknown app, a hostname listed twice, an app listed twice for one
|
||||||
- an unknown app;
|
hostname, or an entry without a colon stops the start.
|
||||||
- a hostname that is not a DNS name, such as `*.example.com`;
|
|
||||||
- a hostname listed twice;
|
|
||||||
- an app listed twice for one hostname;
|
|
||||||
- an entry without a colon.
|
|
||||||
- `SWW_ERROR_BURST_THRESHOLD` (default `30`): the most refusals of one client in
|
- `SWW_ERROR_BURST_THRESHOLD` (default `30`): the most refusals of one client in
|
||||||
a minute (see "Attack detection" in `SPEC.md`). A count or `off`.
|
a minute (see "Attack detection" in `SPEC.md`). A count or `off`.
|
||||||
|
|
||||||
|
|||||||
@@ -406,7 +406,6 @@ var (
|
|||||||
"is not a path starting with / and without a ?, such as /wp-login.php")
|
"is not a path starting with / and without a ?, such as /wp-login.php")
|
||||||
errNotAppsEntry = errors.New(
|
errNotAppsEntry = errors.New(
|
||||||
"is not a hostname, : and a list of apps, such as git.example.com:gitea")
|
"is not a hostname, : and a list of apps, such as git.example.com:gitea")
|
||||||
errNotHostname = errors.New("is not a DNS name such as git.example.com")
|
|
||||||
errNotApp = errors.New("is not an app sww has fixes for; the apps are")
|
errNotApp = errors.New("is not an app sww has fixes for; the apps are")
|
||||||
errNotWAFMode = errors.New("is not off, detect or block")
|
errNotWAFMode = errors.New("is not off, detect or block")
|
||||||
errNotParanoiaLevel = errors.New("is not a paranoia level, from 1 to 4")
|
errNotParanoiaLevel = errors.New("is not a paranoia level, from 1 to 4")
|
||||||
@@ -1676,10 +1675,9 @@ func parseTrapPaths(value string) ([]string, error) {
|
|||||||
// parseApps reads the apps each hostname serves: entries separated by
|
// parseApps reads the apps each hostname serves: entries separated by
|
||||||
// semicolons, each a hostname, a colon, and a comma-separated list of the
|
// semicolons, each a hostname, a colon, and a comma-separated list of the
|
||||||
// names waf.AppNames returns, such as
|
// names waf.AppNames returns, such as
|
||||||
// git.example.com:gitea;cloud.example.com:nextcloud,wordpress. Each
|
// git.example.com:gitea;cloud.example.com:nextcloud,wordpress. It returns
|
||||||
// hostname is a DNS name, without a wildcard. It returns them by hostname
|
// them by hostname in lowercase, since a hostname is matched without
|
||||||
// in lowercase, since a hostname is matched without regard to case. An
|
// regard to case. An empty value lists no hostname.
|
||||||
// empty value lists no hostname.
|
|
||||||
func parseApps(value string) (map[string][]string, error) {
|
func parseApps(value string) (map[string][]string, error) {
|
||||||
apps := map[string][]string{}
|
apps := map[string][]string{}
|
||||||
|
|
||||||
@@ -1698,10 +1696,6 @@ func parseApps(value string) (map[string][]string, error) {
|
|||||||
return nil, fmt.Errorf("%q %w", entry, errNotAppsEntry)
|
return nil, fmt.Errorf("%q %w", entry, errNotAppsEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isZone(hostname) {
|
|
||||||
return nil, fmt.Errorf("%q %w", hostname, errNotHostname)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, listed := apps[hostname]; listed {
|
if _, listed := apps[hostname]; listed {
|
||||||
return nil, fmt.Errorf("%q %w", hostname, errListedTwice)
|
return nil, fmt.Errorf("%q %w", hostname, errListedTwice)
|
||||||
}
|
}
|
||||||
@@ -2158,8 +2152,7 @@ func MaskZoneKey(zone string) string {
|
|||||||
return masked + "." + rest
|
return masked + "." + rest
|
||||||
}
|
}
|
||||||
|
|
||||||
// isZone reports whether each label of zone is as parseZones takes it. It
|
// isZone reports whether each label of zone is as parseZones takes it.
|
||||||
// also checks the hostnames of SWW_APPS, which are DNS names too.
|
|
||||||
func isZone(zone string) bool {
|
func isZone(zone string) bool {
|
||||||
for label := range strings.SplitSeq(zone, ".") {
|
for label := range strings.SplitSeq(zone, ".") {
|
||||||
badChar := strings.ContainsFunc(label, func(char rune) bool {
|
badChar := strings.ContainsFunc(label, func(char rune) bool {
|
||||||
|
|||||||
@@ -603,20 +603,14 @@ func TestApps(t *testing.T) {
|
|||||||
func TestInvalidAppsStopsTheStart(t *testing.T) {
|
func TestInvalidAppsStopsTheStart(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
const (
|
const notEntry = " is not a hostname, : and a list of apps, " +
|
||||||
notEntry = " is not a hostname, : and a list of apps, " +
|
|
||||||
"such as git.example.com:gitea"
|
"such as git.example.com:gitea"
|
||||||
notHostname = " is not a DNS name such as git.example.com"
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, tc := range []struct{ value, want string }{
|
for _, tc := range []struct{ value, want string }{
|
||||||
{"git.example.com:gitea;blog.example.com", `"blog.example.com"` + notEntry},
|
{"git.example.com:gitea;blog.example.com", `"blog.example.com"` + notEntry},
|
||||||
{"git.example.com:gitea;", `""` + notEntry},
|
{"git.example.com:gitea;", `""` + notEntry},
|
||||||
{":gitea", `":gitea"` + notEntry},
|
{":gitea", `":gitea"` + notEntry},
|
||||||
{"git.example.com:", `"git.example.com:"` + notEntry},
|
{"git.example.com:", `"git.example.com:"` + notEntry},
|
||||||
{"*.example.com:gitea", `"*.example.com"` + notHostname},
|
|
||||||
{"git.example.com/:gitea", `"git.example.com/"` + notHostname},
|
|
||||||
{"git example.com:gitea", `"git example.com"` + notHostname},
|
|
||||||
{
|
{
|
||||||
"git.example.com:gitea,nextcloud",
|
"git.example.com:gitea,nextcloud",
|
||||||
`"nextcloud" is not an app sww has fixes for; the apps are gitea`,
|
`"nextcloud" is not an app sww has fixes for; the apps are gitea`,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"sneak.berlin/go/smallwebwaf/internal/alerts"
|
"sneak.berlin/go/smallwebwaf/internal/alerts"
|
||||||
"sneak.berlin/go/smallwebwaf/internal/discriminator"
|
|
||||||
"sneak.berlin/go/smallwebwaf/internal/ratelimit"
|
"sneak.berlin/go/smallwebwaf/internal/ratelimit"
|
||||||
"sneak.berlin/go/smallwebwaf/internal/requestlog"
|
"sneak.berlin/go/smallwebwaf/internal/requestlog"
|
||||||
)
|
)
|
||||||
@@ -22,7 +21,6 @@ const (
|
|||||||
wafDisabledRules = "SWW_WAF_DISABLED_RULES"
|
wafDisabledRules = "SWW_WAF_DISABLED_RULES"
|
||||||
wafExemptPaths = "SWW_WAF_EXEMPT_PATHS"
|
wafExemptPaths = "SWW_WAF_EXEMPT_PATHS"
|
||||||
wafBodyLimit = "SWW_WAF_BODY_LIMIT"
|
wafBodyLimit = "SWW_WAF_BODY_LIMIT"
|
||||||
apps = "SWW_APPS"
|
|
||||||
block = "block"
|
block = "block"
|
||||||
detect = "detect"
|
detect = "detect"
|
||||||
)
|
)
|
||||||
@@ -239,27 +237,6 @@ func TestDisabledRulesSwitchOffWhatGiteaWouldBeRefused(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGiteasFixesApplyOnlyToAHostnameThatServesGitea(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Only gitea's fixes let a file name through in path, a parameter
|
|
||||||
// gitea sends file names in.
|
|
||||||
const fileName = "/?path=.gitignore"
|
|
||||||
|
|
||||||
s := startDiscriminator(t, "", map[string]string{
|
|
||||||
wafMode: block, apps: siteA + ":gitea",
|
|
||||||
})
|
|
||||||
|
|
||||||
wantDisposition(t, do(t, s.question(siteA, client, fileName)),
|
|
||||||
http.StatusOK, discriminator.DispositionAllow)
|
|
||||||
wantDisposition(t, do(t, s.question(siteB, client, fileName)),
|
|
||||||
http.StatusForbidden, discriminator.DispositionDeny)
|
|
||||||
|
|
||||||
lines := s.out.requestLines(t, 2)
|
|
||||||
wantLine(t, lines[0], http.StatusOK, requestlog.ActionForward)
|
|
||||||
wantLine(t, lines[1], http.StatusForbidden, requestlog.ActionWAFBlocked)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAttackInAFormBodyIsRefusedOnlyWhileBodiesAreRead(t *testing.T) {
|
func TestAttackInAFormBodyIsRefusedOnlyWhileBodiesAreRead(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user