extends "res://tests/test_case.gd" const MatchNet = preload("res://scripts/match_net.gd") # Adversarial-review regression: _hello's player_name used to be broadcast # to every peer completely unvalidated — a multi-MB name head-of-line- # blocked the reliable control channel hard enough that a concurrently- # joining client's own _welcome never arrived. _sanitize_player_name() is # the fix; these are pure-function tests for it, independent of the live # two-process rejection test in tests/match_net_smoke.gd (--role=client-longname). func test_normal_name_unchanged() -> void: assert_eq(MatchNet._sanitize_player_name("Alice"), "Alice", "a normal name passes through unchanged") func test_strips_control_characters() -> void: var bell := String.chr(7) # a control char with no named GDScript escape var raw := "Bad\nName\twith\rcontrol" + bell + "chars" var clean := MatchNet._sanitize_player_name(raw) assert_true(not clean.contains("\n"), "no newline") assert_true(not clean.contains("\t"), "no tab") assert_true(not clean.contains("\r"), "no carriage return") assert_true(not clean.contains(bell), "no bell/control char") func test_clamps_to_max_display_length() -> void: var raw := "X".repeat(1000) var clean := MatchNet._sanitize_player_name(raw) assert_eq(clean.length(), MatchNet.MAX_PLAYER_NAME_LENGTH, "clamped to MAX_PLAYER_NAME_LENGTH") func test_empty_or_whitespace_only_falls_back_to_default() -> void: assert_eq(MatchNet._sanitize_player_name(""), "Player", "empty string falls back") assert_eq(MatchNet._sanitize_player_name(" "), "Player", "whitespace-only falls back") assert_eq(MatchNet._sanitize_player_name("\n\t\r"), "Player", "control-characters-only falls back") func test_leading_trailing_whitespace_trimmed() -> void: assert_eq(MatchNet._sanitize_player_name(" Bob "), "Bob", "surrounding whitespace trimmed") func test_reservation_reclaim_requires_stable_identity() -> void: assert_true(MatchNet.reservation_identity_matches("player-a", "player-a", "Alice", "Impostor"), "the verified identity can reclaim despite a changed display name") assert_true(not MatchNet.reservation_identity_matches("player-a", "player-b", "Alice", "Alice"), "a same-name peer cannot reclaim another identity's slot") assert_true(not MatchNet.reservation_identity_matches("player-a", "", "Alice", "Alice"), "an unauthenticated peer cannot reclaim an allocated slot") assert_true(MatchNet.reservation_identity_matches("", "", "Alice", "Alice"), "direct servers retain the legacy display-name fallback") func test_allocated_join_authorisation_is_allowlisted_and_bound_to_server() -> void: var claims := { "MatchID": "match-1", "ServerID": "server-1", "PlayerID": "player-1", "SteamID": "steam-1", "Slot": 2, "Team": 1, "Protocol": "1", "Generation": 1, "ExpiresAt": "2099-08-31T12:00:00Z", } var token := Marshalls.raw_to_base64(JSON.stringify({"Authorisation": claims, "Signature": "trusted-signature"}).to_utf8_buffer()) var match_net := MatchNet.new() assert_true(match_net.configure_join_authorisations([token], {"match_id": "match-1", "server_id": "server-1", "protocol": "1", "protocol_version": 1}), "valid roster configures") assert_true(match_net._valid_join_authorisation(token), "allowlisted matching token is accepted") assert_true(not match_net._valid_join_authorisation(token + "tampered"), "token mutation is rejected") var wrong_claims := claims.duplicate() wrong_claims["ServerID"] = "other-server" var wrong_token := Marshalls.raw_to_base64(JSON.stringify({"Authorisation": wrong_claims, "Signature": "trusted-signature"}).to_utf8_buffer()) assert_true(not match_net._valid_join_authorisation(wrong_token), "wrong server claim is rejected") assert_eq(match_net._reserve_join_authorisation(token, 42), 1, "first admission receives generation one") assert_true(match_net.is_join_authorisation_active(token), "admitted token is active") assert_eq(match_net._reserve_join_authorisation(token, 43), -1, "active token cannot be admitted concurrently") match_net._remove_player(42) assert_true(not match_net.is_join_authorisation_active(token), "disconnect releases active token") assert_eq(match_net._reserve_join_authorisation(token, 43), 2, "reclaim receives the next server-owned generation") match_net._remove_player(43) match_net._join_history[token]["lost_at"] = Time.get_unix_time_from_system() - MatchNet.RECONNECT_GRACE_SECONDS - 1.0 assert_eq(match_net._reserve_join_authorisation(token, 44), -1, "reclaim after the grace window is fenced") func test_allocated_join_authorisation_verifies_canonical_hmac() -> void: # This envelope is generated from server/domain.JoinAuthorisationBytes with # HMAC-SHA256(test-key), proving the Godot verifier agrees with the Go # canonical representation rather than merely checking token membership. var token := "eyJBdXRob3Jpc2F0aW9uIjp7Ik1hdGNoSUQiOiJtYXRjaC0xIiwiU2VydmVySUQiOiJzZXJ2ZXItMSIsIlBsYXllcklEIjoicGxheWVyLTEiLCJTdGVhbUlEIjoic3RlYW0tMSIsIlNsb3QiOjIsIlRlYW0iOjEsIlByb3RvY29sIjoiMSIsIkdlbmVyYXRpb24iOjEsIkV4cGlyZXNBdCI6IjIwOTktMDgtMzFUMTI6MDA6MDBaIn0sIlNpZ25hdHVyZSI6IkQ0VmVEejJheVh3Y1J3bFZUc3JkUW1YS3FYYzRmVG05RnByTjRYK3ZzM1k9In0=" var match_net := MatchNet.new() assert_true(match_net.configure_join_authorisations([token], {"match_id": "match-1", "server_id": "server-1", "protocol": "1", "protocol_version": 1}, "test-key".to_utf8_buffer()), "HMAC roster configures") assert_true(match_net._valid_join_authorisation(token), "Go-compatible canonical HMAC is accepted") var tampered_payload: Dictionary = JSON.parse_string(Marshalls.base64_to_raw(token).get_string_from_utf8()) tampered_payload["Signature"] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" var tampered_token := Marshalls.raw_to_base64(JSON.stringify(tampered_payload).to_utf8_buffer()) var tampered_match_net := MatchNet.new() assert_true(tampered_match_net.configure_join_authorisations([tampered_token], {"match_id": "match-1", "server_id": "server-1", "protocol": "1", "protocol_version": 1}, "test-key".to_utf8_buffer()), "tampered roster fixture configures") assert_true(not tampered_match_net._valid_join_authorisation(tampered_token), "allowlisted but forged signature is rejected")