Skip to content

Commit ebaf83f

Browse files
committed
fix: XSS and potential html injection things
1 parent 1dc5c63 commit ebaf83f

File tree

2 files changed

+53
-22
lines changed

2 files changed

+53
-22
lines changed

src/lib/notificationManager.js

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sidebarApps from "sidebarApps";
2+
import DOMPurify from "dompurify";
23

34
// Singleton instance
45
let instance = null;
@@ -107,21 +108,24 @@ export default class NotificationManager {
107108
data-id={notification.id}
108109
></div>
109110
);
111+
const safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));
112+
const safeTitle = this.sanitizeText(notification.title);
113+
const safeMessage = this.sanitizeText(notification.message);
110114
element.innerHTML = `
111-
<div class="notification-icon">
112-
${this.parseIcon(notification.icon)}
113-
</div>
114-
<div class="notification-content">
115-
<div class="notification-title">
116-
${notification.title}
117-
<span class="notification-time">${this.formatTime(notification.time)}</span>
115+
<div class="notification-icon">
116+
${safeIcon}
118117
</div>
119-
<div class="notification-message">${notification.message}</div>
120-
<div class="notification-actions">
121-
<div class="action-button">Dismiss</div>
118+
<div class="notification-content">
119+
<div class="notification-title">
120+
${safeTitle}
121+
<span class="notification-time">${this.formatTime(notification.time)}</span>
122+
</div>
123+
<div class="notification-message">${safeMessage}</div>
124+
<div class="notification-actions">
125+
<div class="action-button">Dismiss</div>
126+
</div>
122127
</div>
123-
</div>
124-
`;
128+
`;
125129
if (notification.action) {
126130
element.addEventListener("click", (e) => {
127131
if (e.target.closest(".action-button")) {
@@ -140,16 +144,27 @@ export default class NotificationManager {
140144
data-id={notification.id}
141145
></div>
142146
);
147+
const safeIcon = this.sanitizeIcon(this.parseIcon(notification.icon));
148+
const safeTitle = this.sanitizeText(notification.title);
149+
const safeMessage = this.sanitizeText(notification.message);
143150
element.innerHTML = `
144-
<div class="notification-icon">${this.parseIcon(notification.icon)}</div>
145-
<div class="notification-content">
146-
<div class="notification-title">
147-
${notification.title}
151+
<div class="notification-icon">${safeIcon}</div>
152+
<div class="notification-content">
153+
<div class="notification-title">
154+
${safeTitle}
155+
</div>
156+
<div class="notification-message">${safeMessage}</div>
148157
</div>
149-
<div class="notification-message">${notification.message}</div>
150-
</div>
151-
${notification.autoClose ? "" : `<span class="close-icon icon clearclose" onclick="event.stopPropagation(); this.closest('.notification-toast').remove();"></span>`}
152-
`;
158+
${notification.autoClose ? "" : `<span class="close-icon icon clearclose"></span>`}
159+
`;
160+
161+
const closeIcon = element.querySelector(".close-icon");
162+
if (closeIcon) {
163+
closeIcon.addEventListener("click", (event) => {
164+
event.stopPropagation();
165+
element.remove();
166+
});
167+
}
153168
if (notification.action) {
154169
element.addEventListener("click", () =>
155170
notification.action(notification),
@@ -202,13 +217,27 @@ export default class NotificationManager {
202217
}
203218

204219
parseIcon(icon) {
205-
if (!icon) return this.DEFAULT_ICON;
220+
if (typeof icon !== "string" || !icon) return this.DEFAULT_ICON;
206221
if (icon.startsWith("<svg")) return icon;
207222
if (icon.startsWith("data:") || icon.startsWith("http"))
208223
return `<img src="${icon}" alt="notification" width="16" height="16">`;
209224
return `<i class="icon ${icon}"></i>`;
210225
}
211226

227+
sanitizeText(text) {
228+
return DOMPurify.sanitize(String(text ?? ""), {
229+
ALLOWED_TAGS: [],
230+
ALLOWED_ATTR: [],
231+
});
232+
}
233+
234+
sanitizeIcon(iconMarkup) {
235+
return DOMPurify.sanitize(iconMarkup, {
236+
USE_PROFILES: { html: true, svg: true },
237+
ALLOW_DATA_ATTR: false,
238+
});
239+
}
240+
212241
formatTime(date) {
213242
const now = new Date();
214243
const diff = Math.floor((now - date) / 1000);

src/pages/changelog/changelog.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import fsOperation from "fileSystem";
33
import Contextmenu from "components/contextmenu";
44
import Page from "components/page";
55
import toast from "components/toast";
6+
import DOMPurify from "dompurify";
67
import Ref from "html-tag-js/ref";
78
import actionStack from "lib/actionStack";
89
import markdownIt from "markdown-it";
@@ -164,7 +165,8 @@ export default async function Changelog() {
164165

165166
md.use(markdownItTaskLists);
166167
md.use(markdownItFootnote);
167-
body.innerHTML = md.render(processedText);
168+
const renderedHtml = md.render(processedText);
169+
body.innerHTML = DOMPurify.sanitize(renderedHtml);
168170
}
169171

170172
function updateVersionSelector() {

0 commit comments

Comments
 (0)