Module: Sentiero::Fingerprint
- Defined in:
- lib/sentiero/fingerprint.rb,
lib/sentiero/fingerprint/config.rb
Overview
Computes the grouping fingerprint for an exception occurrence. The normalization regexes are deliberately linear-time (simple character classes with a single quantifier, no nesting) so untrusted backtraces cannot trigger catastrophic backtracking (ReDoS).
Defined Under Namespace
Classes: Config
Constant Summary collapse
- MAX_FRAMES =
Only the top frames drive grouping (deeper frames vary by call site).
5- MAX_FRAME_LENGTH =
1000- RUBY_NORMALIZER =
path:LINE:in 'method'(colon-glued) or barepath:LINE. Verbatim pre-refactor logic, kept byte-identical so upgrading does not fracture existing problem groups — this is the tier-1 (no platform tag) default. ->(frame) { frame .gsub(/0x[0-9a-fA-F]+/, "0xHEX") # memory addresses .gsub(/:[0-9]+(?=:in )/, ":N") # `path:LINE:in 'method'` .gsub(/:[0-9]+\z/, ":N") # `path:LINE` (top-level frame, no method) }
- CRYSTAL_NORMALIZER =
path.cr:LINE in 'method'orpath.cr:LINE:COL in 'method'— Crystal separates the line marker frominwith a space, not a colon, and may carry an optional:column, so the Ruby regex is a no-op on it. ->(frame) { frame .gsub(/0x[0-9a-fA-F]+/, "0xHEX") .gsub(/:[0-9]+(?::[0-9]+)?(?= in )/, ":N") }
- GENERIC_NORMALIZER =
Grammar-agnostic fallback: strips a trailing
:LINEor:LINE:COLonly, with noin-token assumption. Coarser than a language-specific normalizer (it can't see line noise that isn't at the end of the frame) but never mis-applies a foreign grammar. ->(frame) { frame .gsub(/0x[0-9a-fA-F]+/, "0xHEX") .gsub(/:[0-9]+(?::[0-9]+)?\z/, ":N") }
Class Method Summary collapse
- .compute(exception_class:, backtrace:, project:, normalizer: RUBY_NORMALIZER) ⇒ Object
-
.safe_normalize(normalizer, frame) ⇒ Object
Internal helper, not part of the public API.
Class Method Details
.compute(exception_class:, backtrace:, project:, normalizer: RUBY_NORMALIZER) ⇒ Object
47 48 49 50 51 |
# File 'lib/sentiero/fingerprint.rb', line 47 def compute(exception_class:, backtrace:, project:, normalizer: RUBY_NORMALIZER) frames = Array(backtrace).first(MAX_FRAMES).map { |frame| safe_normalize(normalizer, frame.to_s) } input = "#{project}\n#{exception_class}\n#{frames.join("\n")}" Digest::SHA256.hexdigest(input)[0, 40] end |
.safe_normalize(normalizer, frame) ⇒ Object
Internal helper, not part of the public API. Wraps a (possibly
user-registered) normalizer so a broken or pathological callable can
never break fingerprinting: the frame is capped before and after
normalization, and any exception falls back to the raw capped frame.
SystemStackError and NoMemoryError are non-StandardError exceptions a
pathological normalizer can realistically raise (e.g. unbounded
recursion); a bare rescue Exception is deliberately avoided so
Interrupt/SystemExit still propagate.
61 62 63 64 65 66 |
# File 'lib/sentiero/fingerprint.rb', line 61 def safe_normalize(normalizer, frame) capped = frame[0, MAX_FRAME_LENGTH].strip normalizer.call(capped).to_s[0, MAX_FRAME_LENGTH] rescue StandardError, SystemStackError, NoMemoryError capped end |