Module: Kodo::Memory::Redactor
- Defined in:
- lib/kodo/memory/redactor.rb
Constant Summary collapse
- PLACEHOLDER =
"[REDACTED]"- SENSITIVE_PATTERNS =
[ /\b\d{3}-\d{2}-\d{4}\b/, # SSN /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/, # Credit card /\b(sk|pk|api|key|token|secret|password)[-_]?\w{10,}/i, # API keys/tokens /\bpassword\s*[:=]\s*\S+/i, # password: value ].freeze
- LLM_PROMPT =
<<~PROMPT You are a sensitive data classifier. Analyze the following message and identify any sensitive information that should be redacted before storage. This includes but is not limited to: - Passwords, passphrases, or secrets mentioned in natural language - API keys, tokens, or credentials - Personal identifiers (SSN, credit card numbers, etc.) - Private keys or certificates Return ONLY a JSON array of objects with "start" and "end" character offsets (0-based, exclusive end) for each sensitive span. If nothing is sensitive, return an empty array []. Example: for "my database password is fluffybunny and that's it" Response: [{"start": 24, "end": 35}] Message to analyze: PROMPT
Class Method Summary collapse
-
.redact(text) ⇒ Object
Regex-only redaction (fast, free).
-
.redact_smart(text) ⇒ Object
Layered redaction: regex first, then LLM for anything regex missed.
-
.redact_with_llm(text) ⇒ Object
LLM-assisted redaction for context-dependent secrets.
- .sensitive?(text) ⇒ Boolean
Class Method Details
.redact(text) ⇒ Object
Regex-only redaction (fast, free)
38 39 40 41 42 43 44 |
# File 'lib/kodo/memory/redactor.rb', line 38 def redact(text) result = text.dup SENSITIVE_PATTERNS.each do |pattern| result.gsub!(pattern, PLACEHOLDER) end result end |
.redact_smart(text) ⇒ Object
Layered redaction: regex first, then LLM for anything regex missed
47 48 49 50 51 52 53 |
# File 'lib/kodo/memory/redactor.rb', line 47 def redact_smart(text) if sensitive?(text) redact(text) else redact_with_llm(text) end end |
.redact_with_llm(text) ⇒ Object
LLM-assisted redaction for context-dependent secrets
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/kodo/memory/redactor.rb', line 56 def redact_with_llm(text) response = Kodo::LLM.utility_chat.ask("#{LLM_PROMPT}#{text}") spans = parse_spans(response.content) return text if spans.empty? apply_redactions(text, spans) rescue StandardError => e Kodo.logger.debug("LLM redaction skipped: #{e.}") text end |
.sensitive?(text) ⇒ Boolean
33 34 35 |
# File 'lib/kodo/memory/redactor.rb', line 33 def sensitive?(text) SENSITIVE_PATTERNS.any? { |pattern| text.match?(pattern) } end |