foil-server 0.4.0 → 0.5.0

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: 142ee8a11ee7b38b933aab55b22188e9f2a6ef73186097a5ea708d93fa0be2b8
4
- data.tar.gz: cbcf119648d0de7699f30ff12894ed6303e7a6c1a874178db73e6a706a828b6a
3
+ metadata.gz: e998be2281e9d8fbacd45e49596b2424d46509066ff8f97e1ddfa5763531ba10
4
+ data.tar.gz: be9cae7a65e5d4e13c0ef726d56f1ca9281237b50e79345a0dc5a5904987d9f6
5
5
  SHA512:
6
- metadata.gz: 899d02338303288f518178bb8c1a72e815dd2372ef428534fe3c36a2574a09eecb021164ebda90f90439d74ce0bdd112d5d2301ddf393dd837fe4bf18e91103a
7
- data.tar.gz: c88c199301d6c99dc1e612824b3d96caddaec9fd70b1dee9abc31f29ac071e75fcfba2d4bc55055ee7c2f75b8ca394b2bd8e01d0d7cc3eeacb5b779b2dc4465c
6
+ metadata.gz: f9bfc32b352566af63551b720bba2f6dc790040073cb793ffa3c31017d126d960ae2cdacc0a1e4cb6db89785a4eb074456e34945274578d01744ba849eefc76b
7
+ data.tar.gz: 21141586684db218908995affc1fd2ac5271dca1ae273a2208682c77a1936b4427c08d376d45c337f73e1d1ec712f906770b660a3b7f049beb30873d36647f25
data/README.md CHANGED
@@ -38,10 +38,12 @@ require "foil/server"
38
38
 
39
39
  client = Foil::Server::Client.new(secret_key: "sk_live_...")
40
40
 
41
- page = client.sessions.list(verdict: "bot", limit: 25)
41
+ page = client.sessions.list(verdict: "bot", limit: 25, client_user_id: "user_123", client_application_id: "app_123")
42
42
  session = client.sessions.get("sid_0123456789abcdefghjkmnpqrs")
43
43
  client.sessions.attach_client_user("sid_0123456789abcdefghjkmnpqrs", "user_123")
44
44
  client.sessions.clear_client_user("sid_0123456789abcdefghjkmnpqrs")
45
+ client.sessions.attach_client_application("sid_0123456789abcdefghjkmnpqrs", "app_123")
46
+ client.sessions.clear_client_application("sid_0123456789abcdefghjkmnpqrs")
45
47
 
46
48
  puts "#{session[:decision][:automation_status]} #{session[:highlights].first&.fetch(:summary, nil)}"
47
49
  ```
@@ -151,12 +151,14 @@ module Foil
151
151
  end
152
152
 
153
153
  class SessionsResource < BaseResource
154
- def list(limit: nil, cursor: nil, verdict: nil, search: nil)
154
+ def list(limit: nil, cursor: nil, verdict: nil, search: nil, client_user_id: nil, client_application_id: nil)
155
155
  payload = @client.request_json("GET", "/v1/sessions", query: {
156
156
  limit: limit,
157
157
  cursor: cursor,
158
158
  verdict: verdict,
159
- search: search
159
+ search: search,
160
+ client_user_id: client_user_id,
161
+ client_application_id: client_application_id
160
162
  })
161
163
  list_result(payload)
162
164
  end
@@ -177,11 +179,30 @@ module Foil
177
179
  })[:data]
178
180
  end
179
181
 
180
- def iter(limit: nil, verdict: nil, search: nil)
182
+ def attach_client_application(session_id, client_application_id)
183
+ @client.request_json("PATCH", "/v1/sessions/#{CGI.escape(session_id)}", body: {
184
+ client_application_id: client_application_id
185
+ })[:data]
186
+ end
187
+
188
+ def clear_client_application(session_id)
189
+ @client.request_json("PATCH", "/v1/sessions/#{CGI.escape(session_id)}", body: {
190
+ client_application_id: nil
191
+ })[:data]
192
+ end
193
+
194
+ def iter(limit: nil, verdict: nil, search: nil, client_user_id: nil, client_application_id: nil)
181
195
  Enumerator.new do |yielder|
182
196
  cursor = nil
183
197
  loop do
184
- page = list(limit: limit, cursor: cursor, verdict: verdict, search: search)
198
+ page = list(
199
+ limit: limit,
200
+ cursor: cursor,
201
+ verdict: verdict,
202
+ search: search,
203
+ client_user_id: client_user_id,
204
+ client_application_id: client_application_id
205
+ )
185
206
  page.items.each { |item| yielder << item }
186
207
  break unless page.has_more && page.next_cursor
187
208
 
@@ -18,6 +18,8 @@ module Foil
18
18
  V2_RECIPIENT_BYTES = RECIPIENT_ID_BYTES + NONCE_BYTES + CONTENT_KEY_BYTES + TAG_BYTES
19
19
  V2_PAYLOAD_AAD_PREFIX = "foil-sealed-results-v2\0payload\0".b
20
20
  V2_WRAP_AAD_PREFIX = "foil-sealed-results-v2\0recipient\0".b
21
+ # Decompressed payloads are a few kilobytes; the cap bounds memory if a token decompresses to far more.
22
+ MAX_PAYLOAD_BYTES = 1024 * 1024
21
23
 
22
24
  module_function
23
25
 
@@ -30,7 +32,7 @@ module Foil
30
32
  raise TokenVerificationError, "Foil token is too short." if raw.bytesize < 29
31
33
 
32
34
  compressed = decrypt_payload(raw, resolved_secret)
33
- payload = JSON.parse(Zlib::Inflate.inflate(compressed))
35
+ payload = JSON.parse(inflate_payload(compressed))
34
36
  deep_symbolize(payload)
35
37
  rescue ConfigurationError, TokenVerificationError
36
38
  raise
@@ -49,6 +51,21 @@ module Foil
49
51
  end
50
52
  private_class_method :derive_key
51
53
 
54
+ def inflate_payload(compressed)
55
+ inflater = Zlib::Inflate.new
56
+ payload = "".b
57
+ inflater.inflate(compressed) do |chunk|
58
+ payload << chunk
59
+ raise TokenVerificationError, "Foil token payload is too large." if payload.bytesize > MAX_PAYLOAD_BYTES
60
+ end
61
+ raise Zlib::BufError, "Foil token payload is truncated." unless inflater.finished?
62
+
63
+ payload
64
+ ensure
65
+ inflater&.close
66
+ end
67
+ private_class_method :inflate_payload
68
+
52
69
  def decrypt_gcm(ciphertext, key, nonce, tag, aad = "".b)
53
70
  cipher = OpenSSL::Cipher.new("aes-256-gcm")
54
71
  cipher.decrypt
@@ -1,5 +1,5 @@
1
1
  module Foil
2
2
  module Server
3
- VERSION = "0.4.0".freeze
3
+ VERSION = "0.5.0".freeze
4
4
  end
5
5
  end
@@ -6,7 +6,7 @@
6
6
  "first_seen_at": "2026-03-24T19:58:00.000Z",
7
7
  "last_seen_at": "2026-03-24T20:00:05.000Z",
8
8
  "seen_count": 3,
9
- "expires_at": "2026-06-22T20:00:05.000Z"
9
+ "expires_at": "9999-12-31T00:00:00.000Z"
10
10
  },
11
11
  "latest_request": {
12
12
  "user_agent": "Mozilla/5.0",
@@ -7,7 +7,7 @@
7
7
  "first_seen_at": "2026-03-24T19:58:00.000Z",
8
8
  "last_seen_at": "2026-03-24T20:00:05.000Z",
9
9
  "seen_count": 3,
10
- "expires_at": "2026-06-22T20:00:05.000Z"
10
+ "expires_at": "9999-12-31T00:00:00.000Z"
11
11
  },
12
12
  "latest_request": {
13
13
  "user_agent": "Mozilla/5.0",
@@ -4,6 +4,7 @@
4
4
  "id": "sid_0123456789abcdefghjkmnpqrs",
5
5
  "created_at": "2026-03-24T20:00:00.000Z",
6
6
  "client_user_id": "user_123",
7
+ "client_application_id": "app_123",
7
8
  "behavior": null,
8
9
  "decision": {
9
10
  "event_id": "evt_23456789abcdefghjkmnpqrstv",
@@ -144,7 +145,7 @@
144
145
  "accuracy_radius_km": 10
145
146
  },
146
147
  "routing": {
147
- "asn": "64512",
148
+ "asn": "AS64512",
148
149
  "organization": "Example Networks"
149
150
  },
150
151
  "anonymity": {
@@ -174,7 +175,7 @@
174
175
  "client_types": [
175
176
  "consumer"
176
177
  ],
177
- "client_count": 3
178
+ "client_count": null
178
179
  },
179
180
  "evaluated_at": "2026-03-24T20:00:05.000Z"
180
181
  },
@@ -206,7 +207,8 @@
206
207
  "lifecycle": {
207
208
  "first_seen_at": "2026-03-01T18:22:11.000Z",
208
209
  "last_seen_at": "2026-03-24T20:00:05.000Z",
209
- "seen_count": 14
210
+ "seen_count": 14,
211
+ "resolution_count": 14
210
212
  }
211
213
  },
212
214
  "connection_fingerprint": {
@@ -383,7 +385,7 @@
383
385
  },
384
386
  "canvas": {
385
387
  "hash": 123456,
386
- "geometry_hash": 998877,
388
+ "geometry_hash": null,
387
389
  "text_hash": 112233,
388
390
  "winding": null,
389
391
  "noise_detected": true,
@@ -398,6 +400,7 @@
398
400
  "parameters_hash": "params_123",
399
401
  "extensions_hash": 333444,
400
402
  "extension_parameters_hash": "webgl2_456",
403
+ "webgl2_parameters_hash": "webgl2_456",
401
404
  "shader_precision_hash": 777888
402
405
  },
403
406
  "webgpu": {
@@ -412,7 +415,7 @@
412
415
  "audio": {
413
416
  "hash": 124.0434,
414
417
  "sample_rate": 48000,
415
- "channel_count": null,
418
+ "channel_count": 2,
416
419
  "voice_count": 3,
417
420
  "local_voice_count": 2,
418
421
  "default_voice_lang": "en-US",
@@ -420,11 +423,11 @@
420
423
  },
421
424
  "fonts": {
422
425
  "detected_count": 2,
423
- "tested_count": null,
426
+ "tested_count": 41,
424
427
  "enumeration_hash": 3649417752,
425
428
  "metrics_hash": 223344,
426
429
  "preferences_hash": null,
427
- "emoji_hash": 112233
430
+ "emoji_hash": "5f0e2a71"
428
431
  },
429
432
  "media": {
430
433
  "device_count": 1,
@@ -5,6 +5,7 @@
5
5
  "id": "sid_0123456789abcdefghjkmnpqrs",
6
6
  "created_at": "2026-03-24T20:00:00.000Z",
7
7
  "client_user_id": "user_123",
8
+ "client_application_id": "app_123",
8
9
  "latest_decision": {
9
10
  "event_id": "evt_23456789abcdefghjkmnpqrstv",
10
11
  "verdict": "human",