mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 15:59:30 +00:00
refactor(scripts): share regexp literal escaping (#99778)
This commit is contained in:
parent
19035bdca1
commit
e7b5946da0
11 changed files with 30 additions and 40 deletions
|
|
@ -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}["'\`]`),
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
4
scripts/lib/regexp.mjs
Normal 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, "\\$&");
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue