mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
[security] replace innerHTML usages in js/ui/components/ShareNostrModal.js — convert to safe DOM APIs
- Replace template `innerHTML` with `DOMParser` - Replace SVG `innerHTML` with `createElementNS` - Replace `innerHTML = ""` with `replaceChildren()` - Update `check-innerhtml` baseline Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
83247a0754
commit
f9f23c0d08
6 files changed
+105
-5
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# Context: innerHTML Migration for ShareNostrModal.js
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Agent:** bitvid-innerhtml-migration-agent (via Jules)
|
||||
**Target File:** `js/ui/components/ShareNostrModal.js`
|
||||
**Reason:** High count (3 assignments). Contains static SVG injection and template loading.
|
||||
|
||||
**Environment:**
|
||||
- Node: v22.22.0
|
||||
- npm: 11.7.0
|
||||
|
||||
**Plan:**
|
||||
1. Analyze `innerHTML` assignments.
|
||||
2. Replace `wrapper.innerHTML = html` with `DOMParser` or similar safe parsing.
|
||||
3. Replace `this.relayPills.innerHTML = ""` with `replaceChildren()`.
|
||||
4. Replace `removeButton.innerHTML = SVG` with `createElementNS` or safe SVG construction.
|
||||
5. Verify with lint and tests.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Decisions: innerHTML Migration for ShareNostrModal.js
|
||||
|
||||
## 1. Template Loading (L107)
|
||||
**Original:** `wrapper.innerHTML = html;`
|
||||
**New:**
|
||||
```javascript
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
wrapper.replaceChildren(...doc.body.childNodes);
|
||||
```
|
||||
**Rationale:** Avoids innerHTML while parsing the fetched HTML string safely.
|
||||
|
||||
## 2. Clearing Content (L274)
|
||||
**Original:** `this.relayPills.innerHTML = "";`
|
||||
**New:** `this.relayPills.replaceChildren();`
|
||||
**Rationale:** Modern, faster, and safer method to clear DOM nodes.
|
||||
|
||||
## 3. SVG Icon (L300)
|
||||
**Original:** `removeButton.innerHTML = '<svg ...>...</svg>';`
|
||||
**New:**
|
||||
```javascript
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("class", "h-3 w-3");
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("fill", "none");
|
||||
svg.setAttribute("stroke", "currentColor");
|
||||
svg.setAttribute("stroke-width", "2");
|
||||
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", "M18 6L6 18M6 6l12 12");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-linejoin", "round");
|
||||
|
||||
svg.appendChild(path);
|
||||
removeButton.appendChild(svg);
|
||||
```
|
||||
**Rationale:** Using `createElementNS` is the correct way to build SVG elements without `innerHTML`.
|
||||
@@ -0,0 +1,24 @@
|
||||
# innerHTML Migration Task: ShareNostrModal.js
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Agent:** innerhtml-migration-agent
|
||||
**Status:** Completed
|
||||
|
||||
## Migrated File
|
||||
* `js/ui/components/ShareNostrModal.js` (3 innerHTML assignments migrated)
|
||||
|
||||
## Changes
|
||||
1. **Template Loading:** Replaced `wrapper.innerHTML = html` with `DOMParser` logic.
|
||||
2. **Clearing Content:** Replaced `this.relayPills.innerHTML = ""` with `replaceChildren()`.
|
||||
3. **SVG Icon:** Replaced `removeButton.innerHTML = SVG` with `createElementNS`.
|
||||
|
||||
## Verification
|
||||
* `npm run lint` passed.
|
||||
* `npm run test:unit` passed.
|
||||
* `node scripts/check-innerhtml.mjs --report` confirmed count reduction (3 -> 0 for this file).
|
||||
* `scripts/check-innerhtml.mjs` baseline updated.
|
||||
|
||||
## Artifacts
|
||||
* `context/CONTEXT_2026-02-20_innerhtml.md`
|
||||
* `todo/TODO_2026-02-20_innerhtml.md`
|
||||
* `decisions/DECISIONS_2026-02-20_innerhtml.md`
|
||||
@@ -107,7 +107,9 @@ export class ShareNostrModal {
|
||||
|
||||
const html = await response.text();
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.innerHTML = html;
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, "text/html");
|
||||
wrapper.replaceChildren(...Array.from(doc.body.childNodes));
|
||||
this.removeTrackingScripts(wrapper);
|
||||
targetContainer.appendChild(wrapper);
|
||||
modal = wrapper.querySelector("#shareNostrModal");
|
||||
@@ -273,7 +275,7 @@ export class ShareNostrModal {
|
||||
if (!this.relayPills) {
|
||||
return;
|
||||
}
|
||||
this.relayPills.innerHTML = "";
|
||||
this.relayPills.replaceChildren();
|
||||
if (!this.currentRelays.length) {
|
||||
const placeholder = document.createElement("span");
|
||||
placeholder.className = "text-xs text-muted";
|
||||
@@ -299,8 +301,21 @@ export class ShareNostrModal {
|
||||
removeButton.setAttribute("aria-label", `Remove ${relay}`);
|
||||
removeButton.dataset.action = "remove-relay";
|
||||
removeButton.dataset.relay = relay;
|
||||
removeButton.innerHTML =
|
||||
'<svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||||
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("class", "h-3 w-3");
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("fill", "none");
|
||||
svg.setAttribute("stroke", "currentColor");
|
||||
svg.setAttribute("stroke-width", "2");
|
||||
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", "M18 6L6 18M6 6l12 12");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-linejoin", "round");
|
||||
|
||||
svg.appendChild(path);
|
||||
removeButton.appendChild(svg);
|
||||
pill.appendChild(removeButton);
|
||||
|
||||
this.relayPills.appendChild(pill);
|
||||
|
||||
@@ -33,7 +33,6 @@ const BASELINE = {
|
||||
"js/ui/components/EmbedVideoModal.js": 1,
|
||||
"js/ui/components/EventDetailsModal.js": 1,
|
||||
"js/ui/components/nip71FormManager.js": 1,
|
||||
"js/ui/components/ShareNostrModal.js": 3,
|
||||
"js/ui/components/UploadModal.js": 1,
|
||||
"js/ui/components/VideoModal.js": 1,
|
||||
"js/ui/dm/AppShell.js": 1,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# TODO: innerHTML Migration for ShareNostrModal.js
|
||||
|
||||
- [ ] L107: `wrapper.innerHTML = html;` (Template loading)
|
||||
- Strategy: Use `new DOMParser().parseFromString(html, 'text/html')` and append children to `wrapper`.
|
||||
- [ ] L274: `this.relayPills.innerHTML = "";` (Clearing content)
|
||||
- Strategy: `this.relayPills.replaceChildren();` or `textContent = ""` (replaceChildren is preferred for performance).
|
||||
- [ ] L300: `removeButton.innerHTML = '<svg ...>...</svg>';` (SVG Icon)
|
||||
- Strategy: Create SVG elements using `document.createElementNS("http://www.w3.org/2000/svg", "svg")` and children.
|
||||
Reference in new issue
Block a user