Skip to content

Align mobile regex with server regex#4590

Open
xkello wants to merge 5 commits into
masterfrom
bugfix/update-project-name-regex
Open

Align mobile regex with server regex#4590
xkello wants to merge 5 commits into
masterfrom
bugfix/update-project-name-regex

Conversation

@xkello

@xkello xkello commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Aligns the mobile app project name validation with the server validation rules. Previously, the mobile app used a different (blacklist-based) character validation than the server (whitelist-based), which allowed users to create local projects with names containing characters the server would reject - meaning the project could never be pushed/synced because some characters were missing from the mobile regex.

Tickets: #4516, #4365

Current behaviour

The mobile app only blocked a fixed set of blacklisted special characters (@#$%^&*(){}[]\/|+=<>~?:;,`'"etc.). Any character not explicitly listed was allowed, even if the server would reject it later.
This meant a user could successfully create a local project with an invalid name (e.g. containing !), work with it locally, and only discover the mismatch when sync to the server failed.

What changed

  • Removed the special-character blacklist from reForbiddenNames in CoreUtils::isValidName()
  • Added a new whitelist regex, reValidCharacters, allowing only word characters, whitespace, hyphens, and dots (^[\w\s\-\.]+$) — matching the server's has_valid_characters() check
  • isValidName() now requires both checks to pass: the name must not match any forbidden pattern (reserved words, OS device names, leading space/dot) and must match the valid-character whitelist

Expected behaviour

Project names can now only contain characters the server also accepts, preventing the local-creation/server-rejection mismatch
Previously problematic characters like ' or ! are now handled consistently - screenshot attached showing project creation working correctly with these characters

Screenshot_20260710-082727 Screenshot_20260710-082717 Screenshot_20260710-081406

@xkello
xkello requested review from Withalion and Copilot and removed request for Copilot July 10, 2026 06:34
@github-actions

Copy link
Copy Markdown

📦 Build Artifacts Ready

OS Status Build Info Workflow run
macOS Build Build failed or not found. #7011
linux Build Build failed or not found. #7037
win64 Build 📬 Mergin Maps 62091 win64 Expires: 08/10/2026 #6209
Android Build 📬 Mergin Maps 832111 APK [armeabi-v7a] Expires: 08/10/2026 #8321
📬 Mergin Maps 832111 APK [armeabi-v7a] Google Play Store #8321
Android Build 📬 Mergin Maps 832151 APK [arm64-v8a] Expires: 08/10/2026 #8321
📬 Mergin Maps 832151 APK [arm64-v8a] Google Play Store #8321
iOS Build 📬 Build number: 26.07.926311 #9263

@Withalion Withalion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure tests (TestCoreUtils::testNameValidation()) are up to date as well

Please rebase to current master

Comment thread core/coreutils.cpp Outdated
bool CoreUtils::isValidName( const QString &name )
{
static QRegularExpression reForbiddenmNames( R"([@#$%^&*\(\)\{\}\[\]\\\/\|\+=<>~\?:;,`\'\"]|^[\s^\.].*$|^CON$|^PRN$|^AUX$|^NUL$|^COM\d$|^LPT\d|^support$|^helpdesk$|^merginmaps$|^lutraconsulting$|^mergin$|^lutra$|^input$|^sales$|^admin$)", QRegularExpression::CaseInsensitiveOption );
static QRegularExpression reForbiddenmNames( R"(^[\s^\.].*$|^CON$|^PRN$|^AUX$|^NUL$|^COM\d$|^LPT\d|^support$|^helpdesk$|^merginmaps$|^lutraconsulting$|^mergin$|^lutra$|^input$|^sales$|^admin$)", QRegularExpression::CaseInsensitiveOption );

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you split this long regex to multiple by category as server does?

Comment thread core/coreutils.cpp Outdated
return !matchForbiddenNames.hasMatch();
QRegularExpressionMatch matchValidCharacters = reValidCharacters.match( name );

return !matchForbiddenNames.hasMatch() && matchValidCharacters.hasMatch();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check the length of the file name, maximum 255 dictated by pathvalidate lib

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts Ready

OS Status Build Info Workflow run
macOS Build Build failed or not found. #7040
linux Build Build failed or not found. #7066
win64 Build 📬 Mergin Maps 62421 win64 Expires: 18/10/2026 #6242
Android Build 📬 Mergin Maps 835051 APK [arm64-v8a] Expires: 18/10/2026 #8350
📬 Mergin Maps 835051 APK [arm64-v8a] Google Play Store #8350
Android Build 📬 Mergin Maps 835011 APK [armeabi-v7a] Expires: 18/10/2026 #8350
📬 Mergin Maps 835011 APK [armeabi-v7a] Google Play Store #8350
iOS Build Build failed or not found. #9292

@xkello
xkello requested a review from Withalion July 21, 2026 06:06

@Withalion Withalion left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about those tests?

Comment thread core/coreutils.cpp Outdated
Comment on lines +272 to +297
static bool hasValidFirstCharacter( const QString &name )
{
static QRegularExpression re( R"(^[\s^\.].*$)", QRegularExpression::CaseInsensitiveOption );
return !re.match( name ).hasMatch();
}

static bool isInvalidFilename( const QString &name )
{
if ( name.length() > 255 )
return true;

static QRegularExpression re( R"(^CON$|^PRN$|^AUX$|^NUL$|^COM\d$|^LPT\d)", QRegularExpression::CaseInsensitiveOption );
return re.match( name ).hasMatch();
}

static bool isReservedWord( const QString &name )
{
static QRegularExpression re( R"(^support$|^helpdesk$|^merginmaps$|^lutraconsulting$|^mergin$|^lutra$|^input$|^sales$|^admin$)", QRegularExpression::CaseInsensitiveOption );
return re.match( name ).hasMatch();
}

static bool hasValidCharacters( const QString &name )
{
static QRegularExpression re( R"(^[\w\s\-\.]+$)" );
return re.match( name ).hasMatch();
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to create new functions, just bundle all the regexes in isValidName()
also you are missing function declarations of these new functions in header file if they would stay

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally forgot there are tests. On it

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts Ready

OS Status Build Info Workflow run
macOS Build Build failed or not found. #7048
linux Build Build failed or not found. #7074
win64 Build 📬 Mergin Maps 62501 win64 Expires: 19/10/2026 #6250
Android Build 📬 Mergin Maps 835811 APK [armeabi-v7a] Expires: 19/10/2026 #8358
📬 Mergin Maps 835811 APK [armeabi-v7a] Google Play Store #8358
Android Build 📬 Mergin Maps 835851 APK [arm64-v8a] Expires: 19/10/2026 #8358
📬 Mergin Maps 835851 APK [arm64-v8a] Google Play Store #8358
iOS Build 📬 Build number: 26.07.930011 #9300

@github-actions

Copy link
Copy Markdown

Coverage Report for CI Build 29826232857

Warning

No base build found for commit 0f8ddca on master.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 59.066%

Details

  • Patch coverage: No coverable lines changed in this PR.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 15647
Covered Lines: 9242
Line Coverage: 59.07%
Coverage Strength: 97.73 hits per line

💛 - Coveralls

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts Ready

OS Status Build Info Workflow run
macOS Build Build failed or not found. #7053
linux Build 📬 Mergin Maps 70791 x86_64 Expires: 19/10/2026 #7079
win64 Build 📬 Mergin Maps 62551 win64 Expires: 19/10/2026 #6255
Android Build 📬 Mergin Maps 836311 APK [armeabi-v7a] Expires: 19/10/2026 #8363
📬 Mergin Maps 836311 APK [armeabi-v7a] Google Play Store #8363
Android Build 📬 Mergin Maps 836351 APK [arm64-v8a] Expires: 19/10/2026 #8363
📬 Mergin Maps 836351 APK [arm64-v8a] Google Play Store #8363
iOS Build 📬 Build number: 26.07.930511 #9305

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts Ready

OS Status Build Info Workflow run
macOS Build 📬 Mergin Maps 70532 dmg Expires: 19/10/2026 #7053
linux Build 📬 Mergin Maps 70791 x86_64 Expires: 19/10/2026 #7079
win64 Build 📬 Mergin Maps 62551 win64 Expires: 19/10/2026 #6255
Android Build 📬 Mergin Maps 836311 APK [armeabi-v7a] Expires: 19/10/2026 #8363
📬 Mergin Maps 836311 APK [armeabi-v7a] Google Play Store #8363
Android Build 📬 Mergin Maps 836351 APK [arm64-v8a] Expires: 19/10/2026 #8363
📬 Mergin Maps 836351 APK [arm64-v8a] Google Play Store #8363
iOS Build 📬 Build number: 26.07.930511 #9305

@xkello
xkello requested a review from Withalion July 21, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unify project name rules with server Invalid character (exclamation mark) in Project title

2 participants