01 · Before and afterThe pattern
// before: build the rich doc (and validate links) from the template
const doc = buildBlocks(template); // isSafeUrl("{{event.url}}") -> false, link dropped
return resolveVariables(doc, context);
// after: resolve into the plain form first, derive and validate last
const markdown = resolveVariables(templateToMarkdown(template), context);
return { markdown, richText: markdownToBlocks(markdown) }; // isSafeUrl sees the real URL02 · Run it on your appThe check
Where your app fills templates (emails, workflow actions, AI prompts), does validation or escaping run on the filled-in value, or on the placeholder?
If the honest answer is no or not sure, this is worth a look before it reaches production.