Ir para o conteúdo
Acecore

Renderizar links Markdown com segurança em respostas de chat com IA

by Gui
Índice
Renderizar links Markdown com segurança em respostas de chat com IA

Quando um chat com IA responde Veja [Serviços]( /services/ ), o link pode não ser renderizado e o Markdown bruto pode permanecer na tela.

Acecore encontrou esse caso no chat de contato e ajustou o renderizador em o PR que corrigiu a renderização de links Markdown.

Este artigo usa essa correção pequena como ponto de partida para converter respostas de IA em DOM com segurança.

Respostas de IA não são HTML confiável

Trate a saída do modelo como texto.

Links, negrito e listas são úteis, mas inserir a resposta em innerHTML faz o navegador interpretar qualquer string gerada pelo modelo.

Não é necessário implementar Markdown completo. É melhor detectar só os recursos suportados pelo chat e criar apenas nós DOM seguros.

O problema não é só espaço

O bug direto era um link assim:

[Serviços](/services/)

Uma regex rígida costuma assumir que a URL não tem espaços:

;/\[([^\]]+)\]\(([^)\s]+)\)/

[^)\s]+ rejeita espaços, então ( /services/ ) não vira link. A correção é permitir espaços dentro dos parênteses e normalizar depois.

;/\[([^\]]+)\]\(\s*([^)]+?)\s*\)/

Depois disso, a validação continua obrigatória.

Faça trim antes de validar

O fluxo deve ser:

  1. Extrair label e raw href
  2. Aplicar trim() no raw href
  3. Validar o href com allowlist
  4. Criar <a> apenas se for permitido
const href = String(rawHref || '').trim()

if (label && isSafeMarkdownHref(href)) {
  const link = document.createElement('a')
  link.href = href
  link.rel = 'noopener noreferrer'

  if (/^https?:\/\//i.test(href)) {
    link.target = '_blank'
  }

  link.textContent = label
  parent.appendChild(link)
}

Valide o mesmo valor que será renderizado. Caso contrário, a checagem perde valor.

A allowlist depende do site

Cada produto deve decidir quais URLs a IA pode mostrar.

TipoExemploDecisão
Path interno/services/Permitir
Mesmo originhttps://acecore.net/...Permitir
LINE oficialhttps://lin.ee/...Permitir quando for o canal oficial
mailtomailto:[email protected]Só endereço fixo
teltel:05088902788Só número fixo
Outros externosQualquer URLNão linkar por padrão
function isSafeMarkdownHref(href) {
  if (href.startsWith('/')) return true

  try {
    const url = new URL(href, window.location.origin)
    if (url.origin === window.location.origin) return true
    if (url.hostname === 'acecore.net') return true
    if (url.hostname === 'lin.ee') return true
  } catch {
    return false
  }

  return href === 'mailto:[email protected]' || href === 'tel:05088902788'
}

Um site de recrutamento pode liberar portais de vaga; um SaaS pode liberar documentação e status page. A função deve refletir o produto.

Faça fallback para texto

Se um link falha na validação, normalmente é melhor preservar o Markdown como texto do que apagá-lo.

O usuário mantém contexto, e a equipe consegue ver o que o modelo tentou emitir. O renderizador deve criar links seguros e também falhar de forma segura.

Teste os casos errados

Inclua pelo menos:

EntradaResultado esperado
[Serviços](/services/)Link interno
[Serviços]( /services/ )Link interno após trim
[LINE]( https://lin.ee/example )Link externo permitido
[Ruim](javascript:alert(1))Não vira link
[Externo](https://example.com/)Não vira link se o domínio não for permitido
[Quebrado](/services/Mostra como texto

No PR #99, as variações com e sem espaços foram confirmadas como a mesma URL pretendida.

Não implemente todo Markdown por padrão

Para chat, geralmente basta:

  • Parágrafos
  • Listas
  • Negrito
  • Código inline
  • Links

Tabelas, imagens, HTML bruto e notas de rodapé aumentam muito a responsabilidade. Mesmo com uma biblioteca, a política de HTML e links precisa ser definida separadamente.

Resumo

Renderizar links Markdown em respostas de IA parece um ajuste pequeno, mas define o limite de confiança na saída do modelo.

A regra prática é: texto primeiro, subconjunto pequeno, trim antes da validação, allowlist rígida e fallback seguro.

Fluxo de renderização de links

Text

Trate a resposta do modelo primeiro como texto puro.

Parse

Detecte apenas o Markdown que o chat realmente usa.

Validate

Faça trim do href e permita apenas URLs internas ou domínios aprovados.

Render

Crie elementos seguros com DOM API em vez de innerHTML.

Decisões que precisam ficar separadas

Renderização frouxa

  • Colocar respostas de IA diretamente em innerHTML
  • Tentar implementar Markdown completo de uma vez
  • Falhar quando há espaços ao redor da URL
  • Tratar URLs externas e javascript: da mesma forma

Renderização pequena e segura

  • Receber respostas como texto e converter só o necessário em DOM
  • Suportar apenas o subconjunto usado no chat
  • Validar URLs depois de trim
  • Manter URLs não permitidas como texto
Checklist de implementação
  • Concluído: Não confiar em respostas de IA como HTML
  • Concluído: Aceitar espaços ao redor de URLs Markdown
  • Concluído: Sempre fazer trim de href antes de validar
  • Concluído: Permitir apenas paths internos, origin atual e domínios necessários
  • Concluído: Definir target e rel em links externos
  • Concluído: Preservar links não permitidos como texto
  • Concluído: Testar URLs perigosas e Markdown quebrado
Perguntas frequentes
Usar markdown-it ou marked basta?
Mesmo com biblioteca, ainda é preciso decidir como tratar HTML, quais destinos de link permitir, como adicionar target e rel e como rejeitar URLs perigosas. Para chat, um renderizador pequeno pode bastar.
Permitir espaços ao redor da URL é perigoso?
O risco não está nos espaços. O ponto importante é validar o href depois do trim. Assim o renderizador tolera variações do modelo sem afrouxar a allowlist.
Links não permitidos devem ser removidos?
Normalmente deixá-los como texto ajuda a depurar e preserva contexto. Se a política exigir ocultar strings suspeitas, remover o link inteiro também é válido.

Comentários

Carregando comentários...

Links, e-mails e textos promocionais não podem ser publicados.

G

Gui

CEO da Acecore. Lidera sistemas de negócio, web, bancos de dados e infraestrutura, qualidade e adoção de IA da definição de problemas de negócio ao design, implantação e melhoria após o lançamento. Parte de capacidade prática em C#/.NET e também cobre PHP/JavaScript, SQL Server/PostgreSQL/MySQL e Linux/Windows Server, desenhando requisitos, escolhas tecnológicas, padrões de qualidade e operações de desenvolvimento baseadas em GitHub como um fluxo único. Incorpora IA generativa a processos de desenvolvimento, verificação e organização de informações, como uma base prática para que equipes pequenas entreguem mais rápido e com maior confiabilidade.

Definição de problemas de negócioSeleção tecnológicaDesign de sistemasC#/.NETDesign de bancos de dados/infraestruturaOperações de desenvolvimento no GitHubIA generativaDesign de fluxos de IADesign de qualidadeIntegração em campo

Quer saber mais sobre nossos serviços?

Oferecemos suporte abrangente em desenvolvimento de sistemas, design web, design gráfico e educação em TI.

Artigos relacionados

Pesquisar artigos