Tips & Tricks (Mis à jour: 02/06/2026)

Traiter Markdown et MDX en sécurité avec Claude Code

Traiter Markdown/MDX avec Claude Code: AST, frontmatter, XSS, liens et QA locale.

Traiter Markdown et MDX en sécurité avec Claude Code

Markdown n’est pas un simple bloc de texte

Un article Markdown ou MDX publié contient plus que des paragraphes. Il porte du frontmatter, une description SEO, des niveaux de titres, des ancres générées, des blocs de code, des liens internes, des liens officiels, des routes par langue et parfois du HTML brut. Si l’on demande à Claude Code de “rendre l’article meilleur” sans contrat de traitement, le texte peut devenir plus fluide tout en cassant un slug, en supprimant un CTA, en changeant l’image hero ou en laissant une locale comme résumé trop court.

La méthode fiable consiste à séparer rédaction et vérification. Claude Code peut réécrire, localiser et enrichir. La structure doit rester contrôlée par des scripts. Pour Markdown et MDX, on lit les fichiers avec un AST, c’est-à-dire un arbre de syntaxe abstraite. Le frontmatter se valide comme des données. Le HTML doit passer par une étape explicite de sanitization. Les dix fichiers de locale doivent être contrôlés ensemble.

Les références principales ont été vérifiées le 2 juin 2026. Le guide unified décrit le pipeline parse, transform et stringify. La page syntax trees explique pourquoi un AST est plus sûr qu’une lecture ligne par ligne. Markdown passe par remark et remark-parse. La syntaxe MDX est couverte par la documentation MDX. Pour le frontmatter, gray-matter est un choix courant. Pour le HTML et le XSS, comparez rehype-sanitize avec la cheatsheet OWASP XSS. Pour cadrer l’agent, lisez aussi Claude Code overview et settings.

flowchart LR
  A["Fichier MDX"] --> B["frontmatter"]
  B --> C["validation schema"]
  A --> D["AST remark / MDX"]
  D --> E["titres, fences, liens"]
  D --> F["pipeline rehype"]
  F --> G["sanitization"]
  C --> H["locale et build checks"]
  E --> H
  G --> H

Choisir le bon parser avant de coder

La première consigne donnée à Claude Code doit nommer les outils. Dire seulement “parse Markdown” pousse souvent vers une regex courte. Cela suffit pour un fichier jouet, pas pour un article publié.

BesoinBon choixRaccourci fragile
Lire titres, liens et code fencesremark-parse avec parcours ASTRegex ^## sur le texte
Gérer du JSX dans .mdxremark-mdx ou le compiler MDXParser Markdown seul
Produire du HTMLremark-rehype vers rehypeConcaténer des strings HTML
Autoriser du HTML brutrehype-raw puis rehype-sanitizeallowDangerousHtml seul
Lire frontmattergray-matter plus validationSplit YAML à la main

L’AST distingue le sens. Un ## faux titre dans un bloc de code ne doit pas entrer dans la table des matières. Une URL dans les props d’un composant MDX n’a pas toujours le même rôle qu’un lien éditorial. Un tags: Claude Code, Markdown est une chaîne, pas un tableau. Ces problèmes se détectent mieux avec parser et schema qu’avec une relecture visuelle.

Quatre cas d’usage concrets

Le premier cas est la mise à jour d’un article déjà publié. Il faut revoir title, description, updatedDate, liens officiels, liens internes, exemples de code et CTA. Pour ClaudeCodeLab, on peut relier le sujet aux bonnes pratiques CLAUDE.md et au web scraping avec Claude Code, sans modifier d’autres slugs.

Le deuxième cas est une documentation qui utilise des composants MDX. Callouts, onglets, cartes de prix, FAQ et exemples vivants sont pratiques, mais ils mélangent Markdown et JSX. Un checker qui ne comprend pas MDX casse vite les composants.

Le troisième cas est la publication multilingue. Un canonical japonais complet ne suffit pas si les versions française, espagnole, portugaise ou indonésienne deviennent de simples résumés. Chaque locale doit contenir cas d’usage, pièges, snippets exécutables, liens officiels, liens internes, CTA et note de vérification.

Le quatrième cas est l’exploitation commerciale du contenu. Pages Gumroad, pages de formation, ressources gratuites et emails réutilisent souvent Markdown. Plus la page est proche d’un achat ou d’une demande de conseil, plus les code fences, les liens et le HTML doivent être vérifiables.

Installation minimale à copier

Les snippets ci-dessous utilisent Node.js 18 ou plus et des modules ESM. Testez-les dans un dossier isolé avant de les brancher sur le dépôt.

mkdir mdx-audit-demo
cd mdx-audit-demo
npm init -y
npm pkg set type=module
npm install unified remark-parse remark-mdx remark-gfm gray-matter
npm install unist-util-visit github-slugger
npm install remark-rehype rehype-raw rehype-sanitize rehype-stringify
mkdir tools

Exemple 1: auditer frontmatter, titres, fences et liens

Ce script lit le frontmatter avec gray-matter, parse le body avec remark et le support MDX, puis vérifie les champs obligatoires, la longueur de description, les langues de code fences et la présence de liens internes et externes.

// tools/audit-mdx.mjs
import fs from "node:fs/promises";
import matter from "gray-matter";
import GithubSlugger from "github-slugger";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkMdx from "remark-mdx";
import remarkGfm from "remark-gfm";
import { visit } from "unist-util-visit";

const file = process.argv[2];
if (!file) {
  throw new Error("Usage: node tools/audit-mdx.mjs article.mdx");
}

const source = await fs.readFile(file, "utf8");
const { data, content } = matter(source);
const errors = [];
const links = { internal: [], external: [] };
const headings = [];
const codeBlocks = [];

for (const key of ["title", "description", "pubDate", "heroImage", "lang"]) {
  if (typeof data[key] !== "string" || data[key].trim() === "") {
    errors.push(`frontmatter.${key} is required`);
  }
}

if ([...String(data.description ?? "")].length > 120) {
  errors.push("description must be 120 characters or fewer");
}

if (!Array.isArray(data.tags) || data.tags.length === 0) {
  errors.push("frontmatter.tags must be a non-empty array");
}

const tree = unified()
  .use(remarkParse)
  .use(remarkMdx)
  .use(remarkGfm)
  .parse(content);

const slugger = new GithubSlugger();

visit(tree, (node) => {
  if (node.type === "heading") {
    const text = plainText(node);
    headings.push({ depth: node.depth, text, slug: slugger.slug(text) });
  }

  if (node.type === "code") {
    codeBlocks.push({ lang: node.lang || "", meta: node.meta || "" });
    if (!node.lang) errors.push("code fence is missing a language");
  }

  if (node.type === "link") {
    const url = String(node.url || "");
    if (url.startsWith("http")) links.external.push(url);
    if (url.startsWith("/")) links.internal.push(url);
  }
});

if (links.internal.length === 0) errors.push("missing internal link");
if (links.external.length === 0) errors.push("missing external link");

if (errors.length > 0) {
  console.error(errors.map((error) => `- ${error}`).join("\n"));
  process.exit(1);
}

console.log(JSON.stringify({ headings, codeBlocks, links }, null, 2));

function plainText(node) {
  if (typeof node.value === "string") return node.value;
  if (!Array.isArray(node.children)) return "";
  return node.children.map(plainText).join("");
}
node tools/audit-mdx.mjs site/src/content/blog-fr/example.mdx

Exemple 2: convertir Markdown en HTML sûr

Si vous n’avez pas besoin de HTML brut, ne l’activez pas. Si vous devez l’accepter, parsez-le puis sanitizez-le immédiatement. allowDangerousHtml seul n’est pas une mesure de sécurité.

// tools/markdown-to-safe-html.mjs
import fs from "node:fs/promises";
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkGfm from "remark-gfm";
import remarkRehype from "remark-rehype";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";

const file = process.argv[2];
const markdown = await fs.readFile(file, "utf8");
const schema = {
  ...defaultSchema,
  attributes: {
    ...defaultSchema.attributes,
    code: [["className", /^language-/]],
  },
};

const html = await unified()
  .use(remarkParse)
  .use(remarkGfm)
  .use(remarkRehype, { allowDangerousHtml: true })
  .use(rehypeRaw)
  .use(rehypeSanitize, schema)
  .use(rehypeStringify)
  .process(markdown);

console.log(String(html));

L’ordre est essentiel. rehype-raw remet le HTML brut dans l’arbre HTML, puis rehype-sanitize supprime les tags et attributs non autorisés. Sans cette deuxième étape, une valeur dangereuse peut arriver jusqu’au DOM.

Exemple 3: vérifier les dix locales

Ce script confirme que le même slug existe dans chaque langue, que heroImage est conservé, que updatedDate est correct et que chaque body contient des liens internes et externes.

// tools/check-locales.mjs
import fs from "node:fs";
import path from "node:path";
import matter from "gray-matter";

const slug = "claude-code-markdown-processing.mdx";
const expectedHero = "/images/hero/hero-077.png";
const locales = [
  ["ja", "site/src/content/blog"],
  ["en", "site/src/content/blog-en"],
  ["zh", "site/src/content/blog-zh"],
  ["ko", "site/src/content/blog-ko"],
  ["es", "site/src/content/blog-es"],
  ["fr", "site/src/content/blog-fr"],
  ["de", "site/src/content/blog-de"],
  ["pt", "site/src/content/blog-pt"],
  ["hi", "site/src/content/blog-hi"],
  ["id", "site/src/content/blog-id"],
];

const errors = [];

for (const [lang, dir] of locales) {
  const file = path.join(dir, slug);
  const source = fs.readFileSync(file, "utf8");
  const { data, content } = matter(source);
  if (data.lang !== lang) errors.push(`${lang}: lang mismatch`);
  if (data.heroImage !== expectedHero) errors.push(`${lang}: hero changed`);
  if (data.updatedDate !== "2026-06-02") {
    errors.push(`${lang}: updatedDate mismatch`);
  }
  if ([...String(data.description ?? "")].length > 120) {
    errors.push(`${lang}: description too long`);
  }
  if (!content.includes("https://")) errors.push(`${lang}: no external link`);
  if (!content.includes("](/")) errors.push(`${lang}: no internal link`);
}

if (errors.length > 0) {
  console.error(errors.map((error) => `- ${error}`).join("\n"));
  process.exit(1);
}

console.log("locale set is consistent");

Pièges concrets

ÉchecRésultatGarde-fou
Lire les titres avec regexLes faux titres dans le code entrent dans le sommaireParcourir les noeuds heading
tags devient une stringFiltres et articles liés cassentValider les types frontmatter
Slugs incohérentsAncres cassées entre languesUtiliser le même slugger
Faire confiance au HTML brutRisque XSS par attributs ou tagsSanitizer avec schema
Ne pas vérifier les liens externesDocumentation officielle déplacéeTester avant publication
Prompt trop largeFichiers d’autres workers modifiésFixer owned_files

Ces pièges doivent être écrits dans la demande. Claude Code répond mieux à des contraintes vérifiables qu’à une consigne vague comme “améliore la qualité”.

Prompt sûr pour Claude Code

task: "Refresh one published MDX article"
owned_files:
  - "site/src/content/blog-fr/claude-code-markdown-processing.mdx"
preserve:
  - "slug path"
  - "heroImage"
  - "unrelated dirty files"
required:
  - "updatedDate: 2026-06-02"
  - "description <= 120 characters"
  - "AST-based Markdown checks"
  - "official external links"
  - "internal links and monetization CTA"
forbidden:
  - "regex-only heading parsing"
  - "raw HTML without sanitization"
  - "thin locale summaries"
verification:
  - "node scripts/check-code-fences.mjs"
  - "node scripts/check-updated-article-quality.mjs"

Vérification avant publication et CTA

Avant publication, combinez scripts locaux et lecture humaine. Les scripts contrôlent structure, metadata, fences, liens et profondeur. La personne vérifie le naturel de la traduction, la lecture mobile, l’intention de recherche et le CTA.

node tools/audit-mdx.mjs site/src/content/blog-fr/claude-code-markdown-processing.mdx
node tools/check-locales.mjs
node scripts/check-code-fences.mjs
node scripts/check-updated-article-quality.mjs

Pour démarrer seul, utilisez la cheatsheet Claude Code gratuite. Pour des prompts réutilisables de review et rédaction, prenez les templates de prompts Claude Code. Pour une équipe qui veut permissions, CI, workflow multilingue et revue éditoriale, passez par le training et conseil Claude Code.

Résultat de vérification pratique

Dans cette mise à jour, Masa a traité l’article comme une chaîne de publication réelle: description courte, updatedDate, conservation de heroImage, langues des code fences, liens officiels, profondeur des locales et CTA. L’audit basé sur AST couvre des erreurs qu’une regex ne voit pas, surtout les titres dans les blocs de code et la syntaxe MDX près des composants. Les commandes finales sont node scripts/check-code-fences.mjs et node scripts/check-updated-article-quality.mjs. La leçon est nette: Claude Code devient fiable quand le contrat de l’article est exécutable, pas seulement quand le prompt demande une meilleure rédaction.

#Claude Code #Markdown #MDX #remark #opérations contenu
Gratuit

PDF gratuit: cheatsheet Claude Code

Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.

Nous protégeons vos données et n'envoyons pas de spam.

Masa

À propos de l'auteur

Masa

Ingénieur spécialisé dans les workflows pratiques avec Claude Code.