Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2026-06-25 - Prevent XSS Attribute Breakout in escHtml
**Vulnerability:** The custom HTML escaping function `escHtml` in `site/layers.js` incorrectly omitted the single quote replacement entirely. This allowed for attribute breakout vulnerabilities when inserting untrusted input into single-quoted HTML attributes.
**Learning:** All escape functions must include the single quote replacement to ensure completeness.
**Prevention:** Consistently use `.replace(/'/g, '&#039;')` (or `&#39;`) alongside replacements for `&`, `<`, `>`, and `"` in all string escaping utilities designed to prevent XSS attribute breakout. Ensure inputs are cast to `String` first.
## 2026-03-10 - Cross-Site Scripting (XSS) in Renamify Panel
**Vulnerability:** Found an XSS vulnerability in `fd-vscode/src/webview-html.ts` where untrusted node IDs (`p.oldId` and `p.newId`) from the extension's `.fd` files were directly interpolated into `row.innerHTML` in the Renamify panel.
**Learning:** Even though the source is internal extension files, untrusted input rendered directly as `innerHTML` causes an XSS injection risk.
Expand Down
4 changes: 2 additions & 2 deletions fd-vscode/webview/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1711,7 +1711,7 @@ function escapeHtml(s) {
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
.replace(/'/g, "&#039;");
}

/**
Expand All @@ -1724,7 +1724,7 @@ function escapeAttr(s) {
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
.replace(/'/g, "&#039;");
}

/** Mark the canvas as needing a re-render on the next animation frame. */
Expand Down
4 changes: 2 additions & 2 deletions fd-vscode/webview/src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function escapeHtml(s) {
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
.replace(/'/g, "&#039;");
}

/**
Expand All @@ -103,7 +103,7 @@ function escapeAttr(s) {
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
.replace(/'/g, "&#039;");
}

/** Mark the canvas as needing a re-render on the next animation frame. */
Expand Down
2 changes: 1 addition & 1 deletion site/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function initLayersPanel(api) {
path: 'γ€œ', text: 'T', style: 'β—†', edge: '⟢', note: 'β—‡', spec: 'β—‡'
};

function escHtml(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
function escHtml(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g, '&#039;'); }

/** Parse FD source into a hierarchical layer tree. */
function parseLayerTree(source) {
Expand Down
Loading