servewaf's and rproxy's settings are read through viper, bound to the same environment variable names as before, in place of the os.LookupEnv function Main passed down. NewViper turns AutomaticEnv and AllowEmptyEnv on, so a setting that is set but empty reads as set, as it did. The _FILE forms, their errors, and rproxy reading no file form are unchanged, and the settings tests pass set into a viper instead of a lookup function. Model: deepseek-v4-pro
This commit is contained in:
1 parent
f8676debda
commit
9c755ee358
16 files changed
+223
-143
No files matched your search
@@ -17,6 +17,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"sneak.berlin/go/smallwebwaf/internal/anomaly"
|
||||
"sneak.berlin/go/smallwebwaf/internal/config"
|
||||
)
|
||||
@@ -217,18 +219,22 @@ const (
|
||||
// environment is a set of environment variables, for FromEnvironment.
|
||||
type environment map[string]string
|
||||
|
||||
// lookupEnv reads one of the variables, as os.LookupEnv does.
|
||||
func (e environment) lookupEnv(name string) (string, bool) {
|
||||
value, ok := e[name]
|
||||
// viper returns a viper Set with each of the variables, as the viper
|
||||
// NewViper returns one bound to a process's environment.
|
||||
func (e environment) viper() *viper.Viper {
|
||||
v := viper.New()
|
||||
for name, value := range e {
|
||||
v.Set(name, value)
|
||||
}
|
||||
|
||||
return value, ok
|
||||
return v
|
||||
}
|
||||
|
||||
// fromEnvironment reads the settings from env, which must be valid.
|
||||
func fromEnvironment(t *testing.T, env environment) *config.Config {
|
||||
t.Helper()
|
||||
|
||||
cfg, err := config.FromEnvironment(env.lookupEnv)
|
||||
cfg, err := config.FromEnvironment(env.viper())
|
||||
if err != nil {
|
||||
t.Fatalf("settings %v: %v", env, err)
|
||||
}
|
||||
@@ -455,7 +461,7 @@ func TestPathPrefixNotStartingWithSlashStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(
|
||||
environment{rateLimitExemptPaths: "/favicon.ico,assets/"}.lookupEnv)
|
||||
environment{rateLimitExemptPaths: "/favicon.ico,assets/"}.viper())
|
||||
|
||||
want := rateLimitExemptPaths + `: "assets/" is not a path prefix ` +
|
||||
`starting with /, such as /assets/`
|
||||
@@ -510,7 +516,7 @@ func TestInvalidTrapPathOrErrorBurstThresholdStopsTheStart(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -636,7 +642,7 @@ func TestInvalidCoreRuleSetSettingStopsTheStart(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -761,7 +767,7 @@ func TestInvalidRemoteLogSettingStopsTheStart(t *testing.T) {
|
||||
{logRemoteAppName, ""}, {logRemoteAppName, "my app"},
|
||||
{logRemoteAppName, "gitéa"}, {logRemoteAppName, strings.Repeat("a", 49)},
|
||||
} {
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
if err == nil || !strings.HasPrefix(err.Error(), tc.name+": ") {
|
||||
t.Errorf("%s=%q: error %v, want one naming it", tc.name, tc.value, err)
|
||||
}
|
||||
@@ -778,7 +784,7 @@ func TestRemoteLogCAFileWithoutCertificateStopsTheStart(t *testing.T) {
|
||||
t.Fatalf("write %s: %v", caFile, err)
|
||||
}
|
||||
|
||||
_, err = config.FromEnvironment(environment{logRemoteTLSCAFile: caFile}.lookupEnv)
|
||||
_, err = config.FromEnvironment(environment{logRemoteTLSCAFile: caFile}.viper())
|
||||
|
||||
want := logRemoteTLSCAFile + `: "` + caFile + `" holds no PEM certificate`
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -793,7 +799,7 @@ func TestInstanceNameNotAnAppNameStopsTheStartOnlyWhileSending(t *testing.T) {
|
||||
|
||||
sending := environment{logRemoteURL: remoteURL, instanceName: spaced}
|
||||
|
||||
_, err := config.FromEnvironment(sending.lookupEnv)
|
||||
_, err := config.FromEnvironment(sending.viper())
|
||||
|
||||
want := logRemoteAppName + `: is unset, and ` + instanceName +
|
||||
` "fsn1 app1", its default, is not 1 to 48 printable ASCII characters ` +
|
||||
@@ -823,7 +829,7 @@ func TestAppNameSetStopsTheStartWhileSending(t *testing.T) {
|
||||
logRemoteURL: remoteURL,
|
||||
instanceName: instance,
|
||||
logRemoteAppName: "my app",
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := logRemoteAppName + `: "my app" is not 1 to 48 printable ASCII ` +
|
||||
`characters without a space, such as gitea`
|
||||
@@ -954,7 +960,7 @@ func TestWebhookHeadersAreLoggedMaskedAndNeverShown(t *testing.T) {
|
||||
// An item that is not a header is named by its place, not shown.
|
||||
_, err := config.FromEnvironment(environment{
|
||||
alertWebhookHeaders: "X-Team:ops," + secret,
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := alertWebhookHeaders + ": item 2 is not a header name followed by : " +
|
||||
"and the header's value, such as Authorization:Bearer <token>"
|
||||
@@ -991,7 +997,7 @@ func TestWebhookURLIsLoggedWithoutItsPathOrQueryAndNeverShown(t *testing.T) {
|
||||
"ftp://hooks.example/services/" + secret,
|
||||
"https://hooks.example/services/%zz" + secret,
|
||||
} {
|
||||
_, err := config.FromEnvironment(environment{alertWebhookURL: value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{alertWebhookURL: value}.viper())
|
||||
|
||||
want := alertWebhookURL + ": is not an http or https URL without a user or " +
|
||||
"a fragment, such as https://alerts.example/smallwebwaf"
|
||||
@@ -1042,7 +1048,7 @@ func TestNtfyTokenWithAControlCharacterStopsTheStartWithoutShowingIt(t *testing.
|
||||
"set": {alertNtfyToken: token + "\r"},
|
||||
"in a file": {alertNtfyToken + "_FILE": writeFile(t, token+"\r\n")},
|
||||
} {
|
||||
_, err := config.FromEnvironment(env.lookupEnv)
|
||||
_, err := config.FromEnvironment(env.viper())
|
||||
|
||||
want := alertNtfyToken + ": holds a control character, such as the " +
|
||||
"carriage return of a Windows line end"
|
||||
@@ -1059,7 +1065,7 @@ func TestInstanceNameWithAControlCharacterStopsTheStartOnlyWithNtfySet(t *testin
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
instanceName: name, alertNtfyURL: "https://ntfy.example/smallwebwaf-alerts",
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := instanceName + `: "fsn1app1\r" holds a control character, such as the ` +
|
||||
`carriage return of a Windows line end, and is sent to ntfy in a header ` +
|
||||
@@ -1084,7 +1090,7 @@ func TestInstanceNameNotUTF8StopsTheStart(t *testing.T) {
|
||||
"set": {instanceName: latin1},
|
||||
"in a file": {instanceName + "_FILE": writeFile(t, latin1+"\n")},
|
||||
} {
|
||||
_, err := config.FromEnvironment(env.lookupEnv)
|
||||
_, err := config.FromEnvironment(env.viper())
|
||||
|
||||
want := instanceName + `: "caf\xe9" is not valid UTF-8`
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1099,7 +1105,7 @@ func TestCodeOnBothCountryListsStopsTheStart(t *testing.T) {
|
||||
_, err := config.FromEnvironment(environment{
|
||||
deniedCountries: "cn,ru",
|
||||
allowedCountries: "de,RU",
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
if err == nil {
|
||||
t.Fatal("ru on both country lists was accepted")
|
||||
}
|
||||
@@ -1161,7 +1167,7 @@ func TestLookupDBPathGoesWithTheFileSourceAlone(t *testing.T) {
|
||||
lookupDBPath + ": is set while " + lookupSource + " is off; only file reads it",
|
||||
},
|
||||
} {
|
||||
_, err := config.FromEnvironment(tc.env.lookupEnv)
|
||||
_, err := config.FromEnvironment(tc.env.viper())
|
||||
if err == nil || err.Error() != tc.want {
|
||||
t.Errorf("settings %v: error %v, want %s", tc.env, err, tc.want)
|
||||
}
|
||||
@@ -1191,7 +1197,7 @@ func TestSettingNeedingLookupsStopsTheStartWhileTheyAreOff(t *testing.T) {
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
lookupSource: off, name: value,
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
if err == nil || !strings.HasPrefix(err.Error(), name+": ") ||
|
||||
!strings.Contains(err.Error(), lookupSource+" is off") {
|
||||
t.Errorf("error %v, want one naming %s and %s", err, name, lookupSource)
|
||||
@@ -1341,7 +1347,7 @@ func TestInvalidAnomalySettingStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1420,7 +1426,7 @@ func TestInvalidBiasedThresholdStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1517,7 +1523,7 @@ func TestInvalidReputationSettingStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1532,7 +1538,7 @@ func TestASNLimitPercentURLThatIsABlocklistStopsTheStart(t *testing.T) {
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
blocklistURLs: dropURL + "," + asnURL, asnLimitPercentURL: asnURL,
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := asnLimitPercentURL + `: "` + asnURL + `" is in SWWAF_BLOCKLIST_URLS too`
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1659,7 +1665,7 @@ func TestInvalidDNSBLSettingStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1727,7 +1733,7 @@ func TestDNSBLZoneKeyIsLoggedMaskedAndNeverShown(t *testing.T) {
|
||||
{spamhaus + "," + otherZone, `"` + spamhausMasked + `" is listed twice`},
|
||||
{spamhaus + ",,", "has an empty item in its list"},
|
||||
} {
|
||||
_, err := config.FromEnvironment(environment{dnsblZones: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{dnsblZones: tc.value}.viper())
|
||||
|
||||
want := dnsblZones + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1780,7 +1786,7 @@ func TestInvalidAbuseIPDBSettingStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
|
||||
want := tc.name + ": " + tc.want
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -1894,7 +1900,7 @@ func TestInvalidCrowdSecSettingStopsTheStartSayingWhatIsWrong(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(tc.env.lookupEnv)
|
||||
_, err := config.FromEnvironment(tc.env.viper())
|
||||
if err == nil || err.Error() != tc.want {
|
||||
t.Errorf("error %v, want %s", err, tc.want)
|
||||
}
|
||||
@@ -2054,7 +2060,7 @@ func wantStartStopped(t *testing.T, invalid []struct{ name, value string }) {
|
||||
t.Run(tc.name+"="+tc.value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{tc.name: tc.value}.viper())
|
||||
if err == nil {
|
||||
t.Fatalf("%s=%q was accepted", tc.name, tc.value)
|
||||
}
|
||||
@@ -2079,7 +2085,7 @@ func TestHostOrTransferEncodingStopsTheStart(t *testing.T) {
|
||||
t.Run(value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{logRequestHeaders: value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{logRequestHeaders: value}.viper())
|
||||
if err == nil || err.Error() != logRequestHeaders+": "+want {
|
||||
t.Errorf("error %v, want %s: %s", err, logRequestHeaders, want)
|
||||
}
|
||||
@@ -2096,7 +2102,7 @@ func TestShortTokenStopsTheStartWithoutShowingIt(t *testing.T) {
|
||||
t.Run(name+"="+value, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{name: value}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{name: value}.viper())
|
||||
|
||||
want := name + ": is shorter than 32 characters"
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -2152,7 +2158,7 @@ func TestSettingFromFileIsCheckedAsTheSettingItself(t *testing.T) {
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
bytesLimitPerMinute + "_FILE": writeFile(t, "lots\n"),
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := bytesLimitPerMinute + `: "lots" is not a size such as 512K, 100M or 5G, or off`
|
||||
if err == nil || err.Error() != want {
|
||||
@@ -2163,7 +2169,7 @@ func TestSettingFromFileIsCheckedAsTheSettingItself(t *testing.T) {
|
||||
logRemoteURL: remoteURL,
|
||||
instanceName: instance,
|
||||
logRemoteAppName + "_FILE": writeFile(t, "my app\n"),
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want = logRemoteAppName + `: "my app" is not 1 to 48 printable ASCII ` +
|
||||
`characters without a space, such as gitea`
|
||||
@@ -2178,7 +2184,7 @@ func TestSettingAndItsFileBothSetStopsTheStart(t *testing.T) {
|
||||
_, err := config.FromEnvironment(environment{
|
||||
metricsToken: token,
|
||||
metricsToken + "_FILE": writeFile(t, token),
|
||||
}.lookupEnv)
|
||||
}.viper())
|
||||
|
||||
want := metricsToken + ": is set, and so is " + metricsToken +
|
||||
"_FILE; set only one of them"
|
||||
@@ -2193,7 +2199,7 @@ func TestUnreadableSettingFileStopsTheStart(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
for _, path := range []string{filepath.Join(dir, "missing"), dir} {
|
||||
_, err := config.FromEnvironment(environment{metricsToken + "_FILE": path}.lookupEnv)
|
||||
_, err := config.FromEnvironment(environment{metricsToken + "_FILE": path}.viper())
|
||||
|
||||
want := metricsToken + "_FILE: cannot be read: "
|
||||
if err == nil || !strings.HasPrefix(err.Error(), want) {
|
||||
|
||||
Reference in new issue
Block a user