Add implementation plan for CLI and configuration test coverage
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||
@@ -126,17 +127,47 @@ func redactOptions(values map[string]any) map[string]any {
|
||||
}
|
||||
|
||||
func redactOptionValue(value any) any {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
return redactOptions(typed)
|
||||
case []any:
|
||||
items := make([]any, len(typed))
|
||||
for i, item := range typed {
|
||||
items[i] = redactOptionValue(item)
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
reflected := reflect.ValueOf(value)
|
||||
switch reflected.Kind() {
|
||||
case reflect.Map:
|
||||
if reflected.Type().Key().Kind() != reflect.String {
|
||||
return value
|
||||
}
|
||||
if reflected.IsNil() {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]any, reflected.Len())
|
||||
iterator := reflected.MapRange()
|
||||
for iterator.Next() {
|
||||
key := iterator.Key().String()
|
||||
if sensitiveConfigKey(key) {
|
||||
out[key] = "[REDACTED]"
|
||||
continue
|
||||
}
|
||||
out[key] = redactOptionValue(iterator.Value().Interface())
|
||||
}
|
||||
return out
|
||||
case reflect.Slice:
|
||||
if reflected.IsNil() {
|
||||
return nil
|
||||
}
|
||||
if reflected.Type().Elem().Kind() == reflect.Uint8 {
|
||||
out := reflect.MakeSlice(reflected.Type(), reflected.Len(), reflected.Len())
|
||||
reflect.Copy(out, reflected)
|
||||
return out.Interface()
|
||||
}
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
items := make([]any, reflected.Len())
|
||||
for i := 0; i < reflected.Len(); i++ {
|
||||
items[i] = redactOptionValue(reflected.Index(i).Interface())
|
||||
}
|
||||
return items
|
||||
default:
|
||||
return typed
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,40 @@ func TestRedactedEffectiveConfigPayloadDoesNotAliasSource(t *testing.T) {
|
||||
assertRedactionTestBindingUnchanged(t, effective.ResolvedPipeline.Input, "effective")
|
||||
}
|
||||
|
||||
func TestRedactedResolvedPipelinePayloadHandlesTypedOptionContainers(t *testing.T) {
|
||||
type optionMap map[string]string
|
||||
type optionList []optionMap
|
||||
|
||||
typed := optionList{{
|
||||
"api_key": "typed-container-secret",
|
||||
"safe": "typed-container-safe",
|
||||
}}
|
||||
effective := EffectiveConfig{ResolvedPipeline: pipeline.ResolvedPipeline{
|
||||
Input: pipeline.ModuleBinding{Options: map[string]any{"nested": typed}},
|
||||
}}
|
||||
|
||||
payload := effective.RedactedResolvedPipelinePayload()
|
||||
nested, ok := payload.Input.Options["nested"].([]any)
|
||||
if !ok || len(nested) != 1 {
|
||||
t.Fatalf("redacted typed list = %#v", payload.Input.Options["nested"])
|
||||
}
|
||||
item, ok := nested[0].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("redacted typed map = %#v", nested[0])
|
||||
}
|
||||
if got := item["api_key"]; got != "[REDACTED]" {
|
||||
t.Fatalf("redacted api_key = %v", got)
|
||||
}
|
||||
if got := item["safe"]; got != "typed-container-safe" {
|
||||
t.Fatalf("safe option = %v", got)
|
||||
}
|
||||
|
||||
item["safe"] = "mutated"
|
||||
if got := typed[0]["safe"]; got != "typed-container-safe" {
|
||||
t.Fatalf("source typed map mutated through redacted payload: %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func redactionTestBinding(name string) pipeline.ModuleBinding {
|
||||
return pipeline.ModuleBinding{
|
||||
Module: "safe-" + name,
|
||||
|
||||
Reference in New Issue
Block a user