Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxCampaignPromptRunes caps custom campaign / brand AI prompts (prompt-injection surface).
|
||||
MaxCampaignPromptRunes = 8000
|
||||
// MaxBrandFieldRunes caps individual brand kit text fields.
|
||||
MaxBrandFieldRunes = 2000
|
||||
// MaxBrandListItems caps dos/donts/preferred_terms entries.
|
||||
MaxBrandListItems = 40
|
||||
// MaxBrandListItemRunes caps each list entry.
|
||||
MaxBrandListItemRunes = 200
|
||||
)
|
||||
|
||||
var injectPhrase = regexp.MustCompile(`(?i)(ignore\s+previous|system\s*:|assistant\s*:|<\s*/?\s*script)`)
|
||||
|
||||
// SanitizePrompt strips controls, soft-filters injection phrases, and truncates by runes.
|
||||
func SanitizePrompt(s string, maxRunes int) string {
|
||||
if maxRunes <= 0 {
|
||||
maxRunes = MaxCampaignPromptRunes
|
||||
}
|
||||
s = stripControls(strings.TrimSpace(s))
|
||||
s = injectPhrase.ReplaceAllString(s, "[filtered]")
|
||||
return TruncateRunes(s, maxRunes)
|
||||
}
|
||||
|
||||
// CapPromptLength reports whether s exceeds max (rune count).
|
||||
func CapPromptLength(s string, maxRunes int) bool {
|
||||
if maxRunes <= 0 {
|
||||
maxRunes = MaxCampaignPromptRunes
|
||||
}
|
||||
n := 0
|
||||
for range s {
|
||||
n++
|
||||
if n > maxRunes {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SanitizeBrandList cleans and bounds a brand kit string list.
|
||||
func SanitizeBrandList(in []string) []string {
|
||||
if len(in) == 0 {
|
||||
return []string{}
|
||||
}
|
||||
out := make([]string, 0, len(in))
|
||||
seen := map[string]struct{}{}
|
||||
for _, s := range in {
|
||||
s = SanitizePrompt(s, MaxBrandListItemRunes)
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
key := strings.ToLower(s)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
out = append(out, s)
|
||||
if len(out) >= MaxBrandListItems {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func stripControls(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
b.Grow(len(s))
|
||||
for _, r := range s {
|
||||
if r == '\n' || r == '\t' || unicode.IsPrint(r) {
|
||||
b.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// TruncateRunes truncates s to at most max runes.
|
||||
func TruncateRunes(s string, max int) string {
|
||||
if max <= 0 {
|
||||
return ""
|
||||
}
|
||||
n := 0
|
||||
for i := range s {
|
||||
if n == max {
|
||||
return s[:i]
|
||||
}
|
||||
n++
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in New Issue
Block a user