Add JA4 support#1428
Conversation
Greptile SummaryThis PR adds support for storing Cloudflare JA4 TLS fingerprints alongside heartbeats to help admins identify requests with spoofed user-agents. When a
Confidence Score: 4/5Safe to merge; the change is additive, the migration is non-locking, and deduplication logic is untouched. The implementation is consistent with existing patterns (cf. ip_address via CF-Connecting-IP), the migration correctly defers FK validation and uses a concurrent index, and create_or_find_by! is the right tool for thread-safe fingerprint upserts. The only minor gap is a missing model-level uniqueness validation on Ja4, which leaves direct Ja4.create! calls outside the resolve path without a clean validation error. app/models/ja4.rb — could benefit from a uniqueness validation to complement the DB constraint. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant CF as Cloudflare
participant Controller as HackatimeController
participant Ingest as HeartbeatIngest
participant Ja4Model as Ja4.resolve
participant DB
Client->>CF: POST /heartbeats (TLS handshake)
CF->>Controller: Request + CF-JA4 header
Controller->>Ingest: "call(request_context: { ja4: t13d... })"
Ingest->>Ja4Model: Ja4.resolve(fingerprint)
Ja4Model->>DB: create_or_find_by!(fingerprint:)
DB-->>Ja4Model: Ja4 record (new or existing)
Ja4Model-->>Ingest: "@resolved_ja4"
Ingest->>DB: Heartbeat.insert(attrs + ja4_id:)
DB-->>Ingest: persisted heartbeat
Ingest-->>Controller: Result
Controller-->>Client: 202 Accepted
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
app/models/ja4.rb:4
**Missing model-level uniqueness validation**
`Ja4` only validates `presence` but not uniqueness at the model layer. `create_or_find_by!` correctly handles the DB-level constraint for the `resolve` path, but any direct `Ja4.create!(fingerprint: x)` call (e.g. in tests or future code) will hit a raw `ActiveRecord::RecordNotUnique` rather than a clear validation error. Adding `validates :fingerprint, uniqueness: true` makes the constraint explicit and produces friendlier messages outside the `resolve` path.
Reviews (1): Last reviewed commit: "Add JA4 support" | Re-trigger Greptile |
Summary of the problem
Describe your changes
Screenshots / Media
JA4 is a trademark of FoxIO, Inc.