Skip to content

Fix client_secret param validator - #20104

Merged
sandhose merged 7 commits into
element-hq:developfrom
barodeur:fix-client-secret-pattern
Sep 7, 2026
Merged

sandhose merged 7 commits into
element-hq:developfrom
barodeur:fix-client-secret-pattern

Conversation

@barodeur

@barodeur barodeur commented Aug 14, 2026 •

Copy link
Copy Markdown
Collaborator

The validation of client_secret params accepts invalid values: any value that includes a char in [0-9a-zA-Z.=_-] is accepted, for example "café" is accepted.

Instead synapse should only accept if all chars are within [0-9a-zA-Z.=_-], not just one.

This is the current validator:

ClientSecretStr = Annotated[
    str,
    StringConstraints(
        pattern="[0-9a-zA-Z.=_-]",
        min_length=1,
        max_length=255,
        strict=True,
    ),
]

Unfortunately, Pydantic only defines the pattern argument as:

pattern

A regex pattern that the string must match.

Which is extremely imprecise.

Here is a little script to verify the behavior:

from typing import Annotated
from pydantic import BaseModel, StringConstraints, ValidationError


def check(pattern: str) -> None:
    ClientSecretStr = Annotated[
        str,
        StringConstraints(pattern=pattern, min_length=1, max_length=255, strict=True),
    ]

    class Body(BaseModel):
        client_secret: ClientSecretStr

    try:
        Body.model_validate({"client_secret": "café"})
        print(f"pattern = {pattern!r}: 'café' ACCEPTED  <-- should have been rejected")
    except ValidationError:
        print(f"pattern = {pattern!r}: 'café' rejected")


check("[0-9a-zA-Z.=_-]")  # before the fix (unanchored)
check("^[0-9a-zA-Z.=_-]+$")  # after the fix

which would output

pattern = '[0-9a-zA-Z.=_-]': 'café' ACCEPTED  <-- should have been rejected
pattern = '^[0-9a-zA-Z.=_-]+$': 'café' rejected

History

This is a regression of a previously reported and fixed bug:


Pull Request Checklist

  • Pull request is based on the develop branch
  • Pull request includes a changelog file. The entry should:
    • Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from EventStore to EventWorkerStore.".
    • Use markdown where necessary, mostly for code blocks.
    • End with either a period (.) or an exclamation mark (!).
    • Start with a capital letter.
    • Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
  • Code style is correct (run the linters)

The Matrix spec requires `client_secret` request parameters to consist
entirely of the characters `[0-9a-zA-Z.=_-]` (non-empty, at most 255
characters), as enforced by `assert_valid_client_secret` /
`CLIENT_SECRET_REGEX = r"^[0-9a-zA-Z\.=_\-]+$"` in
synapse/util/stringutils.py. That validation was originally added for
matrix-org/synapse#6766.

The Pydantic `ClientSecretStr` type in synapse/types/rest/client/__init__.py
lost the regex anchors in the Pydantic port (element-hq#13188) and was further
weakened in the Pydantic v2 migration (element-hq#19071): its pattern is now the
unanchored, unquantified character class `[0-9a-zA-Z.=_-]`. Pydantic
applies patterns with *search* semantics, so any string containing at
least one allowed character passes — e.g. "foo bar", "secret!",
"café" and "a'; DROP TABLE users;--" are all accepted. This is a
regression of the fix for matrix-org/synapse#6766.

This commit adds tests only:
- test_rejects_client_secrets_with_invalid_characters FAILS at this
  commit, demonstrating the regression.
- The empty-string and over-255-character rejection tests already pass,
  since min_length/max_length constraints were unaffected.
…ment

Anchor and quantify the pattern (`^[0-9a-zA-Z.=_-]+$`) so that
`client_secret` values must consist entirely of the spec-mandated
character set, matching `assert_valid_client_secret`. Pydantic applies
patterns with search semantics, so the previous unanchored single
character class accepted any string containing at least one valid
character. Regression of the fix for matrix-org/synapse#6766.
@barodeur
barodeur marked this pull request as ready for review August 14, 2026 13:24
@barodeur
barodeur requested a review from a team as a code owner August 14, 2026 13:24
Comment thread tests/rest/client/test_models.py Resolved
Update test example with a nice reference to https://xkcd.com/327/

Co-authored-by: Quentin Gliech <quenting@element.io>
@MadLittleMods MadLittleMods added the A-Spec-Compliance Matrix spec compliance label Aug 14, 2026
@sandhose
sandhose merged commit e35c8f0 into element-hq:develop Sep 7, 2026
76 of 78 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Spec-Compliance Matrix spec compliance

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants