obsidian-mcp-server
Version:
MCP server for Obsidian vaults — read, write, search, and surgically edit notes, tags, and frontmatter via the Local REST API plugin. STDIO or Streamable HTTP.
34 lines • 1.63 kB
JavaScript
/**
* @fileoverview Static safety guards for user-supplied `nameRegex` filters.
* JavaScript's RegExp engine has no native execution timeout, so we statically
* reject the textbook catastrophic-backtracking shapes before calling
* `new RegExp(...)`. Shared by tools that post-filter upstream listings by
* name (`obsidian_list_tags`, `obsidian_list_commands`).
* @module mcp-server/tools/definitions/_shared/regex-safety
*/
/** Maximum allowed pattern length — bounds compile cost and AST surface. */
export const NAME_REGEX_MAX_LENGTH = 256;
/**
* Detects the canonical catastrophic-backtracking shape: a `+`/`*`/`{N,}`
* quantifier immediately following a `)` whose interior already ends in a
* `+`/`*`/`}` quantifier (e.g. `(a+)+`, `(.*)*`, `(a{2,})*`). Not exhaustive —
* patterns with overlapping alternation like `(a|a)*` still slip through —
* but eliminates the textbook ReDoS vector at zero runtime cost.
*/
const NESTED_QUANTIFIER = /[+*}]\)[*+{]/;
/**
* Returns a human-readable reason string when the pattern is unsafe, or
* `undefined` when it passes the static guards. Callers compile with
* `new RegExp(pattern)` after this returns `undefined` and surface the
* returned reason via the tool's `regex_unsafe` error.
*/
export function nameRegexSafetyIssue(pattern) {
if (pattern.length > NAME_REGEX_MAX_LENGTH) {
return `pattern exceeds ${NAME_REGEX_MAX_LENGTH}-character limit`;
}
if (NESTED_QUANTIFIER.test(pattern)) {
return 'pattern contains nested quantifiers (catastrophic-backtracking risk)';
}
return;
}
//# sourceMappingURL=regex-safety.js.map