feat(frontend): render markdown in description and summary fields
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

- New shared MarkdownText component (react-markdown + remark-gfm)
  that renders links, bold, italic, lists, code, blockquotes.
  External links open in a new tab with rel=noopener.
- Applied to: technique description, threat actor description,
  test description, campaign description, detection rule descriptions,
  D3FEND defense descriptions, red/blue summaries and validation notes.
- procedure_text (code/commands) stays in <pre> — not processed as MD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-05-29 08:38:53 +02:00
parent a8542512b4
commit db208b9f5c
7 changed files with 132 additions and 18 deletions

View File

@@ -1,5 +1,6 @@
import { useState } from "react";
import { useParams, useNavigate } from "react-router-dom";
import MarkdownText from "../components/MarkdownText";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import {
Loader2,
@@ -182,9 +183,10 @@ export default function TechniqueDetailPage() {
{/* Description */}
<div className="lg:col-span-2 rounded-xl border border-gray-800 bg-gray-900 p-6">
<h2 className="mb-3 text-lg font-semibold text-white">Description</h2>
<p className="text-sm text-gray-400 leading-relaxed whitespace-pre-wrap">
{technique.description || "No description available."}
</p>
<MarkdownText
content={technique.description || "No description available."}
className="text-sm text-gray-400 leading-relaxed"
/>
</div>
{/* Metadata */}
@@ -579,7 +581,7 @@ function DetectionRulesSection({ rules }: { rules: DetectionRule[] }) {
onClick={(e) => e.stopPropagation()}
>
{rule.description && (
<p className="text-xs text-gray-400 leading-relaxed">{rule.description}</p>
<MarkdownText content={rule.description} className="text-xs text-gray-400 leading-relaxed" />
)}
<div className="flex flex-wrap gap-3 text-xs text-gray-500">
{rule.rule_format && (
@@ -702,7 +704,7 @@ function D3FENDSection({ defenses }: { defenses: Array<{
{isExpanded && (
<div className="mt-3 space-y-2 border-t border-gray-700/50 pt-3">
{def.description ? (
<p className="text-xs text-gray-300 leading-relaxed">{def.description}</p>
<MarkdownText content={def.description} className="text-xs text-gray-300 leading-relaxed" />
) : (
<p className="text-xs text-gray-500 italic">No description available.</p>
)}