fix: anchor Label title validation with \z instead of \Z (#15443)

## Description

This isn't tied to an open issue — I found it by extrapolating from the
bug class fixed in #15415 ("fix: anchor contact phone number
validation"), which fixed a `Contact#phone_number` format validation
that was missing a leading `\A` anchor. That made me audit every
hand-written regex-based validation in the codebase for the same class
of anchoring mistake (`format: { with: ... }` validators, plus
`match?`/`=~` calls used for validation-style checks, across `app/`,
`enterprise/`, and `lib/`).

Everything else was already correctly anchored. One real instance of the
*sibling* mistake remains:

`RegexHelper::UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE` (used only by
`Label#title`'s format validation) is
`/\A[\p{L}\p{N}]+[\p{L}\p{N}_-]+\Z/` — note `\Z` (capital), not `\z`.
Unlike `\z`, `\Z` also matches just before a single trailing `"\n"` at
the end of the string. The surrounding comment documents the intended
character set (unicode letters/numbers/underscore/hyphen, not starting
with `_`/`-`) and says nothing about tolerating a trailing newline, so
this reads as an unintentional choice of anchor rather than a deliberate
one.

Concretely: `Label.new(title: "hello_world\n").valid?` returns `true` on
current `develop` and persists a title with a literal trailing newline,
because `\Z` lets the `\n` slip through. `Label` only lowercases the
title before validating (no `strip`), so nothing else catches this.

This is a narrower/lower-severity variant of the #15415 bug (it only
ever admits one specific trailing character, not an arbitrary
prefix/suffix), but it's the same underlying mistake, independently
verified against current source, not just pattern-matched from the diff.

## What changed

- `lib/regex_helper.rb`: `UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE`
now ends in `\z` instead of `\Z`, with a comment explaining why.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## How Has This Been Tested?

Ran against a real local Rails env (Ruby 3.4.4, PostgreSQL 16, Redis):

`bundle exec rspec spec/models/label_spec.rb`

Added a regression test asserting `Label.new(title: "hello_world\n")` is
invalid. Confirmed it fails against the pre-fix `\Z` regex and passes
after switching to `\z`. All existing `label_spec.rb` examples
(including the existing "foreign characters", "special characters", and
"uppercase" title-validation cases) continue to pass unchanged.

## Checklist

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective
- [x] New and existing unit tests pass locally with my changes

🤖 This fix was authored by an AI coding agent (Claude) working on behalf
of Mithtech, an ERPNext/Frappe/Medusa.js implementation studio, as part
of a deliberate effort to build a track record of verified upstream
open-source contributions. Flagging this transparently per common
courtesy — happy to answer any questions about the change, including how
it was found (auditing for the same regex-anchor mistake class as
#15415).

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Manoj
2026-08-13 12:05:06 +05:30
committed by GitHub
parent 2ed68cf207
commit 1d905fb019
2 changed files with 10 additions and 1 deletions

View File

@@ -4,7 +4,9 @@ module RegexHelper
# the following regext needs atleast one character which should be
# valid unicode letter, unicode number, underscore, hyphen
# shouldn't start with a underscore or hyphen
UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE = Regexp.new('\A[\p{L}\p{N}]+[\p{L}\p{N}_-]+\Z')
# \z (not \Z) anchors strictly to the end of the string -- \Z would also accept a single
# trailing "\n", which would let a title like "hello_world\n" pass this validation.
UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE = Regexp.new('\A[\p{L}\p{N}]+[\p{L}\p{N}_-]+\z')
# Regex to match mention markdown links and extract display names
# Matches: [@display name](mention://user|team/id/url_encoded_name)
# Captures: 1) @display name (including emojis), 2) url_encoded_name

View File

@@ -26,6 +26,13 @@ RSpec.describe Label do
expect(label.valid?).to be true
end
it 'would not let you use a title with a trailing newline' do
# Regression test: the format validator used to anchor on \Z instead of \z, and \Z tolerates
# a single trailing newline, so 'hello_world' + "\n" incorrectly passed validation.
label = FactoryBot.build(:label, title: "hello_world\n")
expect(label.valid?).to be false
end
it 'converts uppercase letters to lowercase' do
label = FactoryBot.build(:label, title: 'Hello_World')
expect(label.valid?).to be true