Module: PostHog::MCP::Sanitization::SecretDetection Private

Defined in:
lib/posthog/mcp/sanitization.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Last-resort credential detection for bare words.

Constant Summary collapse

MIN_LENGTH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

16
MIN_ENTROPY_BITS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

3.8
MIN_CHAR_CLASSES =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

3
HEX_DIGITS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'0123456789abcdefABCDEF'.chars.to_set.freeze
REJECT_CHARS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

"()[]{}<>'\"`,;".chars.to_set.freeze
UUID_RE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/
PATH_WORD_RE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/\A[a-z][a-z.]*\z/
PEM_PRIVATE_KEY_HINT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

A private key is redacted as a whole block, before anything is split into words. The body would mostly be caught word by word anyway - each base64 line is high entropy on its own - but that is a heuristic, and a line that happens to look like a path (two lowercase /-separated segments) slips through it. Key material should not ride on a heuristic.

Matched non-greedily, so several blocks in one value are handled separately, and terminated at end-of-string so a truncated block still loses its body. Covers the RSA/EC/OPENSSH/ENCRYPTED variants and the PGP BLOCK spelling.

'PRIVATE KEY'
PEM_PRIVATE_KEY_BLOCK =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

/
  -----BEGIN[A-Z0-9\ ]*\ PRIVATE\ KEY(?:\ BLOCK)?-----
  .*?
  (?:-----END[A-Z0-9\ ]*\ PRIVATE\ KEY(?:\ BLOCK)?-----|\z)
/mx
KNOWN_SECRET_MAX_SCAN_LENGTH =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

200
KNOWN_SECRET_RE =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

Regexp.union(
  /sk-ant-[A-Za-z0-9_-]{16,}/,
  /sk-(?:proj-)?[A-Za-z0-9_-]{20,}/,
  /hf_[A-Za-z0-9]{34}/,
  /AKIA[0-9A-Z]{16}/,
  /(?:ASIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ABIA|ACCA)[0-9A-Z]{16}/,
  /AIza[A-Za-z0-9_-]{35}/,
  /ya29\.[A-Za-z0-9_-]{20,}/,
  /do[opr]_v1_[a-f0-9]{64}/,
  /(?:sk|pk|rk)_(?:live|test)_[A-Za-z0-9]{16,}/,
  /sq0[a-z]{3}-[A-Za-z0-9_-]{22,43}/,
  /gh[pousr]_[A-Za-z0-9]{36}/,
  /github_pat_[A-Za-z0-9_]{20,}/,
  /gl(?:pat|ptt|rt|soat)-[A-Za-z0-9_-]{20}/,
  /glsa_[A-Za-z0-9]{32}_[A-Fa-f0-9]{8}/,
  /xox[abeoprs]-[A-Za-z0-9-]{10,}/,
  /xapp-[0-9]-[A-Za-z0-9-]{10,}/,
  /SK[0-9a-fA-F]{32}/,
  /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/,
  /key-[0-9a-f]{32}/,
  /[0-9a-f]{32}-us[0-9]{1,2}/,
  /npm_[A-Za-z0-9]{36}/,
  /pypi-AgEI[A-Za-z0-9_-]{50,}/,
  /dapi[0-9a-f]{32}/,
  /dp\.pt\.[A-Za-z0-9]{40,}/,
  /PMAK-[a-f0-9]{24}-[a-f0-9]{34}/,
  /lin_api_[A-Za-z0-9]{40}/,
  /ntn_[A-Za-z0-9]{40,}/,
  /shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}/,
  /NR(?:AK|JS|II|MA|RA)-[A-Za-z0-9]{27}/,
  /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}/
)

Class Method Summary collapse

Class Method Details

.high_entropy_secret?(value) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/posthog/mcp/sanitization.rb', line 465

def high_entropy_secret?(value)
  return false if value.include?(' ') || path_or_url?(value) || UUID_RE.match?(value)

  counts = value.each_char.tally
  distinct = counts.keys
  return false if distinct.any? { |ch| REJECT_CHARS.include?(ch) }

  has_lower = has_upper = has_digit = has_symbol = false
  hex_only = true
  distinct.each do |ch|
    return false if ch.match?(/\s/)

    case ch
    when /[[:lower:]]/
      has_lower = true
      hex_only = false unless HEX_DIGITS.include?(ch)
    when /[[:upper:]]/
      has_upper = true
      hex_only = false unless HEX_DIGITS.include?(ch)
    when /[[:digit:]]/
      has_digit = true
    else
      has_symbol = true
      hex_only = false
    end
  end
  return false if hex_only
  return false if [has_lower, has_upper, has_digit, has_symbol].count(true) < MIN_CHAR_CLASSES

  n = value.length.to_f
  entropy = counts.values.sum do |occurrences|
    p = occurrences / n
    -p * Math.log2(p)
  end
  entropy >= MIN_ENTROPY_BITS
end

.path_or_url?(value) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)


458
459
460
461
462
463
# File 'lib/posthog/mcp/sanitization.rb', line 458

def path_or_url?(value)
  return true if value.include?('://') || value.include?('\\')
  return false unless value.include?('/')

  value.split('/').count { |segment| !segment.empty? && PATH_WORD_RE.match?(segment) } >= 2
end

.redact_private_key_blocks(value) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the value with every -----BEGIN … PRIVATE KEY----- block replaced, leaving surrounding text intact.

Returns:

  • (String) —

    the value with every -----BEGIN … PRIVATE KEY----- block replaced, leaving surrounding text intact



452
453
454
455
456
# File 'lib/posthog/mcp/sanitization.rb', line 452

def redact_private_key_blocks(value)
  return value unless value.include?(PEM_PRIVATE_KEY_HINT)

  value.gsub(PEM_PRIVATE_KEY_BLOCK, REDACTED_VALUE)
end

.secret?(value) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Whole private-key blocks are handled by redact_private_key_blocks before a value is ever split, so there is no marker check here: the marker contains a space and could never match a single word anyway.

Returns:

  • (Boolean)


437
438
439
440
441
442
443
444
445
446
447
448
# File 'lib/posthog/mcp/sanitization.rb', line 437

def secret?(value)
  return false unless value.is_a?(String) && !value.empty?

  n = value.length
  return false if n < MIN_LENGTH
  return true if high_entropy_secret?(value)
  return KNOWN_SECRET_RE.match?(value) if n <= KNOWN_SECRET_MAX_SCAN_LENGTH

  false
rescue StandardError
  false
end