Settings given as files: the _FILE form of every setting (closes #87)
check / check (push) Successful in 4m53s
check / check (push) Successful in 4m53s
Every setting X may instead be given as a file that X_FILE names, read once at start: its contents, less one trailing newline, are the value, checked as X would be. X and X_FILE both set, or a file that cannot be read, stops the start with a message naming the variable. The logged settings name the file, and mask a token read from one. SWWAF_LOG_REMOTE_TLS_CA_FILE, whose value is a file already, has no _FILE form. The health check reads only SWWAF_LISTEN_ADDR and SWWAF_UPSTREAM_URL, so no other setting or file can fail it. Judgement call: an invalid value read from a file is named as X, not X_FILE. Rule suppressed: gosec G304 on reading the named file, as for the CA file. Model: opus-5-5
This commit was merged in pull request #89.
This commit is contained in:
@@ -722,6 +722,133 @@ func TestTokenIsLoggedMasked(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingFromFileLosesOneNewlineAndNoMore(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for contents, want := range map[string]string{
|
||||
token: token,
|
||||
token + "\n": token,
|
||||
token + "\n\n": token + "\n",
|
||||
token + " \n": token + " ",
|
||||
} {
|
||||
cfg := fromEnvironment(t, environment{
|
||||
metricsToken + "_FILE": writeFile(t, contents),
|
||||
})
|
||||
if cfg.MetricsToken != want {
|
||||
t.Errorf("file holding %q gave %s %q, want %q", contents, metricsToken,
|
||||
cfg.MetricsToken, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingFromFileIsCheckedAsTheSettingItself(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
requestMaxBytes + "_FILE": writeFile(t, "lots\n"),
|
||||
}.lookupEnv)
|
||||
|
||||
want := requestMaxBytes + `: "lots" is not a size such as 512K, 100M or 5G, or off`
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error %v, want %s", err, want)
|
||||
}
|
||||
|
||||
_, err = config.FromEnvironment(environment{
|
||||
logRemoteURL: remoteURL,
|
||||
instanceName: instance,
|
||||
logRemoteAppName + "_FILE": writeFile(t, "my app\n"),
|
||||
}.lookupEnv)
|
||||
|
||||
want = logRemoteAppName + `: "my app" is not 1 to 48 printable ASCII ` +
|
||||
`characters without a space, such as gitea`
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error %v, want %s", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingAndItsFileBothSetStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := config.FromEnvironment(environment{
|
||||
metricsToken: token,
|
||||
metricsToken + "_FILE": writeFile(t, token),
|
||||
}.lookupEnv)
|
||||
|
||||
want := metricsToken + ": is set, and so is " + metricsToken +
|
||||
"_FILE; set only one of them"
|
||||
if err == nil || err.Error() != want {
|
||||
t.Errorf("error %v, want %s", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnreadableSettingFileStopsTheStart(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
for _, path := range []string{filepath.Join(dir, "missing"), dir} {
|
||||
_, err := config.FromEnvironment(environment{metricsToken + "_FILE": path}.lookupEnv)
|
||||
|
||||
want := metricsToken + "_FILE: cannot be read: "
|
||||
if err == nil || !strings.HasPrefix(err.Error(), want) {
|
||||
t.Errorf("%s: error %v, want one starting %s", path, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenFromFileIsLoggedMaskedWithTheFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
path := writeFile(t, token+"\n")
|
||||
cfg := fromEnvironment(t, environment{metricsToken + "_FILE": path})
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
slog.New(slog.NewJSONHandler(&out, nil)).Info("starting", "settings", cfg)
|
||||
|
||||
var line struct {
|
||||
Settings map[string]string `json:"settings"`
|
||||
}
|
||||
|
||||
err := json.Unmarshal(out.Bytes(), &line)
|
||||
if err != nil {
|
||||
t.Fatalf("decode %s: %v", out.Bytes(), err)
|
||||
}
|
||||
|
||||
if strings.Contains(out.String(), token) ||
|
||||
line.Settings[metricsToken] != "********" ||
|
||||
line.Settings[metricsToken+"_FILE"] != path {
|
||||
t.Errorf("the token is not logged masked, with its file %s: %s", path,
|
||||
out.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoteLogCAFileIsNotReadAsAFileInItsTurn(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := fromEnvironment(t, environment{
|
||||
logRemoteTLSCAFile + "_FILE": writeFile(t, "/nonexistent/ca.pem\n"),
|
||||
})
|
||||
if cfg.LogRemoteTLSCAs != nil {
|
||||
t.Errorf("%s_FILE gave certificates", logRemoteTLSCAFile)
|
||||
}
|
||||
}
|
||||
|
||||
// writeFile writes contents to a file in a directory of its own, removed
|
||||
// when the test ends, and returns the file's path.
|
||||
func writeFile(t *testing.T, contents string) string {
|
||||
t.Helper()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "setting")
|
||||
|
||||
err := os.WriteFile(path, []byte(contents), 0o600)
|
||||
if err != nil {
|
||||
t.Fatalf("write %s: %v", path, err)
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func TestLogsEachSettingWithItsValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user