|
| 1 | +import sanitize from 'sanitize-html' |
| 2 | + |
1 | 3 | export type Role = 'steward' | 'maintainer' | 'contributor' |
2 | 4 |
|
3 | 5 | export interface GitHubUserData { |
@@ -40,6 +42,24 @@ const DEFAULT_USER_INFO: GitHubUserData = { |
40 | 42 | twitterUsername: null, |
41 | 43 | } |
42 | 44 |
|
| 45 | +// Configure sanitize-html for GitHub's companyHTML and company fields |
| 46 | +const SANITIZE_HTML_OPTIONS: sanitize.IOptions = { |
| 47 | + allowedTags: ['a', 'span', 'strong', 'em', 'code'], |
| 48 | + allowedAttributes: { |
| 49 | + a: ['href', 'target', 'rel'], |
| 50 | + }, |
| 51 | + transformTags: { |
| 52 | + a: (tagName, attribs) => ({ |
| 53 | + tagName, |
| 54 | + attribs: { |
| 55 | + ...attribs, |
| 56 | + target: '_blank', |
| 57 | + rel: 'noopener noreferrer', |
| 58 | + }, |
| 59 | + }), |
| 60 | + }, |
| 61 | +} |
| 62 | + |
43 | 63 | interface TeamMembers { |
44 | 64 | steward: Set<string> |
45 | 65 | maintainer: Set<string> |
@@ -85,22 +105,14 @@ async function fetchTeamMembers(token: string): Promise<TeamMembers | null> { |
85 | 105 | } |
86 | 106 |
|
87 | 107 | /** |
88 | | - * Cleans GitHub HTML to remove tracking data and add security attributes. |
89 | | - * Specifically targets data-octo, data-hovercard, and keyboard shortcuts. |
| 108 | + * Sanitizes GitHub HTML to remove XSS vectors while preserving safe formatting. |
| 109 | + * Applies to both rich companyHTML and plain-text company fields. |
90 | 110 | */ |
91 | 111 | function sanitizeGitHubHTML(html: string | null): string | null { |
92 | 112 | if (!html) return null |
93 | 113 |
|
94 | | - return ( |
95 | | - html |
96 | | - // 1. Remove GitHub-specific tracking and metadata attributes |
97 | | - .replace(/\s(data-hovercard-[a-z-]+|data-octo-[a-z-]+|aria-keyshortcuts)="[^"]*"/gi, '') |
98 | | - // 2. Inject security and target attributes to all <a> tags |
99 | | - .replace(/<a /gi, '<a target="_blank" rel="noopener noreferrer" ') |
100 | | - // 3. Clean up any resulting double spaces |
101 | | - .replace(/\s{2,}/g, ' ') |
102 | | - .trim() |
103 | | - ) |
| 114 | + const cleaned = sanitize(html.trim(), SANITIZE_HTML_OPTIONS) |
| 115 | + return cleaned === '' ? null : cleaned |
104 | 116 | } |
105 | 117 |
|
106 | 118 | /** |
|
0 commit comments