CVE-2026-81638: Non-canonical ULID spellings are accepted and alias to the same record in ash_double_entry
Improper Handling of Alternate Encoding vulnerability in ash-project ashdoubleentry allows an attacker to submit several distinct string spellings of the same identifier.
AshDoubleEntry.ULID renders a 128-bit ULID as 26 Crockford base-32 characters, but the first character encodes only 3 bits, so canonical values are 0 to 7. decode/1 in lib/ulid.ex masks the first character to its low 3 bits and valid?/1 accepts all 32 characters in that position, so 0..., 8..., G... and R... decode to the identical 16-byte value and resolve to the same row. When the type is exposed as a public ID over an HTTP or API boundary, an attacker-supplied ID can be spelled differently from the record it actually reads or writes, desynchronizing or bypassing string-level checks such as idempotency and deduplication keys, deny-lists, audit correlation, or signatures computed over the submitted ID.
This issue affects ashdoubleentry: from 0.1.0 before 1.0.19.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
ash-project ash_double_entryto a version that resolves this vulnerability.Fixed in 1.0.19 - Configuration
Ensure that ULID parsing in lib/ulid.ex enforces canonical form: for the first character, accept only those that encode low 3 bits (canonical first characters corresponding to values 0–7) and reject all other first-character spellings, so non-canonical inputs cannot alias the same record.
ash-project ash_double_entry ULID string validation/canonicalization (decode/1 & valid?/1 behavior) = Reject non-canonical ULID spellings where first Crockford base-32 character encodes only 3 bits (allow only 0 to 7)
Event History
Frequently Asked Questions
Which deployments are exposed to this issue?
Deployments using ash_double_entry versions from 0.1.0 before 1.0.19 are affected when AshDoubleEntry.ULID values are exposed as public identifiers through an HTTP or API boundary. The practical impact depends on whether the application makes security or consistency decisions using the submitted ID string rather than its decoded value.
What does an attacker need to exploit this behavior?
An attacker needs to be able to submit a ULID-based identifier to an affected application interface. They can use a non-canonical first character such as 8, G, or R in place of the canonical 0 through 7 character that maps to the same underlying record.
What application controls are most at risk?
String-level controls tied to the supplied identifier can be bypassed or desynchronized, including idempotency and deduplication keys, deny-lists, audit-event correlation, and signatures calculated over the submitted ID. Record lookup or write operations may still resolve to the same row despite receiving a different ID spelling.
How can teams identify potentially affected requests or data?
Review externally supplied ULID strings whose first character is outside the canonical 0 through 7 range. In affected versions, strings beginning with values such as 8, G, or R can decode to the same 16-byte value as another ULID and may correspond to the same record.