Skip to content

Iban accepts an invalid IBAN ending in "\n" and rejects a valid one with the same newline (regex $ matches before a trailing newline) #311

Closed

Description

@matt-edmondson

What's wrong

IsIbanAttribute's validator checks ^[A-Z]{2}[0-9]{2}[A-Z0-9]+$ (Semantics.Strings.Identifiers/IsIbanAttribute.cs:24). In .NET, $ without RegexOptions.Multiline also matches just before a final \n, so a value with a trailing newline passes the structural check.

Iban.MakeCanonical (Semantics.Strings.Identifiers/Iban.cs:21) strips only ' ', so the newline reaches the validator. The length gate (15–34) allows the extra character. PassesMod97 then feeds '\n' through ApplyChar as if it were a letter: c - 'A' + 10 gives −45. Whether the value passes then depends on the check digits, not on whether the IBAN is valid.

Reproduction (run against the built assembly)

Iban.Create<Iban>("GB67WEST12345698765432\n") -> ACCEPTED, stored as "GB67WEST12345698765432\n" (length 23)
Iban.Create<Iban>("GB67WEST12345698765432")   -> ArgumentException  (GB67 is not a valid IBAN)
Iban.Create<Iban>("GB82WEST12345698765432")   -> ACCEPTED           (the valid reference IBAN)
Iban.Create<Iban>("GB82WEST12345698765432\n") -> ArgumentException  (valid IBAN rejected)

Why it matters

  • A value with wrong check digits validates, and its stored canonical form contains a control character.
  • A valid IBAN pasted with a trailing newline, which is common from text areas and files, is rejected with a misleading "mod-97 checksum failed".
  • The identifiers spec (docs/superpowers/specs/2026-07-01-concrete-semantic-strings-design.md) says canonicalization strips whitespace, but only spaces are stripped.

The same $ anchor appears in IsUuidAttribute.cs:23 and IsUlidAttribute.cs:23. Uuid and Ulid are protected today only because their MakeCanonical calls Trim(). The attributes are public, so a consumer type that uses them without trimming would inherit the hole.

Suggested fix / acceptance criteria

  • Anchor the identifier patterns with \z (or \A…\z) instead of $.
  • Make Iban.MakeCanonical strip all whitespace (char.IsWhiteSpace), as the spec says, so that "GB82 WEST 1234 5698 7654 32\n" canonicalizes to GB82WEST12345698765432 and validates.
  • Tests:
    • an invalid IBAN with a trailing \n is rejected;
    • a valid IBAN with a trailing \n or \t canonicalizes and validates;
    • [IsUuid] / [IsUlid] on a type that doesn't trim reject a trailing \n.

Activity

  1. matt-edmondson commented on Sep 28, 2026

    @matt-edmondson
    ContributorAuthor

    Triage

    • Category: Bug
    • Priority: Medium. A validated semantic string accepts an IBAN whose check digits are wrong, and stores a control character in its canonical form. A valid IBAN pasted with a trailing newline is rejected with a misleading checksum error. The $ anchor is also in the public [IsUuid] and [IsUlid] attributes.
    • Area / suggested assignment: Semantics.Strings.Identifiers (IsIbanAttribute, Iban.MakeCanonical, IsUuidAttribute, IsUlidAttribute). @matt-edmondson
    • Duplicates: None found.
    • In progress: No matching open PR. Bump MSTest.Sdk from 4.4.0 to 4.4.1 #255 and Bump ktsu.CodeBlocker and 14 others #294 are dependency bumps.

    Notes: It's worth grepping the whole repository for $" and $' regex anchors in validation attributes while making this fix, so the \z change is applied consistently rather than to these three files only.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions