picoclaw/pkg/security/ipia/detector.go

70 lines
2 KiB
Go

package ipia
import (
"context"
"fmt"
"strings"
"github.com/sipeed/picoclaw/pkg/agent"
)
var injectionPatterns = []string{
"ignore previous instructions",
"ignore all previous instructions",
"ignore the above instructions",
"system prompt:",
"you are now an admin",
"new mission:",
"forget your safety guidelines",
"stay in character as",
"dan mode",
}
// Detector implements the agent.ToolInterceptor interface to detect indirect prompt injection.
type Detector struct {
Enabled bool
}
// Ensure Detector implements ToolInterceptor.
var _ agent.ToolInterceptor = (*Detector)(nil)
// NewDetector creates a new IPIA detector.
func NewDetector(enabled bool) *Detector {
return &Detector{Enabled: enabled}
}
func (d *Detector) scan(text string) (bool, string) {
lower := strings.ToLower(text)
for _, pattern := range injectionPatterns {
if strings.Contains(lower, pattern) {
return true, pattern
}
}
return false, ""
}
func (d *Detector) BeforeTool(ctx context.Context, call *agent.ToolCallHookRequest) (*agent.ToolCallHookRequest, agent.HookDecision, error) {
return call, agent.HookDecision{Action: agent.HookActionContinue}, nil
}
func (d *Detector) AfterTool(ctx context.Context, resp *agent.ToolResultHookResponse) (*agent.ToolResultHookResponse, agent.HookDecision, error) {
if !d.Enabled || resp == nil || resp.Result == nil {
return resp, agent.HookDecision{Action: agent.HookActionContinue}, nil
}
if found, pattern := d.scan(resp.Result.ForLLM); found {
return resp, agent.HookDecision{
Action: agent.HookActionAbortTurn,
Reason: fmt.Sprintf("Indirect prompt injection detected in tool output (pattern: %q)", pattern),
}, nil
}
if found, pattern := d.scan(resp.Result.ForUser); found {
return resp, agent.HookDecision{
Action: agent.HookActionAbortTurn,
Reason: fmt.Sprintf("Indirect prompt injection detected in tool output (pattern: %q)", pattern),
}, nil
}
return resp, agent.HookDecision{Action: agent.HookActionContinue}, nil
}