@@ -3,6 +3,7 @@ import { readFileSync } from 'fs'
33import { join } from 'path'
44import PageLayout from '@/components/PageLayout'
55import { generatePageMetadata } from '@/components/PageLayout'
6+ import { Metadata } from 'next'
67
78interface SecurityAdvisoryPageProps {
89 params : Promise < {
@@ -39,34 +40,82 @@ function getAdvisoryContent(advisory: string): { title: string; description: str
3940
4041// Function to convert Markdown content to JSX
4142function parseAdvisoryContent ( content : string ) : React . JSX . Element {
42- // Convert markdown to HTML-like content for rendering
43- let processedContent = content
44- // Convert headers
45- . replace ( / ^ # # ( .+ ) $ / gm, '<h2>$1</h2>' )
46- . replace ( / ^ # # # ( .+ ) $ / gm, '<h3>$1</h3>' )
47- . replace ( / ^ # # # # ( .+ ) $ / gm, '<h4>$1</h4>' )
48-
49- // Convert bold metadata format to definition list
50- . replace ( / ^ \* \* ( .+ ?) : \* \* ( .+ ) $ / gm, ( match , term , definition ) => {
51- return `<dl class="row"><dt class="col-sm-3">${ term } :</dt><dd class="col-sm-9">${ definition } </dd></dl>`
52- } )
53-
54- // Convert paragraphs (lines that don't start with special characters)
55- . split ( '\n' )
56- . map ( line => {
57- line = line . trim ( )
58- if ( ! line ) return ''
59- if ( line . startsWith ( '<' ) ) return line // Already HTML
60- if ( line . startsWith ( '#' ) ) return line // Headers will be processed later
61- if ( line . startsWith ( '**' ) ) return line // Metadata will be processed later
62- return `<p>${ line } </p>`
63- } )
64- . join ( '\n' )
65-
66- // Clean up
67- . replace ( / \n + / g, '\n' )
68- . trim ( )
69-
43+ // Robust parser: group consecutive metadata lines into a single <dl class="dl-horizontal">
44+ const lines = content . split ( '\n' )
45+
46+ const htmlParts : string [ ] = [ ]
47+ let inDl = false
48+
49+ const closeDlIfOpen = ( ) => {
50+ if ( inDl ) {
51+ htmlParts . push ( '</dl>' )
52+ inDl = false
53+ }
54+ }
55+
56+ for ( let line of lines ) {
57+ let l = line . trim ( )
58+
59+ // Skip empty lines, but ensure we close an open DL
60+ if ( l . length === 0 ) {
61+ closeDlIfOpen ( )
62+ continue
63+ }
64+
65+ // Keep raw HTML intact
66+ if ( l . startsWith ( '<' ) ) {
67+ closeDlIfOpen ( )
68+ htmlParts . push ( l )
69+ continue
70+ }
71+
72+ // Headings
73+ if ( l . startsWith ( '#### ' ) ) {
74+ closeDlIfOpen ( )
75+ htmlParts . push ( `<h4>${ l . substring ( 5 ) } </h4>` )
76+ continue
77+ }
78+ if ( l . startsWith ( '### ' ) ) {
79+ closeDlIfOpen ( )
80+ htmlParts . push ( `<h3>${ l . substring ( 4 ) } </h3>` )
81+ continue
82+ }
83+ if ( l . startsWith ( '## ' ) ) {
84+ closeDlIfOpen ( )
85+ htmlParts . push ( `<h2>${ l . substring ( 3 ) } </h2>` )
86+ continue
87+ }
88+
89+ // Metadata lines like **Issued on::** 2004-07-27 or **Risk:** medium
90+ // Strategy: capture bold label and the value; strip trailing colons from label
91+ const metaMatch = l . match ( / ^ \* \* ( .+ ?) \* \* \s * ( .+ ) $ / )
92+ if ( metaMatch ) {
93+ let labelRaw = metaMatch [ 1 ] . trim ( )
94+ const value = metaMatch [ 2 ] . trim ( )
95+
96+ // Accept labels with one or more trailing colons inside the bold
97+ labelRaw = labelRaw . replace ( / : + \s * $ / , '' ) . trim ( )
98+
99+ if ( ! inDl ) {
100+ htmlParts . push ( '<dl class="dl-horizontal">' )
101+ inDl = true
102+ }
103+
104+ htmlParts . push ( `<dt>${ labelRaw } :</dt><dd>${ value } </dd>` )
105+ continue
106+ }
107+
108+ // Default: wrap as paragraph
109+ closeDlIfOpen ( )
110+ htmlParts . push ( `<p>${ l } </p>` )
111+ }
112+
113+ // Close any open DL at the end
114+ if ( inDl ) {
115+ htmlParts . push ( '</dl>' )
116+ }
117+
118+ const processedContent = htmlParts . join ( '\n' ) . replace ( / \n + / g, '\n' ) . trim ( )
70119 return < div dangerouslySetInnerHTML = { { __html : processedContent } } />
71120}
72121
@@ -85,7 +134,7 @@ export async function generateStaticParams() {
85134 } ) )
86135}
87136
88- export async function generateMetadata ( { params } : SecurityAdvisoryPageProps ) {
137+ export async function generateMetadata ( { params } : SecurityAdvisoryPageProps ) : Promise < Metadata > {
89138 const { advisory } = await params
90139
91140 const advisoryData = getAdvisoryContent ( advisory )
0 commit comments