refactor(scripts): share regexp literal escaping (#99778)

This commit is contained in:
Dallin Romney 2026-07-03 20:20:28 -07:00 committed by GitHub
parent 19035bdca1
commit e7b5946da0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 30 additions and 40 deletions

View file

@ -10,6 +10,7 @@ import {
BUNDLED_PLUGIN_ROOT_DIR,
} from "./lib/bundled-plugin-paths.mjs";
import { optionalBundledClusterSet } from "./lib/optional-bundled-clusters.mjs";
import { escapeRegExp } from "./lib/regexp.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const srcRoot = path.join(repoRoot, "src");
@ -548,12 +549,8 @@ function splitNameTokens(name) {
.filter(Boolean);
}
function escapeForRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function hasImportSource(source, specifier) {
const escaped = escapeForRegExp(specifier);
const escaped = escapeRegExp(specifier);
return new RegExp(`from\\s+["']${escaped}["']|import\\s*\\(\\s*["']${escaped}["']\\s*\\)`).test(
source,
);
@ -840,7 +837,7 @@ async function buildTestIndex(testFiles) {
}
function hasExecutableImportReference(source, importPath) {
const escapedImportPath = escapeForRegExp(importPath);
const escapedImportPath = escapeRegExp(importPath);
const suffix = String.raw`(?:\.[^"'\\\`]+)?`;
const patterns = [
new RegExp(String.raw`\bfrom\s*["'\`]${escapedImportPath}${suffix}["'\`]`),
@ -852,7 +849,7 @@ function hasExecutableImportReference(source, importPath) {
}
function hasModuleMockReference(source, importPath) {
const escapedImportPath = escapeForRegExp(importPath);
const escapedImportPath = escapeRegExp(importPath);
const suffix = String.raw`(?:\.[^"'\\\`]+)?`;
const patterns = [
new RegExp(String.raw`\bvi\.mock\s*\(\s*["'\`]${escapedImportPath}${suffix}["'\`]`),

View file

@ -4,6 +4,7 @@ import fs from "node:fs";
import path from "node:path";
import { collectDeprecatedInternalConfigApiViolations } from "./lib/deprecated-config-api-guard.mjs";
import { buildDeprecatedPluginSdkModuleSpecifiers } from "./lib/deprecated-plugin-sdk-usage.mjs";
import { escapeRegExp } from "./lib/regexp.mjs";
const repoRoot = process.cwd();
@ -56,10 +57,6 @@ function* walk(dir, rule) {
}
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
}
function collectIdentifierRuleViolations(rule) {
const allowedFiles = new Set(rule.allowedFiles ?? []);
const pattern = new RegExp(

View file

@ -1,6 +1,7 @@
// Mock OpenAI-compatible server for broader E2E scenarios.
import { createHash } from "node:crypto";
import http from "node:http";
import { escapeRegExp } from "../lib/regexp.mjs";
import { readTcpPortEnv } from "./lib/env-limits.mjs";
import {
boundedRequestLogBody,
@ -244,9 +245,7 @@ function collectFunctionCallOutputText(body) {
}
function hasDeclaredTool(bodyText, name) {
return new RegExp(`"name"\\s*:\\s*"${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"`, "u").test(
bodyText,
);
return new RegExp(`"name"\\s*:\\s*"${escapeRegExp(name)}"`, "u").test(bodyText);
}
function mcpCodeModeApiFileEvents(body, bodyText) {

View file

@ -1,5 +1,6 @@
// Probe script for OpenWebUI E2E connectivity.
import { Agent, setGlobalDispatcher } from "undici";
import { escapeRegExp } from "../lib/regexp.mjs";
import { readBoundedResponseText as readBoundedResponseTextWithLimit } from "./lib/bounded-response-text.mjs";
const baseUrl = process.env.OPENWEBUI_BASE_URL ?? "";
@ -158,10 +159,6 @@ function sleep(ms) {
});
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function redactDiagnosticText(text, extraSecrets = []) {
let redacted = text
.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/giu, "Bearer <redacted>")
@ -194,8 +191,11 @@ function cookieSecretValues(cookieHeader) {
}
function authDiagnosticSecretValues(authHeaders) {
const authorization = typeof authHeaders.authorization === "string" ? authHeaders.authorization : "";
const bearerToken = authorization.startsWith("Bearer ") ? authorization.slice("Bearer ".length) : "";
const authorization =
typeof authHeaders.authorization === "string" ? authHeaders.authorization : "";
const bearerToken = authorization.startsWith("Bearer ")
? authorization.slice("Bearer ".length)
: "";
const cookie = typeof authHeaders.cookie === "string" ? authHeaders.cookie : "";
return [bearerToken, authorization, cookie, ...cookieSecretValues(cookie)].filter(Boolean);
}

View file

@ -1,5 +1,6 @@
// Barnacle owns deterministic GitHub triage and auto-response behavior.
import { escapeRegExp } from "../lib/regexp.mjs";
import {
NEEDS_PR_CONTEXT_LABEL,
PROOF_SUFFICIENT_LABEL,
@ -294,7 +295,7 @@ function extractIssueFormValue(body, field) {
if (!body) {
return "";
}
const escapedField = field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const escapedField = escapeRegExp(field);
const regex = new RegExp(
`(?:^|\\n)###\\s+${escapedField}\\s*\\n([\\s\\S]*?)(?=\\n###\\s+|$)`,
"i",
@ -317,7 +318,7 @@ function hasLinkedReference(text) {
}
function hasFilledTemplateLine(body, field) {
const escapedField = field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const escapedField = escapeRegExp(field);
const regex = new RegExp(`^\\s*-\\s*${escapedField}:\\s*\\S`, "im");
return regex.test(body);
}
@ -334,7 +335,7 @@ function hasMostlyBlankTemplate(body) {
"Root cause",
"Target test or file",
].filter((field) => {
const escapedField = field.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const escapedField = escapeRegExp(field);
const regex = new RegExp(`^\\s*-\\s*${escapedField}(?: \\([^)]*\\))?:\\s*$`, "im");
return regex.test(body);
}).length;

View file

@ -1,5 +1,6 @@
// Shared PR context and evidence policy for GitHub checks and label decisions.
import { readBoundedResponseText } from "../lib/bounded-response.mjs";
import { escapeRegExp } from "../lib/regexp.mjs";
/** ClawSweeper-owned labels that OpenClaw preserves but does not mutate. */
export const PROOF_OVERRIDE_LABEL = "proof: override";
@ -53,10 +54,6 @@ const legacyProofFieldNames = [
const missingValueRegex =
/^(?:n\/?a|none|not applicable|tbd|todo|unknown|unsure|none provided|no evidence|not tested|untested|did not test|didn't test|could not test|couldn't test|-|(?:-{3,}|\*{3,}|_{3,})|\[[^\]]*\])\.?$/i;
function escapeRegex(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function createTimeoutError(label, timeoutMs) {
const error = new Error(`${label} timed out after ${timeoutMs}ms`);
error.code = "ETIMEDOUT";
@ -290,7 +287,7 @@ function extractMarkdownSections(headingRegex, body = "") {
}
export function hasAuthoredPullRequestSection(heading, body = "") {
const headingPattern = new RegExp(`^#{2,6}\\s+${escapeRegex(heading)}\\b[^\\n]*$`, "im");
const headingPattern = new RegExp(`^#{2,6}\\s+${escapeRegExp(heading)}\\b[^\\n]*$`, "im");
return !isMissingValue(extractMarkdownSections(headingPattern, body).at(-1) ?? "");
}
@ -300,7 +297,7 @@ function extractLegacyProofSections(body = "") {
function fieldLineRegex(name) {
return new RegExp(
`^\\s*(?:[-*]\\s*)?(?:\\*\\*)?${escapeRegex(name)}(?:\\s*\\([^)]*\\))?(?:\\*\\*)?\\s*:\\s*(.*)$`,
`^\\s*(?:[-*]\\s*)?(?:\\*\\*)?${escapeRegExp(name)}(?:\\s*\\([^)]*\\))?(?:\\*\\*)?\\s*:\\s*(.*)$`,
"i",
);
}
@ -375,7 +372,7 @@ function result(status, reason, details = {}) {
}
function extractMarkerField(marker, name) {
const match = marker.match(new RegExp(`\\b${escapeRegex(name)}=([^\\s>]+)`, "i"));
const match = marker.match(new RegExp(`\\b${escapeRegExp(name)}=([^\\s>]+)`, "i"));
return match?.[1] ?? "";
}

4
scripts/lib/regexp.mjs Normal file
View file

@ -0,0 +1,4 @@
/** Escape text so it can be embedded literally inside a RegExp constructor pattern. */
export function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

View file

@ -1,4 +1,5 @@
import { createHash } from "node:crypto";
import { escapeRegExp } from "./regexp.mjs";
const STABLE_RELEASE_TAG_RE = /^v(?<version>\d{4}\.\d{1,2}\.\d{1,2})(?:-[1-9]\d*)?$/u;
const MAX_ROLLBACK_DRILL_AGE_MS = 90 * 24 * 60 * 60 * 1000;
@ -14,10 +15,6 @@ function parseStableReleaseTagDetails(tag) {
};
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
}
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}

View file

@ -25,6 +25,7 @@ import {
PACKAGE_DIST_INVENTORY_RELATIVE_PATH,
writePackageDistInventory,
} from "../src/infra/package-dist-inventory.ts";
import { escapeRegExp } from "../src/shared/regexp.js";
import { checkCliBootstrapExternalImports } from "./check-cli-bootstrap-imports.mjs";
import {
collectBundledExtensionManifestErrors,
@ -958,7 +959,7 @@ export function collectForbiddenPackContentPaths(
export { collectPackUnpackedSizeErrors } from "./lib/npm-pack-budget.mjs";
function extractTag(item: string, tag: string): string | null {
const escapedTag = tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const escapedTag = escapeRegExp(tag);
const regex = new RegExp(`<${escapedTag}>([^<]+)</${escapedTag}>`);
return regex.exec(item)?.[1]?.trim() ?? null;
}

View file

@ -6,6 +6,7 @@ import { performance } from "node:perf_hooks";
import { fileURLToPath, pathToFileURL } from "node:url";
import { copyBundledPluginMetadata } from "./copy-bundled-plugin-metadata.mjs";
import { copyPluginSdkRootAlias } from "./copy-plugin-sdk-root-alias.mjs";
import { escapeRegExp } from "./lib/regexp.mjs";
import {
copyStaticExtensionAssets,
copyStaticExtensionAssetsToRuntimeOverlay,
@ -22,7 +23,6 @@ const ROOT_RUNTIME_ALIAS_PATTERN = /^(?<base>.+\.(?:runtime|contract))-[A-Za-z0-
const ROOT_STABLE_RUNTIME_ALIAS_PATTERN = /^.+\.(?:runtime|contract)\.js$/u;
const ROOT_RUNTIME_IMPORT_SPECIFIER_PATTERN =
/(["'])\.\/([^"']+\.(?:runtime|contract)-[A-Za-z0-9_-]+\.js)\1/gu;
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
const PLUGIN_SDK_ROOT_ALIAS_OUTPUT = "dist/plugin-sdk/root-alias.cjs";
const OFFICIAL_CHANNEL_CATALOG_OUTPUT = "dist/channel-catalog.json";
const LEGACY_ROOT_RUNTIME_COMPAT_ALIASES = [

View file

@ -7,6 +7,7 @@ import path from "node:path";
import process from "node:process";
import YAML from "yaml";
import { readBoundedResponseText } from "./lib/bounded-response.mjs";
import { escapeRegExp } from "./lib/regexp.mjs";
import { parseReportCliArgs, writeReportArtifact } from "./lib/report-cli-helpers.mjs";
import {
collectAllResolvedPackagesFromLockfile,
@ -132,10 +133,6 @@ function splitMinimumReleaseAgeExcludeSelector(selector) {
};
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
}
function packagePatternMatches(pattern, packageName) {
const regex = new RegExp(`^${pattern.split("*").map(escapeRegExp).join(".*")}$`, "u");
return regex.test(packageName);