Module: Restless::Mask
- Defined in:
- lib/restless/mask.rb
Overview
CONTRACT.md section 3. The masked end-user API key.
sha512-<base64(sha512(utf8(key)))>?<last4>
This is the lookup key the ingest and the dashboard index on, so it has to
be byte-identical across every SDK and the server. Server-side reference:
logs/src/clickhouse/models/request.ts.
Constant Summary collapse
- PLACEHOLDER_KEYS =
MASK-011. Setup-time placeholders the CLI (or a copy-pasted doc) leaves in curl examples. Hashing them would cluster unrelated requests under an arbitrary tail. Compared case-sensitively and exactly.
%w[ API_KEY_HERE YOUR_API_KEY YOUR_KEY REPLACE_ME ].freeze
- REDACTED_RE =
MASK-013. Redaction may run before masking in some call paths; hashing a sentinel produces a meaningless key.
\A...\zrather than^...$: PRIM-005. Ruby's$also matches before a trailing newline, so"<REDACTED:5>\n"would pass through unmasked here and be hashed in JavaScript. /\A<REDACTED:[0-9]+(?::[^>]*)?>\z/.freeze
Class Method Summary collapse
-
.mask(api_key) ⇒ Object
MASK-001.
Class Method Details
.mask(api_key) ⇒ Object
MASK-001. Returns nil for "no key" (MASK-010).
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/restless/mask.rb', line 38 def mask(api_key) return nil if api_key.nil? || api_key == "" return nil if PLACEHOLDER_KEYS.include?(api_key) # MASK-012. Idempotent for its own output. return api_key if api_key.start_with?("sha512-") return api_key if REDACTED_RE.match?(api_key) # MASK-004: hash the UTF-8 encoding. MASK-002: SHA-512. # MASK-003: STANDARD base64 with padding, not base64url. digest = Base64.strict_encode64(Digest::SHA512.digest(Text.to_utf8(api_key))) # MASK-006: last 4 CODE POINTS of the plaintext, never a byte or # UTF-16 code-unit slice. Keys shorter than 4 contribute all of theirs. last4 = api_key.chars.last(4).join "sha512-#{digest}?#{last4}" end |