mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-15 22:42:03 +00:00
fix(multiplayer): fail closed on ticket timestamps
This commit is contained in:
@@ -318,10 +318,15 @@ static func should_recover_queue_after_conflict(operation: String, response_code
|
|||||||
|
|
||||||
static func normalize_ticket(payload: Dictionary) -> Dictionary:
|
static func normalize_ticket(payload: Dictionary) -> Dictionary:
|
||||||
var result := payload.duplicate(true)
|
var result := payload.duplicate(true)
|
||||||
if result.has("enqueued_at") and result["enqueued_at"] is String:
|
for pair in [["enqueued_at", "enqueued_at_unix"], ["expires_at", "expires_at_unix"]]:
|
||||||
result["enqueued_at_unix"] = Time.get_unix_time_from_datetime_string(String(result["enqueued_at"]))
|
var source_key: String = pair[0]
|
||||||
if result.has("expires_at") and result["expires_at"] is String:
|
var target_key: String = pair[1]
|
||||||
result["expires_at_unix"] = Time.get_unix_time_from_datetime_string(String(result["expires_at"]))
|
if not result.has(source_key):
|
||||||
|
continue
|
||||||
|
if not result[source_key] is String or not is_valid_rfc3339_timestamp(String(result[source_key])):
|
||||||
|
result[target_key] = -1
|
||||||
|
else:
|
||||||
|
result[target_key] = Time.get_unix_time_from_datetime_string(String(result[source_key]))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ func test_ticket_normalization_preserves_payload_and_derives_expiry() -> void:
|
|||||||
func test_ticket_normalization_derives_authoritative_enqueue_time() -> void:
|
func test_ticket_normalization_derives_authoritative_enqueue_time() -> void:
|
||||||
var normalized := ControlPlaneClient.normalize_ticket({"enqueued_at": "1970-01-01T00:16:40Z"})
|
var normalized := ControlPlaneClient.normalize_ticket({"enqueued_at": "1970-01-01T00:16:40Z"})
|
||||||
assert_eq(int(normalized["enqueued_at_unix"]), 1000, "RFC3339 enqueue time is converted to epoch")
|
assert_eq(int(normalized["enqueued_at_unix"]), 1000, "RFC3339 enqueue time is converted to epoch")
|
||||||
|
assert_eq(ControlPlaneClient.normalize_ticket({"enqueued_at": "not-a-timestamp"})["enqueued_at_unix"], -1, "malformed enqueue time remains visibly invalid")
|
||||||
|
assert_eq(ControlPlaneClient.normalize_ticket({"expires_at": 123})["expires_at_unix"], -1, "non-string expiry remains visibly invalid")
|
||||||
|
|
||||||
|
|
||||||
func test_session_expiry_is_checked_at_the_boundary_and_fails_closed() -> void:
|
func test_session_expiry_is_checked_at_the_boundary_and_fails_closed() -> void:
|
||||||
|
|||||||
@@ -1583,6 +1583,8 @@ Ranked profile `season_id` now enforces the OpenAPI opaque-ID shape and exact st
|
|||||||
|
|
||||||
Persisted matchmaking snapshots now validate field types, non-negative integral revisions/epochs, and proposal identity/state consistency before restoration; malformed restart data cannot be coerced into an active projection.
|
Persisted matchmaking snapshots now validate field types, non-negative integral revisions/epochs, and proposal identity/state consistency before restoration; malformed restart data cannot be coerced into an active projection.
|
||||||
|
|
||||||
|
Ticket timestamp normalization now preserves an invalid sentinel for malformed or non-string raw timestamps, allowing the projection to reject bad server metadata instead of silently converting it to epoch zero.
|
||||||
|
|
||||||
Ranked profile projection now rejects fractional `ranked_games` values instead of silently truncating them, matching the OpenAPI integer contract.
|
Ranked profile projection now rejects fractional `ranked_games` values instead of silently truncating them, matching the OpenAPI integer contract.
|
||||||
|
|
||||||
Signed MatchNet claims now also require exact JSON string/integer types for every identity, protocol, expiry, slot, team, and generation field; string-number coercion is rejected before canonical signature verification.
|
Signed MatchNet claims now also require exact JSON string/integer types for every identity, protocol, expiry, slot, team, and generation field; string-number coercion is rejected before canonical signature verification.
|
||||||
|
|||||||
Reference in New Issue
Block a user