Mask padded credentials as sent to providers
This commit is contained in:
@@ -122,6 +122,11 @@ func (p *fixtureProvider) serve(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
content := `{"corrections":[{"id":1,"original_text":"hello world","corrected_text":"Hello world","confidence":0.99}]}`
|
||||
if p.mode == "invalid-credential-echo" {
|
||||
sent := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
|
||||
raw, _ := json.Marshal(map[string]string{"unexpected": sent + "|" + p.secret})
|
||||
content = string(raw)
|
||||
}
|
||||
if p.mode == "repair-filter-later-failure" && !validation {
|
||||
if proposalCall == 1 {
|
||||
content = `{"wrong":true}`
|
||||
@@ -231,6 +236,9 @@ func newProcessFixture(t *testing.T, mode, modules string) processFixture {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
p := &fixtureProvider{mode: mode, secret: `selected-secret-"\-123`, entered: make(chan struct{}, 1)}
|
||||
if mode == "invalid-credential-echo" {
|
||||
p.secret = " synthetic-echo-credential\t "
|
||||
}
|
||||
server := httptest.NewServer(http.HandlerFunc(p.serve))
|
||||
t.Cleanup(server.Close)
|
||||
resources := fmt.Sprintf("backends:\n - id: local\n endpoint: %s/v1\n api_key_env: AUDITA_TEST_PROVIDER_KEY\nprofiles:\n - id: proposal\n backend: local\n model: local\n - id: validation\n backend: local\n model: local\n", server.URL)
|
||||
@@ -944,6 +952,38 @@ func TestSubprocessAllModelFailureStillCompletes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessMasksPaddedCredentialInProviderResponse(t *testing.T) {
|
||||
f := newProcessFixture(t, "invalid-credential-echo", "[grammar]")
|
||||
effective := strings.TrimSpace(f.provider.secret)
|
||||
code, stdout, stderr := f.run(t, true, "--debug")
|
||||
if code != 0 || f.provider.callCount() != 1 || !strings.Contains(stderr, "1 work units with errors") {
|
||||
t.Fatalf("code=%d calls=%d stdout=%q stderr=%q", code, f.provider.callCount(), stdout, stderr)
|
||||
}
|
||||
if strings.Contains(stderr, effective) || strings.Contains(stdout, effective) {
|
||||
t.Fatal("credential escaped redaction in command output")
|
||||
}
|
||||
responseCaptured := false
|
||||
err := filepath.WalkDir(f.dir, func(path string, entry os.DirEntry, err error) error {
|
||||
if err != nil || entry.IsDir() {
|
||||
return err
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bytes.Contains(data, []byte(effective)) {
|
||||
t.Errorf("credential escaped redaction in %s", entry.Name())
|
||||
}
|
||||
if strings.HasSuffix(entry.Name(), "-response-payload.json") && bytes.Contains(data, []byte("[REDACTED]|[REDACTED]")) {
|
||||
responseCaptured = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil || !responseCaptured {
|
||||
t.Fatalf("missing redacted provider echo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubprocessAllValidationFailureStillCompletes(t *testing.T) {
|
||||
f := newProcessFixture(t, "all-validation-failure", "[grammar, grammar]")
|
||||
code, stdout, stderr := f.run(t, true, "--verbose")
|
||||
|
||||
@@ -352,6 +352,8 @@ still produces normalized output without provider calls.
|
||||
|
||||
## R10 — P2: Mask the Credential Value PromptKit Actually Sends
|
||||
|
||||
**Status:** Completed. Redaction now includes the raw and trimmed forms of referenced credential values. A local provider regression echoes both forms and checks retained diagnostics, reports, and command output.
|
||||
|
||||
**Category**
|
||||
|
||||
Credential normalization and diagnostic redaction disagree in Stage 4.
|
||||
|
||||
@@ -27,7 +27,7 @@ type transportFunc func(*http.Request) (*http.Response, error)
|
||||
func (f transportFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||
|
||||
func TestRoleClientUsesNativeTargetAndRedactsCredential(t *testing.T) {
|
||||
t.Setenv("AUDITA_TEST_KEY", "synthetic-secret-value")
|
||||
t.Setenv("AUDITA_TEST_KEY", " synthetic-secret-value\t ")
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "config.yml"), []byte("version: 3\n"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package llm
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/audita/v3/internal/core/redaction"
|
||||
)
|
||||
@@ -41,6 +42,10 @@ func (r *Runtime) ConfiguredSecrets(ctx context.Context, additionalEnvNames ...s
|
||||
for name := range names {
|
||||
if value := os.Getenv(name); value != "" {
|
||||
secrets = append(secrets, value)
|
||||
// PromptKit trims the value before sending it to the provider.
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" && trimmed != value {
|
||||
secrets = append(secrets, trimmed)
|
||||
}
|
||||
}
|
||||
}
|
||||
return secrets
|
||||
|
||||
Reference in New Issue
Block a user