Please report security issues privately via GitHub's "Report a vulnerability" flow on this repository, rather than opening a public issue. You can expect an acknowledgement within a few working days.
This gem handles credentials that authenticate against a tax authority, and invoice payloads that are legally binding documents. Reports about credential leakage, weakened TLS, or incorrect cryptographic parameters are treated as high severity.
Security fixes land on the latest minor release.
The supported interpreter range is whatever required_ruby_version declares — currently
MRI >= 3.2. Note that this includes Ruby 3.2 even though 3.2 is EOL upstream: the
floor is a deliberate commitment (see DESIGN.md §3), so 3.2 users do receive fixes from
this gem.
That said, an EOL interpreter stops receiving fixes for CVEs in Ruby itself, which this project cannot patch. If you are on 3.2 for compliance reasons, upgrading Ruby is still the stronger position.
Once a series does fall below the floor in a future minor release, required_ruby_version
resolves at install time, so users on that interpreter stay on the last compatible release
and stop receiving fixes from us.
The following must never appear in logs, #inspect output, exception messages, or test
fixtures:
- KSeF tokens and JWTs (access and refresh)
- Session symmetric keys and IVs
- Full invoice payloads at default log level
Enforced by:
-
A redacting
#inspecton configuration and authentication objects. -
spec/cassette_hygiene_spec.rb, which scans every committed VCR cassette forBearerheaders and for any value this machine holds inKSEF_TEST_TOKEN. Not the NIP: a tax identifier is printed on every invoice and is embedded in the KSeF number itself, so redacting it corrupts the very documents a cassette exists to hold — and costs the two checks only a real response can support, the KSeF number's checksum and KSeF's own integrity header. The token is the credential.It also scans for anything shaped like a JSON Web Token, added 2026-08-26 after a recording carried a live refresh token past both of the other checks (caught before it was committed; the cassettes now in the repository were recorded after the fix and are scanned on every run): it was in a JSON response body, so it was neither a
Bearerheader nor a value this machine held in its environment. Scanning for the shape of a credential catches the ones you did not predict. It finds cassettes by content, not by path, so one saved somewhere unconventional is still scanned. Three cassettes are committed as of 2026-08-26, so the check is live rather than vacuous. What it scans is the decoded content — bodies, including the ones YAML stores as!binary, and every header value — because a scan of the file misses a secret in a body that carries one non-ASCII byte, and Polish text in this domain routinely does.It looks for four shapes: a
Bearervalue, anything JWT-shaped, any value this machine holds inKSEF_TEST_TOKEN, and a pre-signed URL that still carries its signature. That last one was added after two cassettes reached git holding a live Azure SAS for a TEST UPO — read-only, three-day, and invisible to the other three checks because a SASsigis none of them. The scrubbing hooks it depends on are inspec/support/vcr.rb, written before the first recording rather than after, because a cassette committed without them is a leakgitremembers. -
Integration tests reading credentials only from environment variables (
KSEF_TEST_NIP,KSEF_TEST_TOKEN,KSEF_ENV=test).
- TLS verification is always on. No code path may set
VERIFY_NONE; there is no configuration option to disable it. - Minimum TLS version is 1.2.
The test suite aborts if KSEF_ENV=prod, and the nightly workflow fails the same way.
Never point automated tests at the production environment — invoices submitted there have
full legal force.
KSeF records and analyses rate-limit breaches, and explicitly treats spreading one
context across many IP addresses as an abuse pattern. This client honours Retry-After
and will never work around a 429 by rotating connections. Please do not add such a
mechanism.
Releases use RubyGems Trusted Publishing (OIDC). No long-lived RubyGems API key exists
for this project. The gemspec sets rubygems_mfa_required.