# Identity normalization collision across accounts

Lossy username/ID normalization or type coercion mapping distinct callers to one identity, scope, or record

_Area: Web access control and IDOR. Seen in 2 disclosed reports; latest 2026-07-20. Updated 2026-11-02._

## How it works
A security boundary (home directory, tenant scope, credential store, file attachment) is derived from a user-controlled or user-identifying string through a lossy transformation. The transformation is many-to-one: distinct inputs collapse to one output. If nothing checks that the derived key is unique per account, two callers resolve to the same identity or record, and one can read or overwrite the other's data. The broken assumption is that "sanitized/normalized/coerced" implies "distinct." It does not; only an injective mapping preserves distinctness.

## Where it shows up
- Username/email sanitizers that strip or replace characters (`..`, non-alphanumerics mapped to `-`) before building a path, scope, or key [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md)
- Per-user directories, buckets, or scopes built from a cleaned string with no uniqueness check at signup [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md)
- UUID primary keys attached to a relation whose foreign column is a numeric type (bigint), coercing via `to_i` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md)
- Session credentials persisted keyed by user, written while rendering another user's context [E1](https://bugbountylibrary.com/r/h1%3A1061591.md)
- Any code path where an admin/background/impersonation context writes data attributed to a different user [E1](https://bugbountylibrary.com/r/h1%3A1061591.md)

## How to review
Find every place a boundary key is derived from a user string or ID. Ask: is the transform injective, and is uniqueness enforced? The invariant: **two distinct accounts must never produce the same derived key, and a write must be attributed to the session user who owns the secret.**
- For sanitizers: construct two raw inputs that differ but clean to the same output; if both are accepted, it is unsafe [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md).
- For typed columns: confirm the column type matches the key type (UUID column for UUID keys); a bigint `record_id` truncates UUIDs [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md).
- For credential persistence: check that the writer verifies the session user equals the credential owner before storing [E1](https://bugbountylibrary.com/r/h1%3A1061591.md).

## How finders spotted it
- Users reported broken SMB mounts; inspecting `oc_storages_credentials` showed entries for all users after admin visited `/settings/users`, traced to session credential persistence without an ownership check [E1](https://bugbountylibrary.com/r/h1%3A1061591.md).
- Code reading of `settings/dir.go`: `MakeUserDir` built scope from `cleanUsername()`, the rewrite was lossy, and signup had no scope-uniqueness check [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md).
- A flaky spec failed intermittently; investigating revealed UUIDs converting to `0` via `.to_i`, then the attachments schema showed `record_id` is bigint [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md).

## Variants seen
- Lossy character sanitizer collapsing usernames to one home directory [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md)
- Type coercion truncating UUIDs to numeric prefixes, colliding attachment records [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md)
- Credential store keyed by user but written from another user's session context [E1](https://bugbountylibrary.com/r/h1%3A1061591.md)

## Impact and severity
Low-privilege users gained another user's data or higher privilege: admin-level SMB access and internal network reach [E1](https://bugbountylibrary.com/r/h1%3A1061591.md); read/overwrite of a victim's files [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md); download of another user's private export [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md). Impact rises with what the collided resource grants — cross-tenant reach, admin credentials, or private data exports.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A1061591.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-7rc3-g7h6-22m7.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-3cx6-j9j4-54mp.md
