Class: Vangrail::Rails::Secrets

Inherits:
Vangrail::Rail show all
Defined in:
lib/vangrail/rails/secrets.rb

Overview

Redacts credentials, returning :modified rather than blocking.

This is the rail that justifies having three statuses. An answer that quotes a config file with a live token is a useful answer with one bad span in it: blocking it throws away the help, and passing it leaks the token. Replacing the span keeps both halves honest, and the caller can see from the status that what it is about to show has been edited.

Patterns cover shapes that are unambiguous on sight. Anything needing judgement belongs in a policy rail, not here: a false positive silently corrupts an answer, which is worse than a missed match a later rail can still catch.

Constant Summary collapse

PLACEHOLDER =
'[redacted]'
DEFAULT_PATTERNS =
{
  'private_key' => /-----BEGIN[A-Z ]*PRIVATE KEY-----.*?-----END[A-Z ]*PRIVATE KEY-----/m,
  'openai_key' => /\bsk-[A-Za-z0-9_-]{20,}\b/,
  'anthropic_key' => /\bsk-ant-[A-Za-z0-9_-]{20,}\b/,
  'github_token' => /\bgh[pousr]_[A-Za-z0-9]{30,}\b/,
  'slack_token' => /\bxox[abposr]-[A-Za-z0-9-]{10,}\b/,
  'aws_access_key' => /\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/,
  'jwt' => /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/,
  'bearer_header' => /\b(?i:authorization)\s*:\s*(?i:bearer)\s+\S{12,}/,
  'inline_password' => /\b(?i:password|passwd|api[_-]?key|secret)\s*[=:]\s*(?!\[redacted\])\S{6,}/
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(patterns: DEFAULT_PATTERNS, placeholder: PLACEHOLDER, name: 'secrets', sides: [:output]) ⇒ Secrets

Returns a new instance of Secrets.



36
37
38
39
40
41
# File 'lib/vangrail/rails/secrets.rb', line 36

def initialize(patterns: DEFAULT_PATTERNS, placeholder: PLACEHOLDER, name: 'secrets',
               sides: [:output])
  super(name: name, sides: sides)
  @patterns = patterns
  @placeholder = placeholder
end

Instance Attribute Details

#patternsObject (readonly)

Returns the value of attribute patterns.



34
35
36
# File 'lib/vangrail/rails/secrets.rb', line 34

def patterns
  @patterns
end

#placeholderObject (readonly)

Returns the value of attribute placeholder.



34
35
36
# File 'lib/vangrail/rails/secrets.rb', line 34

def placeholder
  @placeholder
end

Instance Method Details

#cache_key(text, _context) ⇒ Object



47
48
49
# File 'lib/vangrail/rails/secrets.rb', line 47

def cache_key(text, _context)
  text
end

#call(text, _context) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/vangrail/rails/secrets.rb', line 51

def call(text, _context)
  body = text.to_s
  found = []
  redacted = patterns.reduce(body) do |acc, (label, pattern)|
    acc.gsub(pattern) do |match|
      found << label
      replacement(label, match)
    end
  end
  return pass if found.empty?

  modify(redacted, categories: found.uniq, reason: "redacted #{found.uniq.join(', ')}")
end

#offline?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/vangrail/rails/secrets.rb', line 43

def offline?
  true
end