fix(perms): never write a never-seen player into Carbon's shared blank record #47

Merged
whitlocktech merged 1 commits from fix/carbon-blank-user into main 2026-10-30 04:31:56 -05:00
Member

The bug (W3 from the account deletion walk, docs#397). In Carbon 2.0.262, GetUserData(id) for a Steam id Carbon has no record for returns one shared, static blank record, and GrantUserPermission and AddUserGroup write into whatever it returns (Carbon Permissions.cs, _blankUser). So when the site granted something to a player who had never joined a Carbon server:

  • The grant or group went to that shared record, and every Steam id Carbon had no record for then held it until the server restarted.
  • UserHasGroup reads the same blank record, so the membership read back as a success. The sync reported it as applied, and later syncs saw nothing to fix.
  • The grant read back notLanded, because GetPermissionUsers lists only real records, but it had still leaked.

Oxide creates a real record in GetUserData, so this is Carbon-only.

The fix. On Carbon, the sync checks permission.UserExists before either write (HasNoCarbonRecord). A never-seen player's grant is reported notLanded and their membership pending, and the store is not touched. Both land at the first audit sync after the player joins, which is what PLAN.md §29.8 and §12.2 rule 4 already describe. Oxide is unchanged, and the source is still the same file on both frameworks (it uses the existing IsCarbon(), not #if CARBON). The org lead chose "hold until they join" over creating the record. The change is 36 added lines in RunicGateway.cs, with no protocol or message-shape change.

Proven on the Carbon rig (Rust 26139947, Carbon 2.0.262), with a throwaway probe plugin that asks the store about random Steam ids it has never seen:

Result
Before (released 0.3.0, after one admin grant during the walk) 3 random never-seen ids each held autodoors.use
After this branch: a group plus a grant to a known id (…042) and a never-seen id (…043) …042 is in the group and holds the grant; the report says notLanded: [043:autodoors.use] and pending: [043:w3vip]
After this branch: random never-seen ids none holds the permission or the group

The rig was put back on the released 0.3.0 afterwards, and the probe and test data were removed. The docs correction to PLAN.md §29.8 is in a separate docs PR.

  • AI-assisted: written with Claude Code (Claude Opus 5.5)

🤖 Generated with Claude Code

https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY

**The bug (W3 from the account deletion walk, docs#397).** In Carbon 2.0.262, `GetUserData(id)` for a Steam id Carbon has no record for returns **one shared, static blank record**, and `GrantUserPermission` and `AddUserGroup` write into whatever it returns ([Carbon `Permissions.cs`](https://github.com/CarbonCommunity/Carbon/blob/production_build/src/Carbon.Components/Carbon.Common/src/Oxide/Libraries/Permissions.cs), `_blankUser`). So when the site granted something to a player who had never joined a Carbon server: - The grant or group went to that shared record, and **every Steam id Carbon had no record for then held it** until the server restarted. - `UserHasGroup` reads the same blank record, so the membership read back as a success. The sync reported it as applied, and later syncs saw nothing to fix. - The grant read back `notLanded`, because `GetPermissionUsers` lists only real records, but it had still leaked. Oxide creates a real record in `GetUserData`, so this is Carbon-only. **The fix.** On Carbon, the sync checks `permission.UserExists` before either write (`HasNoCarbonRecord`). A never-seen player's grant is reported `notLanded` and their membership `pending`, and the store is not touched. Both land at the first audit sync after the player joins, which is what PLAN.md §29.8 and §12.2 rule 4 already describe. Oxide is unchanged, and the source is still the same file on both frameworks (it uses the existing `IsCarbon()`, not `#if CARBON`). The org lead chose "hold until they join" over creating the record. The change is 36 added lines in `RunicGateway.cs`, with no protocol or message-shape change. **Proven on the Carbon rig** (Rust 26139947, Carbon 2.0.262), with a throwaway probe plugin that asks the store about random Steam ids it has never seen: | | Result | |---|---| | Before (released 0.3.0, after one admin grant during the walk) | 3 random never-seen ids each held `autodoors.use` | | After this branch: a group plus a grant to a known id (…042) and a never-seen id (…043) | …042 is in the group and holds the grant; the report says `notLanded: [043:autodoors.use]` and `pending: [043:w3vip]` | | After this branch: random never-seen ids | none holds the permission or the group | The rig was put back on the released 0.3.0 afterwards, and the probe and test data were removed. The docs correction to PLAN.md §29.8 is in a separate docs PR. - [x] AI-assisted: written with Claude Code (Claude Opus 5.5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
wtclaude added 1 commit 2026-10-30 04:23:50 -05:00
fix(perms): never write a never-seen player into Carbon's shared blank record
Some checks failed
PR Checks / plugin-checks (pull_request) Failing after -14s
59db88d006
Carbon 2.0.262 answers GetUserData for a Steam id it has no record for with
one shared, static blank record, and GrantUserPermission and AddUserGroup
write into it. A grant or membership for one never-seen player was then held
by every Steam id Carbon had no record for, until the server restarted, and
UserHasGroup read the membership back as a success, so the sync reported it
applied and later syncs saw nothing to fix.

On Carbon the sync now checks permission.UserExists before either write. A
never-seen player's grant is reported notLanded and their membership pending,
without touching the store; both land at the first audit sync after they
join, as PLAN.md section 29.8 and section 12.2 rule 4 already describe. Oxide
creates a real record and is unchanged.

Proven on the Carbon rig (Rust 26139947, Carbon 2.0.262): before, three
random never-seen ids each held autodoors.use after one admin grant; after,
a known id got its grant and membership, the never-seen id was reported
notLanded + pending, and random ids held nothing. Found in the account
deletion walk (docs/website/ACCOUNT_DELETION.md step 6, W3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
whitlocktech merged commit 7483e131ef into main 2026-10-30 04:31:56 -05:00
whitlocktech deleted branch fix/carbon-blank-user 2026-10-30 04:31:58 -05:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RunicGateway/Rust-Plugins#47
No description provided.