Add secrets directory credential resolver
This commit is contained in:
237
internal/config/secrets_test.go
Normal file
237
internal/config/secrets_test.go
Normal file
@@ -0,0 +1,237 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoadSecretEnvironmentLoadsValidFiles(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, "API_KEY", "value\n")
|
||||
writeSecret(t, directory, "CRLF", "value\r\n")
|
||||
writeSecret(t, directory, "MULTILINE", "value\n\n")
|
||||
writeSecret(t, directory, "SPACES", " value \n")
|
||||
writeSecret(t, directory, "CARRIAGE", "value\r")
|
||||
|
||||
result, err := LoadSecretEnvironment(directory, emptyLookup)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
assertEnvValue(t, result.Environment, "API_KEY", "value")
|
||||
assertEnvValue(t, result.Environment, "CRLF", "value")
|
||||
assertEnvValue(t, result.Environment, "MULTILINE", "value\n")
|
||||
assertEnvValue(t, result.Environment, "SPACES", " value ")
|
||||
assertEnvValue(t, result.Environment, "CARRIAGE", "value\r")
|
||||
}
|
||||
|
||||
func TestLoadSecretEnvironmentRejectsInvalidFilenames(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
secretValue := "do-not-print"
|
||||
writeSecret(t, directory, "1INVALID", secretValue)
|
||||
|
||||
_, err := LoadSecretEnvironment(directory, emptyLookup)
|
||||
if err == nil {
|
||||
t.Fatal("LoadSecretEnvironment() error = nil, want error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "secret filename") {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %q, want filename error", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), secretValue) {
|
||||
t.Fatalf("LoadSecretEnvironment() error exposed secret value: %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretEnvironmentIgnoresDirectoriesAndFollowsSymlinks(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
if err := os.Mkdir(filepath.Join(directory, "IGNORED_DIR"), 0o700); err != nil {
|
||||
t.Fatalf("mkdir ignored dir: %v", err)
|
||||
}
|
||||
targetFile := filepath.Join(t.TempDir(), "target")
|
||||
if err := os.WriteFile(targetFile, []byte("linked\n"), 0o600); err != nil {
|
||||
t.Fatalf("write target file: %v", err)
|
||||
}
|
||||
if err := os.Symlink(targetFile, filepath.Join(directory, "LINKED_SECRET")); err != nil {
|
||||
t.Fatalf("symlink file: %v", err)
|
||||
}
|
||||
targetDir := t.TempDir()
|
||||
if err := os.Symlink(targetDir, filepath.Join(directory, "LINKED_DIR")); err != nil {
|
||||
t.Fatalf("symlink dir: %v", err)
|
||||
}
|
||||
|
||||
result, err := LoadSecretEnvironment(directory, emptyLookup)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
assertEnvValue(t, result.Environment, "LINKED_SECRET", "linked")
|
||||
if _, ok := result.Environment.Lookup("IGNORED_DIR"); ok {
|
||||
t.Fatal("directory appeared in environment")
|
||||
}
|
||||
if _, ok := result.Environment.Lookup("LINKED_DIR"); ok {
|
||||
t.Fatal("directory symlink appeared in environment")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSecretEnvironmentMissingDirectoryFails(t *testing.T) {
|
||||
missing := filepath.Join(t.TempDir(), "missing")
|
||||
_, err := LoadSecretEnvironment(missing, emptyLookup)
|
||||
if err == nil {
|
||||
t.Fatal("LoadSecretEnvironment() error = nil, want error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "load secrets directory") || !strings.Contains(err.Error(), missing) {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %q, want directory context", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentPrefersProcessValuesAndReportsDifferingConflicts(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
secretValue := "secret-value"
|
||||
processValue := "process-value"
|
||||
writeSecret(t, directory, "TOKEN", secretValue)
|
||||
|
||||
result, err := LoadSecretEnvironment(directory, mapLookup(map[string]string{"TOKEN": processValue}))
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
assertEnvValue(t, result.Environment, "TOKEN", processValue)
|
||||
if got, want := len(result.Conflicts), 1; got != want {
|
||||
t.Fatalf("conflict count = %d, want %d", got, want)
|
||||
}
|
||||
if result.Conflicts[0].Name != "TOKEN" {
|
||||
t.Fatalf("conflict name = %q, want TOKEN", result.Conflicts[0].Name)
|
||||
}
|
||||
if strings.Contains(result.Conflicts[0].Name, secretValue) || strings.Contains(result.Conflicts[0].Name, processValue) {
|
||||
t.Fatalf("conflict exposed secret values: %#v", result.Conflicts[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentDoesNotWarnWhenProcessValueMatchesSecret(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, "TOKEN", "same-value")
|
||||
|
||||
result, err := LoadSecretEnvironment(directory, mapLookup(map[string]string{"TOKEN": "same-value"}))
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
if len(result.Conflicts) != 0 {
|
||||
t.Fatalf("conflicts = %#v, want none", result.Conflicts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvironmentDoesNotMutateProcessEnvironment(t *testing.T) {
|
||||
name := "DISTRIBUTOR_TEST_SECRET_ONLY"
|
||||
t.Setenv(name, "")
|
||||
if err := os.Unsetenv(name); err != nil {
|
||||
t.Fatalf("unset env: %v", err)
|
||||
}
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, name, "secret")
|
||||
|
||||
if _, err := LoadSecretEnvironment(directory, nil); err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
if _, ok := os.LookupEnv(name); ok {
|
||||
t.Fatalf("%s was added to process environment", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsUsesSecretsAwareEnvironment(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, "ACCESS_KEY_ID", "secret-access")
|
||||
writeSecret(t, directory, "SECRET_ACCESS_KEY", "secret-secret")
|
||||
result, err := LoadSecretEnvironment(directory, emptyLookup)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
|
||||
creds, err := result.Environment.ResolveCredentials(Credentials{
|
||||
AccessKeyIDEnv: "ACCESS_KEY_ID",
|
||||
SecretAccessKeyEnv: "SECRET_ACCESS_KEY",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials() error = %v", err)
|
||||
}
|
||||
if creds.AccessKeyID != "secret-access" || creds.SecretAccessKey != "secret-secret" {
|
||||
t.Fatalf("resolved credentials = %#v", creds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsPrefersProcessEnvironment(t *testing.T) {
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, "ACCESS_KEY_ID", "secret-access")
|
||||
result, err := LoadSecretEnvironment(directory, mapLookup(map[string]string{"ACCESS_KEY_ID": "process-access"}))
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
|
||||
creds, err := result.Environment.ResolveCredentials(Credentials{AccessKeyIDEnv: "ACCESS_KEY_ID"})
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveCredentials() error = %v", err)
|
||||
}
|
||||
if creds.AccessKeyID != "process-access" {
|
||||
t.Fatalf("access key = %q, want process-access", creds.AccessKeyID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsDoesNotFeedProcessEnvironment(t *testing.T) {
|
||||
name := "DISTRIBUTOR_TEST_SDK_CHAIN_VALUE"
|
||||
t.Setenv(name, "")
|
||||
if err := os.Unsetenv(name); err != nil {
|
||||
t.Fatalf("unset env: %v", err)
|
||||
}
|
||||
directory := t.TempDir()
|
||||
writeSecret(t, directory, name, "secret")
|
||||
|
||||
result, err := LoadSecretEnvironment(directory, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSecretEnvironment() error = %v", err)
|
||||
}
|
||||
assertEnvValue(t, result.Environment, name, "secret")
|
||||
if _, ok := os.LookupEnv(name); ok {
|
||||
t.Fatalf("%s is visible to process environment", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveCredentialsMissingReferenceFailsWithoutSecretValue(t *testing.T) {
|
||||
env := NewEnvironment(map[string]string{"PRESENT": "do-not-print"}, emptyLookup)
|
||||
_, err := env.ResolveCredentials(Credentials{AccessKeyIDEnv: "MISSING"})
|
||||
if err == nil {
|
||||
t.Fatal("ResolveCredentials() error = nil, want error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "MISSING") {
|
||||
t.Fatalf("ResolveCredentials() error = %q, want missing variable name", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "do-not-print") {
|
||||
t.Fatalf("ResolveCredentials() error exposed secret value: %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeSecret(t *testing.T, directory, name, value string) {
|
||||
t.Helper()
|
||||
if err := os.WriteFile(filepath.Join(directory, name), []byte(value), 0o600); err != nil {
|
||||
t.Fatalf("write secret %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertEnvValue(t *testing.T, env Environment, name, want string) {
|
||||
t.Helper()
|
||||
got, ok := env.Lookup(name)
|
||||
if !ok {
|
||||
t.Fatalf("Lookup(%q) ok = false", name)
|
||||
}
|
||||
if got != want {
|
||||
t.Fatalf("Lookup(%q) = %q, want %q", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func emptyLookup(string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
func mapLookup(values map[string]string) EnvLookup {
|
||||
return func(name string) (string, bool) {
|
||||
value, ok := values[name]
|
||||
return value, ok
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user