fix(gitlab): reject bare project slugs in config validation (#8701)

The #8667 regex only rejected pasted display names (values with spaces),
so a bare single-segment slug like `test_config` still passed and produced
a confusing 404 at poll time (#8665). A GitLab project reference must be a
numeric ID or a namespace-qualified path (`group/project`, subgroups, or
the `%2F`-encoded form) — the REST API cannot resolve a bare slug (verified
live: `/projects/gitlab/issues` → 404, `/projects/gitlab-org%2Fgitlab/issues`
→ 200). Require a path separator for the non-numeric branch so the mistake
gets inline feedback (Save/Test are gated on form validity) instead. The
separator lookahead keeps the char class a single unnested quantifier.
This commit is contained in:
Johannes Millan 2026-07-02 12:53:12 +02:00 committed by GitHub
parent 222fa4c8aa
commit 6c1117b7b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 19 additions and 13 deletions

View file

@ -29,9 +29,9 @@ describe('GITLAB_PROJECT_REGEX', () => {
'a.b-c/d_e',
'group%2Fproject',
'group%2Fsub%2Fproject',
// GitLab allows consecutive hyphens in a path segment; must not be rejected.
'foo--bar',
'single',
// GitLab allows consecutive hyphens in a path segment; must not be rejected
// (as long as the reference is still namespace-qualified).
'group/foo--bar',
'12345',
];
validCases.forEach((value) => {
@ -42,8 +42,9 @@ describe('GITLAB_PROJECT_REGEX', () => {
});
describe('invalid project identifiers', () => {
// Regression for #8665: a display name with a space must be rejected inline
// instead of producing a confusing 404 at poll time.
// Regression for #8665: both a display name with a space AND a bare
// single-segment slug (which the REST API can never resolve, so it 404s at
// poll time) must be rejected inline instead.
const invalidCases = [
'My Group/My Gitlab',
'group/Test Gitlab',
@ -52,6 +53,10 @@ describe('GITLAB_PROJECT_REGEX', () => {
'trailingSpace ',
'https://gitlab.com/foo/bar',
'foo/bar?scope=all',
// Missing namespace — the exact value from the #8665 logs.
'test_config',
'single',
'foo--bar',
];
invalidCases.forEach((value) => {
it(`rejects "${value}"`, () => {

View file

@ -8,14 +8,15 @@ import {
CROSS_ORIGIN_WARNING,
ISSUE_PROVIDER_COMMON_FORM_FIELDS,
} from '../../common-issue-form-stuff.const';
// Matches either a numeric project ID or a path (namespace/project-slug, or its
// %2F-encoded form). Anchored at both ends: without a leading `^` the original
// pattern only matched a valid *tail*, so display names with spaces (e.g.
// "My Group/My Gitlab") passed validation and produced a 404 at poll time (#8665).
// This is a "did you paste a display name?" sanity guard, not a strict GitLab
// path validator, so the char class stays permissive (e.g. accepts consecutive
// hyphens, which GitLab paths allow) rather than risk false-rejecting valid paths.
export const GITLAB_PROJECT_REGEX = /^(?:[1-9][0-9]*|[\w%./-]+)$/i;
// A GitLab project reference is EITHER a numeric project ID OR a namespace-qualified
// path (`group/project`, subgroups, or the `%2F`-encoded form) — the REST API has no
// way to resolve a project by a bare slug, so a single-segment name like `test_config`
// always 404s at poll time (#8665). Require a path separator (`/` or `%2F`) for the
// non-numeric branch so that mistake gets inline feedback instead. Still permissive
// about the segment chars (e.g. consecutive hyphens, which GitLab paths allow) to
// avoid false-rejecting valid paths; the separator lookahead keeps the char class a
// single unnested quantifier (no catastrophic backtracking).
export const GITLAB_PROJECT_REGEX = /^(?:[1-9][0-9]*|(?=.*(?:\/|%2F))[\w.%/-]+)$/i;
export const GITLAB_CONFIG_FORM: LimitedFormlyFieldConfig<IssueProviderGitlab>[] = [
...CROSS_ORIGIN_WARNING,