diff --git a/lib/config/validation.ts b/lib/config/validation.ts index f302fb005c37df57a934b6b268eac0e9f25b9abe..f1d0a36ca80292632dc21185b7f901a59d24e587 100644 --- a/lib/config/validation.ts +++ b/lib/config/validation.ts @@ -8,8 +8,12 @@ import type { } from '../modules/manager/custom/regex/types'; import type { CustomManager } from '../modules/manager/custom/types'; import type { HostRule } from '../types/host-rules'; -import { configRegexPredicate, isConfigRegex, regEx } from '../util/regex'; -import { anyMatchRegexOrMinimatch } from '../util/string'; +import { regEx } from '../util/regex'; +import { + anyMatchRegexOrMinimatch, + configRegexPredicate, + isConfigRegex, +} from '../util/string-match'; import * as template from '../util/template'; import { hasValidSchedule, diff --git a/lib/modules/datasource/docker/index.ts b/lib/modules/datasource/docker/index.ts index ea9e6dd897098e2f65c2b426a0ceaa1d88cbdcb3..0653cc22f6f4b48b4d364ab79ee1f1ab6a74140f 100644 --- a/lib/modules/datasource/docker/index.ts +++ b/lib/modules/datasource/docker/index.ts @@ -8,7 +8,7 @@ import type { HttpResponse } from '../../../util/http/types'; import { hasKey } from '../../../util/object'; import { regEx } from '../../../util/regex'; import { type AsyncResult, Result } from '../../../util/result'; -import { isDockerDigest } from '../../../util/string'; +import { isDockerDigest } from '../../../util/string-match'; import { ensurePathPrefix, joinUrlParts, diff --git a/lib/modules/platform/bitbucket/index.ts b/lib/modules/platform/bitbucket/index.ts index 660e9dd732f73c0b20b86580fa46a79f38e399a0..dc6301ae89d5a1daa507de82ca37ea19648cafc3 100644 --- a/lib/modules/platform/bitbucket/index.ts +++ b/lib/modules/platform/bitbucket/index.ts @@ -10,7 +10,7 @@ import { BitbucketHttp, setBaseUrl } from '../../../util/http/bitbucket'; import type { HttpOptions } from '../../../util/http/types'; import { regEx } from '../../../util/regex'; import { sanitize } from '../../../util/sanitize'; -import { isUUID } from '../../../util/string'; +import { UUIDRegex } from '../../../util/string-match'; import type { BranchStatusConfig, CreatePRConfig, @@ -701,7 +701,11 @@ export async function addReviewers( const body = { title, reviewers: reviewers.map((username: string) => { - const key = isUUID(username) ? 'uuid' : 'username'; + const isUUID = + username.startsWith('{') && + username.endsWith('}') && + UUIDRegex.test(username.slice(1, -1)); + const key = isUUID ? 'uuid' : 'username'; return { [key]: username, }; diff --git a/lib/util/http/host-rules.ts b/lib/util/http/host-rules.ts index 2572891f7c19d56af4e1a5782375ff2bcbe1b10d..009d18450e4315c7205b79417f5e647fcf0f024c 100644 --- a/lib/util/http/host-rules.ts +++ b/lib/util/http/host-rules.ts @@ -10,7 +10,7 @@ import { logger } from '../../logger'; import { hasProxy } from '../../proxy'; import type { HostRule } from '../../types'; import * as hostRules from '../host-rules'; -import { anyMatchRegexOrMinimatch } from '../string'; +import { anyMatchRegexOrMinimatch } from '../string-match'; import { parseUrl } from '../url'; import { dnsLookup } from './dns'; import { keepAliveAgents } from './keep-alive'; diff --git a/lib/util/package-rules/base-branches.ts b/lib/util/package-rules/base-branches.ts index dd6241ad2f7ca7b0d58da4a9ce91e5deca58fb45..c95e66cbf772d3e3d15c0e9812641d865fd9010b 100644 --- a/lib/util/package-rules/base-branches.ts +++ b/lib/util/package-rules/base-branches.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import type { PackageRule, PackageRuleInputConfig } from '../../config/types'; -import { configRegexPredicate } from '../regex'; +import { configRegexPredicate } from '../string-match'; import { Matcher } from './base'; export class BaseBranchesMatcher extends Matcher { diff --git a/lib/util/package-rules/current-value.ts b/lib/util/package-rules/current-value.ts index 5f3f24a0d35e58d9191e837f5d0b4aa48e04f207..449274f229dd7e4c0439aa23d97fc193ddd7777b 100644 --- a/lib/util/package-rules/current-value.ts +++ b/lib/util/package-rules/current-value.ts @@ -1,7 +1,7 @@ import is from '@sindresorhus/is'; import type { PackageRule, PackageRuleInputConfig } from '../../config/types'; import { logger } from '../../logger'; -import { configRegexPredicate } from '../regex'; +import { configRegexPredicate } from '../string-match'; import { Matcher } from './base'; export class CurrentValueMatcher extends Matcher { diff --git a/lib/util/package-rules/current-version.ts b/lib/util/package-rules/current-version.ts index b4f89db904c12b9dfb3d439b38254952f3940759..82ea5db9bf3eb7afd33712c985f8e174e5050354 100644 --- a/lib/util/package-rules/current-version.ts +++ b/lib/util/package-rules/current-version.ts @@ -2,7 +2,7 @@ import is from '@sindresorhus/is'; import type { PackageRule, PackageRuleInputConfig } from '../../config/types'; import { logger } from '../../logger'; import * as allVersioning from '../../modules/versioning'; -import { configRegexPredicate } from '../regex'; +import { configRegexPredicate } from '../string-match'; import { Matcher } from './base'; export class CurrentVersionMatcher extends Matcher { diff --git a/lib/util/package-rules/repositories.ts b/lib/util/package-rules/repositories.ts index ac99ef4d0f165236559a9083aa607a42e4376b48..3b7158e8cc6f8787e31b39cece80b4f67029d921 100644 --- a/lib/util/package-rules/repositories.ts +++ b/lib/util/package-rules/repositories.ts @@ -1,6 +1,6 @@ import is from '@sindresorhus/is'; import type { PackageRule, PackageRuleInputConfig } from '../../config/types'; -import { anyMatchRegexOrMinimatch } from '../string'; +import { anyMatchRegexOrMinimatch } from '../string-match'; import { Matcher } from './base'; export class RepositoriesMatcher extends Matcher { diff --git a/lib/util/regex.spec.ts b/lib/util/regex.spec.ts index 923e5769fdf108d7cceedaa4d695eaa791585014..4252ed17b01b288a4567bbcdd901b186de5e12c4 100644 --- a/lib/util/regex.spec.ts +++ b/lib/util/regex.spec.ts @@ -1,6 +1,6 @@ import RE2 from 're2'; import { CONFIG_VALIDATION } from '../constants/error-messages'; -import { configRegexPredicate, regEx } from './regex'; +import { regEx } from './regex'; describe('util/regex', () => { beforeEach(() => { @@ -37,26 +37,4 @@ describe('util/regex', () => { const regex = await import('./regex'); expect(regex.regEx('foo')).toBeInstanceOf(RegExp); }); - - describe('configRegexPredicate', () => { - it('allows valid regex pattern', () => { - expect(configRegexPredicate('/hello/')).not.toBeNull(); - }); - - it('invalidates invalid regex pattern', () => { - expect(configRegexPredicate('/^test\\d+$/m')).toBeNull(); - }); - - it('allows the i flag in regex pattern', () => { - expect(configRegexPredicate('/^test\\d+$/i')).not.toBeNull(); - }); - - it('allows negative regex pattern', () => { - expect(configRegexPredicate('!/^test\\d+$/i')).not.toBeNull(); - }); - - it('does not allow non-regex input', () => { - expect(configRegexPredicate('hello')).toBeNull(); - }); - }); }); diff --git a/lib/util/regex.ts b/lib/util/regex.ts index 719dfce054359885d7fd0ea20dda6acd59be012e..947a312a8021bd973309b40a9b04537fc1771cd5 100644 --- a/lib/util/regex.ts +++ b/lib/util/regex.ts @@ -72,42 +72,3 @@ export function escapeRegExp(input: string): string { } export const newlineRegex = regEx(/\r?\n/); - -const configValStart = regEx(/^!?\//); -const configValEnd = regEx(/\/i?$/); - -export function isConfigRegex(input: unknown): input is string { - return ( - is.string(input) && configValStart.test(input) && configValEnd.test(input) - ); -} - -function parseConfigRegex(input: string): RegExp | null { - try { - const regexString = input - .replace(configValStart, '') - .replace(configValEnd, ''); - return input.endsWith('i') ? regEx(regexString, 'i') : regEx(regexString); - } catch (err) { - // no-op - } - return null; -} - -type ConfigRegexPredicate = (s: string) => boolean; - -export function configRegexPredicate( - input: string, -): ConfigRegexPredicate | null { - if (isConfigRegex(input)) { - const configRegex = parseConfigRegex(input); - if (configRegex) { - const isPositive = !input.startsWith('!'); - return (x: string): boolean => { - const res = configRegex.test(x); - return isPositive ? res : !res; - }; - } - } - return null; -} diff --git a/lib/util/string-match.spec.ts b/lib/util/string-match.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..02ab8c4598e470ff81e8f099f73df29f0e267db5 --- /dev/null +++ b/lib/util/string-match.spec.ts @@ -0,0 +1,25 @@ +import { configRegexPredicate } from './string-match'; + +describe('util/string-match', () => { + describe('configRegexPredicate', () => { + it('allows valid regex pattern', () => { + expect(configRegexPredicate('/hello/')).not.toBeNull(); + }); + + it('invalidates invalid regex pattern', () => { + expect(configRegexPredicate('/^test\\d+$/m')).toBeNull(); + }); + + it('allows the i flag in regex pattern', () => { + expect(configRegexPredicate('/^test\\d+$/i')).not.toBeNull(); + }); + + it('allows negative regex pattern', () => { + expect(configRegexPredicate('!/^test\\d+$/i')).not.toBeNull(); + }); + + it('does not allow non-regex input', () => { + expect(configRegexPredicate('hello')).toBeNull(); + }); + }); +}); diff --git a/lib/util/string-match.ts b/lib/util/string-match.ts new file mode 100644 index 0000000000000000000000000000000000000000..42c7b234faaaf785aea1c377e0942f4e5d66494b --- /dev/null +++ b/lib/util/string-match.ts @@ -0,0 +1,70 @@ +import is from '@sindresorhus/is'; +import { minimatch } from './minimatch'; +import { regEx } from './regex'; + +export function isDockerDigest(input: string): boolean { + return /^sha256:[a-f0-9]{64}$/i.test(input); +} + +export function matchRegexOrMinimatch(input: string, pattern: string): boolean { + if (pattern.length > 2 && pattern.startsWith('/') && pattern.endsWith('/')) { + try { + const regex = regEx(pattern.slice(1, -1)); + return regex.test(input); + } catch (err) { + return false; + } + } + + return minimatch(pattern, { dot: true }).match(input); +} + +export function anyMatchRegexOrMinimatch( + input: string, + patterns: string[], +): boolean | null { + return patterns.some((pattern) => matchRegexOrMinimatch(input, pattern)); +} + +export const UUIDRegex = regEx( + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, +); + +const configValStart = regEx(/^!?\//); +const configValEnd = regEx(/\/i?$/); + +export function isConfigRegex(input: unknown): input is string { + return ( + is.string(input) && configValStart.test(input) && configValEnd.test(input) + ); +} + +function parseConfigRegex(input: string): RegExp | null { + try { + const regexString = input + .replace(configValStart, '') + .replace(configValEnd, ''); + return input.endsWith('i') ? regEx(regexString, 'i') : regEx(regexString); + } catch (err) { + // no-op + } + return null; +} + +type ConfigRegexPredicate = (s: string) => boolean; + +export function configRegexPredicate( + input: string, +): ConfigRegexPredicate | null { + if (isConfigRegex(input)) { + const configRegex = parseConfigRegex(input); + if (configRegex) { + const isPositive = !input.startsWith('!'); + return (x: string): boolean => { + const res = configRegex.test(x); + return isPositive ? res : !res; + }; + } + } + return null; +} diff --git a/lib/util/string.spec.ts b/lib/util/string.spec.ts index 3afa60e9f214337160ad9d37bde0555c397a0cd7..9df3c52ac209371c740da67975e60760c9f56251 100644 --- a/lib/util/string.spec.ts +++ b/lib/util/string.spec.ts @@ -1,4 +1,4 @@ -import { coerceString, isUUID, looseEquals, replaceAt } from './string'; +import { coerceString, looseEquals, replaceAt } from './string'; describe('util/string', () => { describe('replaceAt', () => { @@ -40,12 +40,4 @@ describe('util/string', () => { expect(coerceString(null)).toBe(''); expect(coerceString(null, 'foo')).toBe('foo'); }); - - describe('isUUID', () => { - it('proper checks valid and invalid UUID strings', () => { - expect(isUUID('{90b6646d-1724-4a64-9fd9-539515fe94e9}')).toBe(true); - expect(isUUID('{90B6646D-1724-4A64-9FD9-539515FE94E9}')).toBe(true); - expect(isUUID('not-a-uuid')).toBe(false); - }); - }); }); diff --git a/lib/util/string.ts b/lib/util/string.ts index 3944412038c1dbd85b1a7f0ef9649b8010b652e0..4ad6797651b067d881a7a3f646461ca5dfb35496 100644 --- a/lib/util/string.ts +++ b/lib/util/string.ts @@ -1,6 +1,3 @@ -import { minimatch } from './minimatch'; -import { regEx } from './regex'; - // Return true if the match string is found at index in content export function matchAt( content: string, @@ -56,10 +53,6 @@ export function looseEquals( return a.localeCompare(b, undefined, { sensitivity: 'base' }) === 0; } -export function isDockerDigest(input: string): boolean { - return /^sha256:[a-f0-9]{64}$/i.test(input); -} - export function titleCase(input: string): string { const words = input.toLowerCase().split(' '); @@ -97,31 +90,3 @@ export function coerceString( ): string { return val ?? def ?? ''; } - -export function matchRegexOrMinimatch(input: string, pattern: string): boolean { - if (pattern.length > 2 && pattern.startsWith('/') && pattern.endsWith('/')) { - try { - const regex = regEx(pattern.slice(1, -1)); - return regex.test(input); - } catch (err) { - return false; - } - } - - return minimatch(pattern, { dot: true }).match(input); -} - -export function anyMatchRegexOrMinimatch( - input: string, - patterns: string[], -): boolean | null { - return patterns.some((pattern) => matchRegexOrMinimatch(input, pattern)); -} - -const UUIDRegex = regEx( - /^\{[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\}$/i, -); - -export function isUUID(input: string): boolean { - return UUIDRegex.test(input); -} diff --git a/lib/workers/global/autodiscover.ts b/lib/workers/global/autodiscover.ts index f70f925f030d1cb3f667767ac474fe502a985438..2e9f1216cd50ad6597538d1d613b06a79ab390e7 100644 --- a/lib/workers/global/autodiscover.ts +++ b/lib/workers/global/autodiscover.ts @@ -3,7 +3,7 @@ import type { AllConfig } from '../../config/types'; import { logger } from '../../logger'; import { platform } from '../../modules/platform'; import { minimatchFilter } from '../../util/minimatch'; -import { configRegexPredicate, isConfigRegex } from '../../util/regex'; +import { configRegexPredicate, isConfigRegex } from '../../util/string-match'; // istanbul ignore next function repoName(value: string | { repository: string }): string { diff --git a/lib/workers/repository/process/index.ts b/lib/workers/repository/process/index.ts index cef8b546c1886a7bf3c90a217a3b335456c55b90..22aac60464d8d55948d56e65478b411908b85f2f 100644 --- a/lib/workers/repository/process/index.ts +++ b/lib/workers/repository/process/index.ts @@ -11,8 +11,8 @@ import { scm } from '../../../modules/platform/scm'; import { getCache } from '../../../util/cache/repository'; import { clone } from '../../../util/clone'; import { getBranchList } from '../../../util/git'; -import { configRegexPredicate } from '../../../util/regex'; import { addSplit } from '../../../util/split'; +import { configRegexPredicate } from '../../../util/string-match'; import type { BranchConfig } from '../../types'; import { readDashboardBody } from '../dependency-dashboard'; import { ExtractResult, extract, lookup, update } from './extract-update'; diff --git a/lib/workers/repository/process/lookup/filter.ts b/lib/workers/repository/process/lookup/filter.ts index bdcc4371e9d02c3a98c1a469e6ca9151c9f89a4d..3efcc43978ec759f2481d620f1f6ba4d8d8b5d56 100644 --- a/lib/workers/repository/process/lookup/filter.ts +++ b/lib/workers/repository/process/lookup/filter.ts @@ -6,7 +6,7 @@ import type { VersioningApi } from '../../../../modules/versioning'; import * as npmVersioning from '../../../../modules/versioning/npm'; import * as pep440 from '../../../../modules/versioning/pep440'; import * as poetryVersioning from '../../../../modules/versioning/poetry'; -import { configRegexPredicate } from '../../../../util/regex'; +import { configRegexPredicate } from '../../../../util/string-match'; import type { FilterConfig } from './types'; export function filterVersions(