Skip to content

Commit dc6c5d1

Browse files
committed
strip html leak from search
1 parent 4a78821 commit dc6c5d1

1 file changed

Lines changed: 18 additions & 5 deletions

File tree

src/components/Search.jsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ import styles from './Search.module.css';
55

66
const allModules = import.meta.glob('/src/content/**/*.md', { query: '?raw', import: 'default' });
77

8+
function stripMarkdown(text) {
9+
return text
10+
.replace(/<[^>]+>/g, ' ') // HTML tags
11+
.replace(/#+\s+/g, '') // headings
12+
.replace(/[*_~`]+/g, '') // bold/italic/code markers
13+
.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') // [text](url) → text
14+
.replace(/!\[[^\]]*\]\([^)]*\)/g, '') // images
15+
.replace(/&[a-z]+;/gi, ' ') // HTML entities
16+
.replace(/\s+/g, ' ')
17+
.trim();
18+
}
19+
820
function getTitle(content, path) {
921
const firstLine = content.split('\n').find(l => l.startsWith('# '));
1022
if (firstLine) return firstLine.replace(/^#\s+/, '');
@@ -13,10 +25,11 @@ function getTitle(content, path) {
1325
}
1426

1527
function getSnippet(content, query) {
16-
const lower = content.toLowerCase();
28+
const clean = stripMarkdown(content);
29+
const lower = clean.toLowerCase();
1730
const idx = lower.indexOf(query.toLowerCase());
18-
const raw = idx === -1 ? content.slice(0, 120) : content.slice(Math.max(0, idx - 40), idx + query.length + 80);
19-
return (idx > 40 ? '…' : '') + raw.replace(/#+\s/g, '').replace(/\n+/g, ' ') + '…';
31+
const raw = idx === -1 ? clean.slice(0, 120) : clean.slice(Math.max(0, idx - 40), idx + query.length + 80);
32+
return (idx > 40 ? '…' : '') + raw + '…';
2033
}
2134

2235
function pathToRoute(path) {
@@ -47,7 +60,7 @@ const Search = ({ onClose }) => {
4760
const entries = await Promise.all(
4861
Object.entries(allModules).map(async ([path, loader]) => {
4962
const content = await loader();
50-
return { path, content, title: getTitle(content, path), route: pathToRoute(path) };
63+
return { path, content, clean: stripMarkdown(content), title: getTitle(content, path), route: pathToRoute(path) };
5164
})
5265
);
5366
setLoading(false);
@@ -61,7 +74,7 @@ const Search = ({ onClose }) => {
6174
const entries = await buildIndex();
6275
const q = query.toLowerCase();
6376
const matched = entries
64-
.filter(e => e.title.toLowerCase().includes(q) || e.content.toLowerCase().includes(q))
77+
.filter(e => e.title.toLowerCase().includes(q) || e.clean.toLowerCase().includes(q))
6578
.slice(0, 7)
6679
.map(e => ({ title: e.title, route: e.route, snippet: getSnippet(e.content, query) }));
6780
setResults(matched);

0 commit comments

Comments
 (0)