Resolve typed credentials safely
This commit is contained in:
@@ -217,7 +217,7 @@ seriatim:
|
||||
binary: ` + seriatimBinary + `
|
||||
audita:
|
||||
binary: ` + auditaBinary + `
|
||||
llm_api_key_env: OPENROUTER_API_KEY
|
||||
llm_api_key: OPENROUTER_API_KEY
|
||||
notification:
|
||||
mode: noop
|
||||
`
|
||||
@@ -262,6 +262,28 @@ inputs:
|
||||
if !strings.Contains(stdout.String(), "stage=polish executed=1 skipped=0") {
|
||||
t.Fatalf("stdout = %q, want polish execution", stdout.String())
|
||||
}
|
||||
foundReference := false
|
||||
if err := filepath.WalkDir(workRoot, func(path string, entry os.DirEntry, walkErr error) error {
|
||||
if walkErr != nil || entry.IsDir() {
|
||||
return walkErr
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes.Contains(data, []byte("from-secret-file")) {
|
||||
t.Fatalf("workspace output %q exposed credential value", path)
|
||||
}
|
||||
if bytes.Contains(data, []byte("OPENROUTER_API_KEY")) {
|
||||
foundReference = true
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("inspect polish outputs: %v", err)
|
||||
}
|
||||
if !foundReference {
|
||||
t.Fatal("polish outputs omitted safe credential reference")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteRunFailsWhenExplicitSecretsDirMissing(t *testing.T) {
|
||||
|
||||
@@ -299,6 +299,62 @@ func TestConfigDiffBootstrapsSecretsOnceForBothProfiles(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigInspectionOutputsNeverExposeResolvedCredentialValues(t *testing.T) {
|
||||
const firstCanary = "inspection-credential-canary-one"
|
||||
const secondCanary = "inspection-credential-canary-two"
|
||||
for name, value := range map[string]string{
|
||||
"NARRATIO_INSPECT_ACCESS": firstCanary,
|
||||
"NARRATIO_INSPECT_SECRET": firstCanary,
|
||||
"NARRATIO_INSPECT_LLM_ONE": firstCanary,
|
||||
"NARRATIO_INSPECT_LLM_TWO": secondCanary,
|
||||
} {
|
||||
t.Setenv(name, value)
|
||||
}
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, _, _ := writeValidConfigFiles(t, workspaceRoot)
|
||||
pipelineData, err := os.ReadFile(pipelinePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pipelineYAML := strings.Replace(string(pipelineData), " bucket: test-bucket\n", ` bucket: test-bucket
|
||||
access_key_id: NARRATIO_INSPECT_ACCESS
|
||||
secret_access_key: NARRATIO_INSPECT_SECRET
|
||||
`, 1)
|
||||
if err := os.WriteFile(pipelinePath, []byte(pipelineYAML), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeInspectionProfiles(t, pipelinePath,
|
||||
"audita:\n model: production\n llm_api_key: NARRATIO_INSPECT_LLM_ONE\n",
|
||||
"audita:\n model: testing\n llm_api_key: NARRATIO_INSPECT_LLM_TWO\n",
|
||||
)
|
||||
|
||||
var show bytes.Buffer
|
||||
if err := ConfigShow(context.Background(), []string{"--config", pipelinePath, "--profile", "production"}, &show); err != nil {
|
||||
t.Fatalf("config show: %v", err)
|
||||
}
|
||||
var sources bytes.Buffer
|
||||
if err := ConfigSources(context.Background(), []string{"--config", pipelinePath, "--profile", "production"}, &sources); err != nil {
|
||||
t.Fatalf("config sources: %v", err)
|
||||
}
|
||||
var diff bytes.Buffer
|
||||
if err := ConfigDiff(context.Background(), []string{"production", "testing", "--config", pipelinePath}, &diff); err != nil {
|
||||
t.Fatalf("config diff: %v", err)
|
||||
}
|
||||
for label, output := range map[string]string{"show": show.String(), "sources": sources.String(), "diff": diff.String()} {
|
||||
if strings.Contains(output, firstCanary) || strings.Contains(output, secondCanary) {
|
||||
t.Fatalf("%s output exposed credential value: %q", label, output)
|
||||
}
|
||||
}
|
||||
for _, reference := range []string{"NARRATIO_INSPECT_ACCESS", "NARRATIO_INSPECT_SECRET", "NARRATIO_INSPECT_LLM_ONE"} {
|
||||
if !strings.Contains(show.String(), reference) {
|
||||
t.Fatalf("show omitted safe credential reference %q: %q", reference, show.String())
|
||||
}
|
||||
}
|
||||
if !strings.Contains(diff.String(), "NARRATIO_INSPECT_LLM_ONE") || !strings.Contains(diff.String(), "NARRATIO_INSPECT_LLM_TWO") {
|
||||
t.Fatalf("diff omitted safe credential references: %q", diff.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigCommandsInspectMaintainedSplitBundleWithoutRuntimeState(t *testing.T) {
|
||||
examplesDir := filepath.Join("..", "..", "examples")
|
||||
pipelinePath := filepath.Join(examplesDir, "production-testing", "pipeline.yml")
|
||||
|
||||
@@ -31,9 +31,9 @@ func commandObjectStoreTestConfig() *config.Config {
|
||||
Storage: config.StorageConfig{
|
||||
Backend: "s3",
|
||||
S3: &config.StorageS3Config{
|
||||
Bucket: "test-bucket",
|
||||
AccessKeyIDEnv: "NARRATIO_TEST_COMMAND_STORE_KEY_ID",
|
||||
SecretKeyEnv: "NARRATIO_TEST_COMMAND_STORE_SECRET",
|
||||
Bucket: "test-bucket",
|
||||
AccessKeyID: testSecretReference("NARRATIO_TEST_COMMAND_STORE_KEY_ID"),
|
||||
SecretAccessKey: testSecretReference("NARRATIO_TEST_COMMAND_STORE_SECRET"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -478,7 +478,7 @@ func addSecretsToPipelineConfig(t *testing.T, pipelinePath, secretsDir, accessKe
|
||||
pipelineYAML := strings.Replace(
|
||||
string(pipelineData),
|
||||
"storage:\n backend: s3\n s3:\n bucket: test-bucket\n",
|
||||
"storage:\n backend: s3\n s3:\n bucket: test-bucket\n access_key_id_env: "+accessKeyEnv+"\n secret_access_key_env: "+secretKeyEnv+"\n",
|
||||
"storage:\n backend: s3\n s3:\n bucket: test-bucket\n access_key_id: "+accessKeyEnv+"\n secret_access_key: "+secretKeyEnv+"\n",
|
||||
1,
|
||||
)
|
||||
if err := os.WriteFile(pipelinePath, []byte(pipelineYAML), 0o644); err != nil {
|
||||
|
||||
@@ -211,11 +211,18 @@ func buildDefaultAuditaRunner(cfg *config.Config) (audita.Runner, error) {
|
||||
if a.Report != nil {
|
||||
report = *a.Report
|
||||
}
|
||||
llmAPIKey := ""
|
||||
llmAPIKeyReference := ""
|
||||
if a.LLMAPIKey != nil {
|
||||
llmAPIKey = a.LLMAPIKey.Value()
|
||||
llmAPIKeyReference = a.LLMAPIKey.EnvName()
|
||||
}
|
||||
|
||||
runner, err := audita.NewSubprocessRunnerFromConfigValues(
|
||||
a.Binary,
|
||||
a.Timeout,
|
||||
a.LLMAPIKeyEnv,
|
||||
llmAPIKey,
|
||||
llmAPIKeyReference,
|
||||
append([]string(nil), a.Modules...),
|
||||
a.BaseURL,
|
||||
a.Model,
|
||||
|
||||
@@ -471,8 +471,8 @@ func TestDeliverySemanticChangesInvalidateOnlyTheirFixedDependents(t *testing.T)
|
||||
cfg.Pipeline.Publish.Outputs[0].Dest = "published/alternate.json"
|
||||
}, wantRuns: [2]int{1, 1}},
|
||||
{name: "publish credentials", mutate: func(cfg *config.Config) {
|
||||
cfg.Pipeline.Storage.S3.AccessKeyIDEnv = "OTHER_ACCESS_KEY"
|
||||
cfg.Pipeline.Storage.S3.SecretKeyEnv = "OTHER_SECRET_KEY"
|
||||
cfg.Pipeline.Storage.S3.AccessKeyID = testSecretReference("OTHER_ACCESS_KEY")
|
||||
cfg.Pipeline.Storage.S3.SecretAccessKey = testSecretReference("OTHER_SECRET_KEY")
|
||||
}, wantRuns: [2]int{0, 0}},
|
||||
{name: "notify mode", mutate: func(cfg *config.Config) {
|
||||
cfg.Pipeline.Notification.Mode = "webhook"
|
||||
@@ -534,7 +534,7 @@ func configureDeliverySemanticConfig(cfg *config.Config) {
|
||||
Backend: config.StorageBackendLocal,
|
||||
S3: &config.StorageS3Config{
|
||||
Bucket: "campaign", RootPrefix: "narratio", Region: "us-east-1",
|
||||
Endpoint: "https://objects.example", AccessKeyIDEnv: "ACCESS_KEY", SecretKeyEnv: "SECRET_KEY",
|
||||
Endpoint: "https://objects.example", AccessKeyID: testSecretReference("ACCESS_KEY"), SecretAccessKey: testSecretReference("SECRET_KEY"),
|
||||
},
|
||||
}
|
||||
cfg.Pipeline.Notification.Mode = config.DefaultNotificationMode
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
)
|
||||
|
||||
// restoreEnvAfterTest clears names for a test and restores both their prior
|
||||
@@ -31,6 +33,11 @@ func restoreEnvAfterTest(t *testing.T, names ...string) {
|
||||
})
|
||||
}
|
||||
|
||||
func testSecretReference(name string) *configkit.Secret {
|
||||
reference := configkit.SecretEnv(name)
|
||||
return &reference
|
||||
}
|
||||
|
||||
func mustWriteSecretFile(t *testing.T, path, value string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user