Skip to content

fix(bans): keep validated SteamID-of-record on IP-type bans; hide synthetic community id (#1486)#1488

Merged
rumblefrog merged 3 commits into
mainfrom
fix/1486-ip-ban-community-id
May 29, 2026
Merged

fix(bans): keep validated SteamID-of-record on IP-type bans; hide synthetic community id (#1486)#1488
rumblefrog merged 3 commits into
mainfrom
fix/1486-ip-ban-community-id

Conversation

@rumblefrog

@rumblefrog rumblefrog commented May 29, 2026

Copy link
Copy Markdown
Member

Summary

Fixes #1486. The report: ban a player by IP while also filling the Steam ID field, and the ban detail drawer rendered a bogus STEAM_0:0:0 "Community" id.

Two parts, one per commit:

  1. fix(drawer) — hide the synthetic community id. community_id is computed in SQL straight off BA.authid. An empty (or malformed) authid collapsed the arithmetic to the base 76561197960265728 (STEAM_0:0:0). api_bans_detail / api_comms_detail now gate community_id on the same SteamID::isValidID() check already guarding steam_id / steam_id_3, so it surfaces only when there's a real SteamID behind it.

  2. feat(bans) — keep the SteamID instead of dropping it. Root-cause of the user's follow-up ("I don't think we should drop the SteamID if both are filled out — is this a schema limitation?"). It was not a schema limitation: :prefix_bans has always had separate ip + authid columns. The handler was hard-clearing authid for type=1. Now, when the operator fills both fields on an IP-type ban, the SteamID is kept as a record-of-fact alongside the IP.

Why keeping it is safe

Enforcement stays IP-only. The SourceMod plugin matches an IP ban on the ip column alone (sbpp_main.sp: (type=0 AND authid…) OR (type=1 AND ip…)), so the stored authid is inert plugin-side. It exists purely so the ban detail / banlist can show which account the IP belonged to. This also removes a pre-existing asymmetry: the handler already retained a typed IP on a Steam-type ban, but dropped a typed SteamID on an IP-type ban.

Security gate preserved

The #1420 / #1423-follow-up-#4 validate-before-convert contract is intact. A non-empty Steam ID on an IP ban is shape-gated (HANDLER_STRICT_REGEX in the JSON handler, SteamID::isValidID() in the page handler) before toSteam2(), so a garbage value (?type=1&steam=garbage, newline-bypass, etc.) returns a structured validation error on the steam field rather than escaping the converter as a 500 or being silently swallowed. Empty Steam ID field → nothing recorded.

Changes

  • web/api/handlers/bans.phpapi_bans_add keeps a validated SteamID on IP bans; api_bans_detail community-id guard + comment.
  • web/pages/admin.edit.ban.php — same keep-when-valid behavior via the validate-then-convert ladder; rationale comment updated.
  • web/tests/api/BansTest.phptestAddIpTypeAlwaysWritesEmptyAuthidtestAddIpTypeKeepsValidatedSteamOfRecord (kept-on-valid, empty-on-blank, validation-rejected on garbage/newline with no row written); detail test asserts the IP still surfaces.
  • AGENTS.md — reversed the IP-type empty-authid contract + anti-pattern in lockstep (the residual anti-pattern is storing an unvalidated SteamID, not storing one at all).

Test plan

  • CI: PHPUnit (BansTest, CommsTest, SteamIDValidationOrderTest), PHPStan, API-contract, E2E.
  • php -l clean on all modified PHP files.
  • SteamIDValidationOrderTest validate-before-convert order preserved in admin.edit.ban.php (first isValidID precedes first toSteam2).
  • Manual: IP ban + valid Steam ID → row stores both, drawer shows the SteamID, no STEAM_0:0:0.
  • Manual: IP ban + empty Steam ID → empty authid, drawer shows IP only.
  • Manual: IP ban + garbage Steam ID → inline "Please enter a valid Steam ID or Community ID", no 500, no row.

An IP-type ban stores an empty authid (the SteamID field is intentionally
dropped when the operator picks IP type — see testAddIpTypeAlwaysWritesEmptyAuthid).
api_bans_detail computes community_id in SQL straight off authid, so an
empty authid collapsed the arithmetic to the base 76561197960265728
(STEAM_0:0:0) and the player-detail drawer painted a bogus "Community" id.

Gate community_id on the same SteamID::isValidID() check that already
guards steam_id / steam_id_3, returning '' so the drawer hides the row.
Mirror the gate in api_comms_detail for parity and to cover malformed or
empty legacy authids (#900). No wire-format change for valid authids, so
existing snapshots and the api-contract stay valid.
When an operator fills both the Steam ID and IP fields and picks an
IP-type ban, store the SteamID alongside the IP instead of dropping it.
The schema has always had separate ip + authid columns; enforcement
stays IP-only (the SourceMod plugin matches an IP ban on the ip column
alone), so the recorded authid is a record-of-fact surfaced by the ban
detail / banlist.

The #1423 validate-before-convert shape gate is preserved on the
IP-type branch: a non-empty Steam ID is shape-checked before toSteam2()
so garbage returns a structured validation error on the steam field
instead of 500ing (the #1420/#1423 bug class) or being silently
swallowed (the pre-#1486 drop). An empty Steam ID field records nothing.

Covers api_bans_add (JSON) and admin.edit.ban.php (page handler) so the
two surfaces can't diverge. Updates the #1486 drawer guard comment
(now covers no-steam IP bans + legacy malformed authids), rewrites the
contract test (testAddIpTypeAlwaysWritesEmptyAuthid ->
testAddIpTypeKeepsValidatedSteamOfRecord), and reverses the IP-type
empty-authid contract + anti-pattern in AGENTS.md in lockstep.
@rumblefrog rumblefrog changed the title fix(drawer): hide synthetic community id on IP-type ban detail (#1486) fix(bans): keep validated SteamID-of-record on IP-type bans; hide synthetic community id (#1486) May 29, 2026
The handler trims `$rawSteam` before the shape gate, so a trailing
newline (`STEAM_0:0:1\n`) is stripped to a valid `STEAM_0:0:1` and
kept, not rejected. Replace the bogus trailing-newline rejection
assertion with a non-trimmable mid-string newline (the shape the
handler gate actually catches) and add a positive case documenting
the trim-then-keep behavior. Aligns AGENTS.md wording with the
trim nuance.
@rumblefrog
rumblefrog added this pull request to the merge queue May 29, 2026
Merged via the queue into main with commit 7e4875a May 29, 2026
8 checks passed
@rumblefrog
rumblefrog deleted the fix/1486-ip-ban-community-id branch May 29, 2026 03:47
@github-actions github-actions Bot locked and limited conversation to collaborators May 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SteamID invalid when banning IP and SteamID

1 participant