@@ -17,11 +17,32 @@ import * as marked from "marked";
1717import * as markedGfmHeadingId from "marked-gfm-heading-id" ;
1818import * as shiki from "shiki" ;
1919
20+ import sanitizeHtml from "sanitize-html" ;
21+
2022// Utility for reading files to strings.
2123function readFile ( path ) {
2224 return fs . readFileSync ( path , { encoding : "utf-8" } ) ;
2325}
2426
27+ // Utility for escaping HTML tags.
28+ function escapeHtml ( text ) {
29+ return text
30+ . replaceAll ( "&" , "&" )
31+ . replaceAll ( "<" , "<" )
32+ . replaceAll ( ">" , ">" )
33+ . replaceAll ( "\"" , """ )
34+ . replaceAll ( "'" , "'" ) ;
35+ }
36+
37+ // Utility for removing HTML tags.
38+ function removeHtml ( text ) {
39+ return sanitizeHtml ( text , {
40+ allowedTags : [ ] ,
41+ allowedAttributes : { } ,
42+ disallowedTagsMode : "discard"
43+ } ) ;
44+ } ;
45+
2546// Fetch arguments.
2647const args = process . argv . slice ( 2 ) ;
2748
@@ -67,11 +88,14 @@ let mainHtml = await marked.parse(readFile(input), {
6788 return ;
6889 }
6990
91+ // Escape token text.
92+ const tokenText = escapeHtml ( token . text ) ;
93+
7094 // Absolute link; make it open in a new tab.
7195 if ( / ^ [ a - z ] + : \/ \/ / i. test ( token . href ) ) {
7296 token . type = "html" ;
7397 token . text = /* HTML */ `
74- <a href="${ token . href } " target="_blank">${ token . text } </a>
98+ <a href="${ token . href } " target="_blank">${ tokenText } </a>
7599 ` . trim ( ) ;
76100
77101 return ;
@@ -105,7 +129,7 @@ let mainHtml = await marked.parse(readFile(input), {
105129
106130 token . type = "html" ;
107131 token . text = /* HTML */ `
108- <a href="${ href } " target="_blank">${ token . text } </a>
132+ <a href="${ href } " target="_blank">${ tokenText } </a>
109133 ` . trim ( ) ;
110134
111135 return ;
@@ -177,7 +201,7 @@ let tocHtml = "";
177201for ( const heading of headings ) {
178202 tocHtml += /* HTML */ `
179203 <a href="#${ heading . id } " style="margin-left: ${ heading . level - 1 } em">
180- - ${ heading . text }
204+ - ${ removeHtml ( heading . text ) }
181205 </a>
182206 <br>
183207 ` ;
@@ -230,14 +254,14 @@ for (const entry of nav) {
230254let navHtml = "" ;
231255
232256for ( const [ _ , category ] of Object . entries ( categories ) ) {
233- navHtml += /* HTML */ `<h2>${ category . name } </h2>` ;
257+ navHtml += /* HTML */ `<h2>${ escapeHtml ( category . name ) } </h2>` ;
234258
235259 for ( const file of category . files ) {
236260 // Current file, highlighted in bold.
237261 if ( input === file . path ) {
238262 navHtml += /* HTML */ `
239263 <li id="dei-currentpage">
240- <b>${ file . title } </b>
264+ <b>${ removeHtml ( file . title ) } </b>
241265 <br>
242266 </li>
243267 ` ;
@@ -246,7 +270,7 @@ for (const [_, category] of Object.entries(categories)) {
246270 else {
247271 navHtml += /* HTML */ `
248272 <li>
249- <a href="${ file . href } ">${ file . title } </a>
273+ <a href="${ file . href } ">${ removeHtml ( file . title ) } </a>
250274 <br>
251275 </li>
252276 ` ;
0 commit comments