closeyourit-ruby 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e7fb813ea37f0fb206ed31a5a960f795e364c6b23902d59b9b5d6e56ff4fb295
4
- data.tar.gz: 926f4e714781796b43ad8c9eaf0e6bf52d7976599a188d3e3720637df623d2bb
3
+ metadata.gz: a1d5e126aa96c17501b6830b9c7763979c239cd4bf4b0e0d43685b0652025378
4
+ data.tar.gz: c2d52c988da1288a5cea2986261481ec87fd67db8f3fb0921e2161c90653022e
5
5
  SHA512:
6
- metadata.gz: d8d590e358c098c3061383a85f9e1593b0b574a0ee52fcb67bbdd76e215cf44076703d9a1917d52acc99e43dee0ba58113afd9516c44b1adaa807341c22cf3ee
7
- data.tar.gz: e492934a7b0329fe0ba887dc45b05fa8c148ac3fb9665d866f95bdfe25b1cd114aa66bf95bca76cb83f3f16a96a34b4b24165152a247971e43888358e69291f9
6
+ metadata.gz: e9b3c6b9d352ad92055e710951157956c6fa3a75c8cbcee6bf6f9dbdbd3eb829ac6c990f54a38a72ef141c2e8d5a71eb714905355cbeee7be94a3957d6f6b22e
7
+ data.tar.gz: 6bc37b84e2d6a19e6690df0c40732df11fcf32069f1c9942f2d2ddbf6bb76cedb29a94e86c6fd4269002c9f0f9a4c352c53e1c0b41da9a32f5588503ad645b45
data/README.md CHANGED
@@ -92,7 +92,7 @@ end
92
92
  | `send_pii` | `false` | Master switch PII |
93
93
  | `obfuscate_sql` | `true` | Maschera i literal nello SQL |
94
94
  | `filter_parameters` | `[]` | Chiavi extra da redarre (mergiate con quelle di Rails) |
95
- | `scrub_message_patterns` | `[]` | Regexp da redarre da messaggi d'eccezione **e da `message` dei log** |
95
+ | `scrub_message_patterns` | `[]` | Regexp **aggiuntive** da redarre da messaggi d'eccezione **e da `message` dei log**; le credenziali `Bearer`/`Basic`/`Token` sono già redatte di serie |
96
96
  | `logs_enabled` | `true` | Master switch dello stream di log strutturato |
97
97
  | `logs_min_level` | `:info` | Soglia minima: i log sotto (es. `debug`) non vengono inviati |
98
98
  | `logs_sample_rate` | `1.0` | Frazione di log inviata (`1.0` tutto, `0.0` niente) |
@@ -381,8 +381,10 @@ Privacy-by-default (`send_pii = false`). In sintesi:
381
381
  - **SQL**: inviato il template, **mai** i bind values; `obfuscate_sql` maschera anche i literal inline.
382
382
  - **Chiavi sensibili** (`password`, `token`, `authorization`, `cookie`, `secret`, `api_key`, `csrf`,
383
383
  `credit_card`, `cvv`, `ssn`, `iban`, …) → `[FILTERED]`; estendibili con `filter_parameters`.
384
- - **Messaggi d'eccezione**: inviati per il debug, ma redigibili con `scrub_message_patterns` /
385
- `before_send`.
384
+ - **Messaggi d'eccezione**: inviati per il debug. Le credenziali di autenticazione finite nel testo
385
+ (`Authorization: Bearer …`, `Basic …`, `Token …`) sono redatte **di serie**, senza configurazione:
386
+ chiave e schema restano leggibili, sparisce il valore → `Authorization: Bearer [FILTERED]`. Il resto
387
+ è redigibile con `scrub_message_patterns` / `before_send`.
386
388
  - **Mai inviati**: variabili locali dei frame, argomenti dei metodi, IP/cookie/Authorization, token (che
387
389
  viaggia solo nell'header su HTTPS).
388
390
 
@@ -23,6 +23,53 @@ module CloseYourIt
23
23
  email phone telephone mobile dob birth passport bearer session pin pan
24
24
  ].freeze
25
25
 
26
+ # CYRB-23 — redazione INTEGRATA delle credenziali di autenticazione nel testo libero.
27
+ # `filter_params`/`filter_value` coprono gli header STRUTTURATI (la chiave `authorization` è in
28
+ # DENYLIST); un `Authorization: Bearer <token>` incollato dentro `exception.message` o dentro la
29
+ # riga di un log è testo, non una chiave, e prima di questo ticket partiva in chiaro perché
30
+ # `scrub_message` applicava solo i pattern configurati dall'utente (default `[]`).
31
+ #
32
+ # Forma del testo redatto (decisa qui una volta per tutti gli SDK — gemelli CYDA-26/CYJS-38/
33
+ # CYPY-15): chiave e schema restano leggibili, sparisce SOLO la credenziale →
34
+ # `Authorization: Bearer [FILTERED]`. Il messaggio resta diagnosticamente utile (si vede CHE
35
+ # c'era un header e con quale schema) senza trasportare il segreto.
36
+ AUTH_SCHEME = /(?i:Bearer|Basic|Token)/
37
+
38
+ # `token68` (RFC 7235) con padding `=` finale: la forma di una credenziale reale. Minimo 4
39
+ # caratteri — un `Basic` di credenziali brevi è corto (`dTpw` = `u:p`); a filtrare la prosa
40
+ # pensa NOT_PROSE, non la lunghezza.
41
+ CREDENTIAL_TOKEN = %r{[A-Za-z0-9\-._~+/]{4,}={0,2}}
42
+
43
+ # Guardia anti-falso-positivo per lo schema NUDO: quello che segue NON è una credenziale se ha
44
+ # la forma di una parola scritta da un umano, cioè sole lettere in UNA di tre forme —
45
+ # `minuscole`, `Capitalizzata`, `MAIUSCOLE` — e più corta di 20 caratteri. Salva "the bearer of
46
+ # bad news", "invalid bearer credentials", "Basic HTTP authentication", "Bearer Token expired".
47
+ # Tutto il resto è credenziale: entropia (cifre, `_`, `-`, `.`), maiuscole interne miste
48
+ # (`dTpw`, base64) o lunghezza ≥ 20 (nessuna parola è così lunga, mentre un token opaco di sole
49
+ # minuscole sì).
50
+ # Trade-off accettato: un CamelCase subito dopo lo schema (`Token MyAppName`) viene redatto.
51
+ PROSE_WORD = /(?:[a-z]{1,19}|[A-Z][a-z]{0,18}|[A-Z]{1,19})/
52
+ NOT_PROSE = %r{(?!\[FILTERED\])(?!#{PROSE_WORD}(?![A-Za-z0-9\-._~+/=]))}
53
+
54
+ # Valore di un header incollato nel testo: sequenza di pezzi non delimitatori, dove una stringa
55
+ # fra virgolette conta come UN pezzo. NON `\S+`, che si mangerebbe la `)` di
56
+ # "(Authorization: Bearer <token>)"; ma nemmeno una classe secca, che si fermerebbe alla prima
57
+ # virgoletta lasciando in chiaro il segreto di `Token token="<token>"` (sintassi RFC 7235 e
58
+ # `authenticate_with_http_token` di Rails) e di un frammento JSON incollato nel messaggio.
59
+ QUOTED = /"[^"\n]*"|'[^'\n]*'/
60
+ HEADER_VALUE = /(?:#{QUOTED}|[^\s,;)\]}"'<>])+/
61
+
62
+ # `Authorization:`/`authorization=` (anche `Proxy-Authorization`, anche `authorization_header`)
63
+ # seguito dal valore: qui la chiave è esplicita, quindi si redige senza euristica di prosa —
64
+ # over-redaction voluta, coerente con la DENYLIST. `[ \t]` e non `\s`: mai oltre il newline.
65
+ # Il lookahead copre lo schema OPZIONALE: senza, su un testo già redatto il motore farebbe
66
+ # backtracking sul ramo "senza schema" e redigerebbe la parola `Bearer` stessa (non idempotente).
67
+ AUTH_HEADER = /((?i:(?:proxy[_-]?)?authorization)[\w-]*["']?[ \t]*[:=][ \t]*)(?!(?:#{AUTH_SCHEME}[ \t]+)?\[FILTERED\])(#{AUTH_SCHEME}[ \t]+)?#{HEADER_VALUE}/
68
+
69
+ # Schema nudo (`Bearer <token>` senza il nome dell'header) seguito da qualcosa che ha la forma
70
+ # di una credenziale: qui la guardia di prosa serve, la parola "bearer" ricorre nei messaggi.
71
+ AUTH_CREDENTIAL = /(\b#{AUTH_SCHEME}[ \t]+)#{NOT_PROSE}#{CREDENTIAL_TOKEN}/
72
+
26
73
  STRING_LITERAL = /'(?:[^']|'')*'/
27
74
  NUMERIC_LITERAL = /\b\d+(?:\.\d+)?\b/
28
75
 
@@ -51,10 +98,16 @@ module CloseYourIt
51
98
  sql.to_s.gsub(STRING_LITERAL, "?").gsub(NUMERIC_LITERAL, "?")
52
99
  end
53
100
 
101
+ # Regola integrata (schemi di autenticazione) PRIMA dei pattern utente: le due protezioni si
102
+ # sommano, la prima non è disattivabile — privacy-by-default (CYRB-23).
54
103
  def scrub_message(message)
55
104
  return message if message.nil?
56
105
 
57
- @configuration.scrub_message_patterns.reduce(message.to_s) do |acc, pattern|
106
+ redacted = message.to_s
107
+ .gsub(AUTH_HEADER, "\\1\\2#{FILTERED}")
108
+ .gsub(AUTH_CREDENTIAL, "\\1#{FILTERED}")
109
+
110
+ @configuration.scrub_message_patterns.reduce(redacted) do |acc, pattern|
58
111
  acc.gsub(pattern, FILTERED)
59
112
  end
60
113
  end
@@ -91,12 +91,28 @@ module CloseYourIt
91
91
  target.relative? ? current + target : target
92
92
  end
93
93
 
94
- # Il Bearer va ri-inviato solo se il redirect resta sullo stesso host (o la sua variante www) e
95
- # senza downgrade https→http: evita di consegnare il segreto d'ingest a un host non fidato.
94
+ # Il Bearer va ri-inviato solo se il redirect resta sull'autorità dell'endpoint configurato:
95
+ # stesso host (o la sua variante www), stessa porta effettiva e senza downgrade https→http.
96
+ # La porta fa parte del confine di fiducia (CYRB-22): sullo stesso server una porta diversa è
97
+ # un servizio distinto, che non deve ricevere il segreto d'ingest.
96
98
  def same_authority?(origin, target)
97
99
  return false if origin.scheme == "https" && target.scheme != "https"
100
+ return false unless canonical_host(origin) == canonical_host(target)
98
101
 
99
- origin.host.to_s.sub(/\Awww\./, "") == target.host.to_s.sub(/\Awww\./, "")
102
+ origin.port == target.port || canonical_upgrade?(origin, target)
103
+ end
104
+
105
+ # Un endpoint configurato in `http` che redirige a `https` resta fidato: la porta cambia solo
106
+ # perché cambia lo schema (80 → 443), non perché punti a un servizio su porta non standard.
107
+ def canonical_upgrade?(origin, target)
108
+ origin.scheme == "http" && target.scheme == "https" &&
109
+ origin.port == origin.default_port && target.port == target.default_port
110
+ end
111
+
112
+ # Una porta esplicita pari alla default (`:443` su https) equivale a ometterla: `URI#port` la
113
+ # normalizza già, qui resta solo da neutralizzare la variante www dell'host canonico.
114
+ def canonical_host(uri)
115
+ uri.host.to_s.sub(/\Awww\./, "")
100
116
  end
101
117
 
102
118
  # Estrae " R…: messaggio" dall'envelope d'errore del backend, così il dev vede il codice d'errore
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloseYourIt
4
- VERSION = "0.9.3"
4
+ VERSION = "0.9.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: closeyourit-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessio Bussolari