Fix false positives in agent guardrails

Skip empty fingerprints in SystemPromptLeakGuardrail to prevent blank
values from flagging every message. Replace overly broad "sk-" pattern
in SensitiveDataGuardrail with specific LLM provider prefixes
("sk-proj-" for OpenAI, "sk-ant-" for Anthropic) to avoid false
positives on common words like "risk-based" or "task-management".

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-27 12:16:37 +01:00
committed by Sacha Al Himdani
parent 2f8674471b
commit 4725a1b080
5 changed files with 32 additions and 16 deletions

View File

@@ -59,20 +59,20 @@ func (g *PromptInjectionGuardrail) Check(ctx context.Context, messages []llm.Mes
resp, err := g.client.ChatCompletion( resp, err := g.client.ChatCompletion(
ctx, ctx,
&llm.ChatCompletionRequest{ &llm.ChatCompletionRequest{
Model: "gpt-4o-mini", Model: "gpt-4o-mini",
Messages: []llm.Message{ Messages: []llm.Message{
{ {
Role: llm.RoleSystem, Role: llm.RoleSystem,
Parts: []llm.Part{llm.TextPart{Text: promptInjectionClassifierPrompt}}, Parts: []llm.Part{llm.TextPart{Text: promptInjectionClassifierPrompt}},
}, },
{ {
Role: llm.RoleUser, Role: llm.RoleUser,
Parts: []llm.Part{llm.TextPart{Text: userText}}, Parts: []llm.Part{llm.TextPart{Text: userText}},
},
}, },
MaxTokens: new(10),
Temperature: new(0.0),
}, },
MaxTokens: new(10),
Temperature: new(0.0),
},
) )
if err != nil { if err != nil {
// If the classifier fails, allow the message through rather than // If the classifier fails, allow the message through rather than

View File

@@ -58,7 +58,8 @@ func (g *SensitiveDataGuardrail) Check(_ context.Context, message llm.Message) (
"sk_test_", "sk_test_",
// LLM provider keys // LLM provider keys
"sk-", "sk-proj-", // OpenAI
"sk-ant-", // Anthropic
// JWT tokens // JWT tokens
"eyj", // base64-encoded JSON header "eyj", // base64-encoded JSON header

View File

@@ -66,6 +66,8 @@ func TestSensitiveDataGuardrail_Check(t *testing.T) {
// LLM provider keys // LLM provider keys
{"openai key", "The API key is sk-proj-abc123", true}, {"openai key", "The API key is sk-proj-abc123", true},
{"anthropic key", "Key: sk-ant-api03-abc123", true},
{"sk prefix not a false positive", "This is a risk-based approach to task-management.", false},
// JWT tokens // JWT tokens
{"jwt token", "Token: eyJhbGciOiJIUzI1NiJ9.payload.sig", true}, {"jwt token", "Token: eyJhbGciOiJIUzI1NiJ9.payload.sig", true},

View File

@@ -27,9 +27,12 @@ type SystemPromptLeakGuardrail struct {
} }
func NewSystemPromptLeakGuardrail(fingerprints []string) *SystemPromptLeakGuardrail { func NewSystemPromptLeakGuardrail(fingerprints []string) *SystemPromptLeakGuardrail {
lowered := make([]string, len(fingerprints)) lowered := make([]string, 0, len(fingerprints))
for i, f := range fingerprints { for _, f := range fingerprints {
lowered[i] = strings.ToLower(f) if f == "" {
continue
}
lowered = append(lowered, strings.ToLower(f))
} }
return &SystemPromptLeakGuardrail{fingerprints: lowered} return &SystemPromptLeakGuardrail{fingerprints: lowered}

View File

@@ -66,4 +66,14 @@ func TestSystemPromptLeakGuardrail_Check(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.False(t, result.Tripwire) assert.False(t, result.Tripwire)
}) })
t.Run("empty fingerprints are ignored", func(t *testing.T) {
t.Parallel()
g := guardrail.NewSystemPromptLeakGuardrail([]string{"", "secret phrase", ""})
result, err := g.Check(context.Background(), assistantMessage("hello world"))
require.NoError(t, err)
assert.False(t, result.Tripwire)
})
} }