fix: ReDoS prevention + bound slugify fallback scan

- Validate elicitation keys match variable name format before regex construction
  (prevents ReDoS via user-controlled regex metacharacters — CWE-1333)
- Narrow slugify fallback to only prompts with slug=null, cap at 500 results
- Add TODO to backfill slug column so fallback can be removed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mert Koseoglu
2026-03-19 21:40:18 +03:00
parent 0e9258c300
commit c95f80dd75
+9 -3
View File
@@ -220,12 +220,14 @@ function createServer(options: ServerOptions = {}) {
});
}
// Fallback: lookup by slugified title (for prompts without stored slug)
// TODO: Backfill slug column for all existing prompts so this fallback can be removed
if (!prompt) {
const allPrompts = await db.prompt.findMany({
where: promptFilter,
const unslugged = await db.prompt.findMany({
where: { ...promptFilter, slug: null },
select: promptSelect,
take: 500,
});
prompt = allPrompts.find((p) => slugify(p.title) === promptSlug) || null;
prompt = unslugged.find((p) => slugify(p.title) === promptSlug) || null;
}
if (!prompt) {
@@ -453,6 +455,10 @@ function createServer(options: ServerOptions = {}) {
if (elicitResult.action === "accept" && elicitResult.content) {
let filledContent = prompt.content;
for (const [key, value] of Object.entries(elicitResult.content)) {
// Skip keys that don't match valid variable name format (ReDoS prevention)
if (!/^[a-zA-Z_][a-zA-Z0-9_\s]*$/.test(key)) {
continue;
}
// Replace ${key} or ${key:default} patterns
filledContent = filledContent.replace(
new RegExp(`\\$\\{${key}(?::[^}]*)?\\}`, "g"),