mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
fix(multiplayer): reject typed claim coercion
This commit is contained in:
@@ -335,7 +335,13 @@ func _valid_join_authorisation(token: String) -> bool:
|
||||
var claims = envelope["Authorisation"]
|
||||
if not claims is Dictionary:
|
||||
return false
|
||||
if str(claims.get("PlayerID", "")).is_empty():
|
||||
for string_claim in ["MatchID", "ServerID", "PlayerID", "SteamID", "Protocol", "ExpiresAt"]:
|
||||
if not claims.has(string_claim) or not claims[string_claim] is String or String(claims[string_claim]).is_empty():
|
||||
return false
|
||||
for integer_claim in ["Slot", "Team", "Generation"]:
|
||||
if not claims.has(integer_claim) or not _valid_integer_claim(claims[integer_claim]):
|
||||
return false
|
||||
if not envelope["Signature"] is String or String(envelope["Signature"]).is_empty():
|
||||
return false
|
||||
var claimed_team := int(claims.get("Team", -1))
|
||||
var claimed_slot := int(claims.get("Slot", -1))
|
||||
@@ -374,6 +380,14 @@ func _valid_join_authorisation(token: String) -> bool:
|
||||
and expiry > Time.get_unix_time_from_system()
|
||||
|
||||
|
||||
static func _valid_integer_claim(value: Variant) -> bool:
|
||||
if value is int:
|
||||
return int(value) >= 0
|
||||
if value is float:
|
||||
return is_finite(float(value)) and float(value) >= 0.0 and float(value) == floor(float(value))
|
||||
return false
|
||||
|
||||
|
||||
func _join_identity(token: String) -> String:
|
||||
if token.is_empty():
|
||||
return ""
|
||||
|
||||
@@ -78,6 +78,10 @@ func test_allocated_join_authorisation_is_allowlisted_and_bound_to_server() -> v
|
||||
malformed_claims["ExpiresAt"] = "tomorrow"
|
||||
var malformed_token := Marshalls.raw_to_base64(JSON.stringify({"Authorisation": malformed_claims, "Signature": "trusted-signature"}).to_utf8_buffer())
|
||||
assert_true(not match_net._valid_join_authorisation(malformed_token), "malformed expiry claim is rejected before admission")
|
||||
var string_slot_claims := claims.duplicate()
|
||||
string_slot_claims["Slot"] = "5"
|
||||
var string_slot_token := Marshalls.raw_to_base64(JSON.stringify({"Authorisation": string_slot_claims, "Signature": "trusted-signature"}).to_utf8_buffer())
|
||||
assert_true(not match_net._valid_join_authorisation(string_slot_token), "string slot claim is rejected instead of coerced")
|
||||
assert_true(not match_net._valid_join_authorisation(token + "tampered"), "token mutation is rejected")
|
||||
var wrong_claims := claims.duplicate()
|
||||
wrong_claims["ServerID"] = "other-server"
|
||||
|
||||
@@ -1577,4 +1577,6 @@ Assignment expiry validation now fails closed on malformed non-empty timestamps
|
||||
|
||||
MatchNet join-authorisation admission now applies the same expiry format guard before parsing signed roster claims, closing the malformed-expiry gap at the transport handshake boundary.
|
||||
|
||||
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.
|
||||
|
||||
Presentation progress: a shared `Game/themes/cosmic_clash_theme.tres` now gives the menu, lobby, matchmaking, and settings surfaces consistent button, input, option, and label styling. The custom-font portion of `TODO.md` remains open until a distributable font asset is selected.
|
||||
|
||||
Reference in New Issue
Block a user