[security] replace innerHTML usages in js/subscriptions.js — convert to safe DOM APIs

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-13 16:51:33 +00:00
1 parent 6aa9da1383
commit 8536f2f858
8 files changed
+113 -16

No files matched your search

@@ -0,0 +1,16 @@
# Context
- **File:** `js/subscriptions.js`
- **Reason:** Migrating `innerHTML` assignments to safe DOM APIs as part of security hardening.
- **Date:** 2026-02-13
- **Commit:** (will be added)
- **Node:** v22.22.0
- **npm:** 11.7.0
## Plan
1. Replace `innerHTML` assignments with `textContent` where possible.
2. Implement `renderStatusMessage` helper for simple messages.
3. Implement `renderLoadingIndicator` helper to replace `getSidebarLoadingMarkup`.
4. Verify changes with lint and tests.
5. Update `check-innerhtml` baseline.
@@ -0,0 +1,7 @@
# Decisions
- **File:** `js/subscriptions.js`
- **Migration:** Replaced `innerHTML` assignments with `replaceChildren()` and `document.createElement()`.
- **Helpers:** Added `_renderStatusMessage` and `_renderLoading` to `SubscriptionsManager` to encapsulate DOM creation for status and loading states.
- **Dependency:** Removed dependency on `getSidebarLoadingMarkup` (which returned a string) in this file, preferring local DOM creation.
- **Baseline:** Updated baseline for `js/subscriptions.js` from 7 to 0.
@@ -0,0 +1,8 @@
# Agent Task Log Entry
- **Date:** 2026-02-13
- **Agent:** innerhtml-migration-agent
- **Prompt:** bitvid-innerhtml-migration-agent.md
- **Status:** started
- **Branch:** chore/scheduler/innerhtml-migration-2026-02-13
- **Summary:** Task claimed — execution beginning
@@ -0,0 +1,8 @@
# Agent Task Log Entry
- **Date:** 2026-02-13
- **Agent:** innerhtml-migration-agent
- **Prompt:** bitvid-innerhtml-migration-agent.md
- **Status:** completed
- **Branch:** ai/innerhtml-fix/subscriptions
- **Summary:** Migrated 7 innerHTML assignments in js/subscriptions.js to safe DOM APIs and updated baseline
+48 -15
View File
@@ -19,7 +19,6 @@ import {
NOTE_TYPES,
} from "./nostrEventSchemas.js";
import { CACHE_POLICIES, STORAGE_TIERS } from "./nostr/cachePolicies.js";
import { getSidebarLoadingMarkup } from "./sidebarLoading.js";
import {
publishEventToRelays,
assertAnyRelayAccepted
@@ -1563,8 +1562,7 @@ class SubscriptionsManager {
const container = document.getElementById(containerId);
if (!userPubkey) {
if (container) {
container.innerHTML =
"<p class='text-muted-strong'>Please log in first.</p>";
this._renderStatusMessage(container, "Please log in first.");
}
this.lastRunOptions = null;
this.lastResult = null;
@@ -1593,8 +1591,7 @@ class SubscriptionsManager {
}
if (!channelHexes.length) {
container.innerHTML =
"<p class='text-muted-strong'>No subscriptions found.</p>";
this._renderStatusMessage(container, "No subscriptions found.");
this.lastRunOptions = {
actorPubkey: userPubkey,
limit,
@@ -1606,7 +1603,7 @@ class SubscriptionsManager {
}
if (!this.hasRenderedOnce) {
container.innerHTML = getSidebarLoadingMarkup("Fetching subscriptions…");
this._renderLoading(container, "Fetching subscriptions…");
}
this.lastRunOptions = {
@@ -1634,8 +1631,7 @@ class SubscriptionsManager {
const engine = this.getFeedEngine();
if (!engine || typeof engine.run !== "function") {
container.innerHTML =
"<p class='text-muted-strong'>Subscriptions are unavailable right now.</p>";
this._renderStatusMessage(container, "Subscriptions are unavailable right now.");
this.hasRenderedOnce = true;
return null;
}
@@ -1714,8 +1710,7 @@ class SubscriptionsManager {
reason: fallbackReason,
});
} else if (container) {
container.innerHTML =
"<p class='text-muted-strong'>Unable to load subscriptions right now.</p>";
this._renderStatusMessage(container, "Unable to load subscriptions right now.");
}
this.hasRenderedOnce = Boolean(container);
return this.lastResult;
@@ -1904,10 +1899,11 @@ class SubscriptionsManager {
const listView = this.getListView(container, app);
if (!listView) {
container.innerHTML = `
<p class="flex justify-center items-center h-full w-full text-center text-muted-strong">
Unable to render subscriptions feed.
</p>`;
this._renderStatusMessage(
container,
"Unable to render subscriptions feed.",
"flex justify-center items-center h-full w-full text-center"
);
return;
}
@@ -1942,7 +1938,7 @@ class SubscriptionsManager {
? message.trim()
: "No playable subscription videos found yet. We'll keep watching for new posts.";
container.innerHTML = getSidebarLoadingMarkup(copy, { showSpinner: false });
this._renderLoading(container, copy, false);
if (this.subscriptionListView && this.subscriptionListView.state) {
const currentMetadata =
@@ -2365,6 +2361,43 @@ class SubscriptionsManager {
convertEventToVideo(evt) {
return sharedConvertEventToVideo(evt);
}
_renderStatusMessage(container, message, extraClasses = "") {
if (!container) return;
container.replaceChildren();
const p = document.createElement("p");
p.className = `text-muted-strong ${extraClasses}`.trim();
p.textContent = message;
container.appendChild(p);
}
_renderLoading(container, message, showSpinner = true) {
if (!container) return;
container.replaceChildren();
const wrapper = document.createElement("div");
wrapper.className = "sidebar-loading-wrapper";
wrapper.setAttribute("role", "status");
wrapper.setAttribute("aria-live", "polite");
const indicator = document.createElement("div");
indicator.className = "sidebar-loading-indicator";
if (showSpinner) {
const spinner = document.createElement("span");
spinner.className = "status-spinner status-spinner--inline";
spinner.setAttribute("aria-hidden", "true");
indicator.appendChild(spinner);
}
const text = document.createElement("span");
text.className = "sidebar-loading-text";
text.textContent = message;
indicator.appendChild(text);
wrapper.appendChild(indicator);
container.appendChild(wrapper);
}
}
SubscriptionsManager.listVideoViewEvents = listVideoViewEventsApi;
-1
View File
@@ -31,7 +31,6 @@ const BASELINE = {
"js/historyView.js": 4,
"js/index.js": 1,
"js/kidsView.js": 1,
"js/subscriptions.js": 7,
"js/ui/components/DeleteModal.js": 3,
"js/ui/components/EditModal.js": 1,
"js/ui/components/EmbedVideoModal.js": 1,
@@ -0,0 +1,17 @@
# Test Log
- **Date:** 2026-02-13
- **File:** `js/subscriptions.js`
## Commands
### Lint
`npm run lint` -> Passed (after `npm ci`).
### innerHTML Audit
`node scripts/check-innerhtml.mjs --report` -> `js/subscriptions.js` removed from list (count 0).
## Verification
- Replaced 7 assignments of `innerHTML` with `_renderStatusMessage` and `_renderLoading`.
- These helpers use `replaceChildren()` and `document.createElement()`.
- Verified that `getSidebarLoadingMarkup` dependency was removed from the file.
+9
View File
@@ -0,0 +1,9 @@
# TODO - js/subscriptions.js Migration
- [ ] Line 1566: `container.innerHTML = "<p class='text-muted-strong'>Please log in first.</p>";`
- [ ] Line 1596: `container.innerHTML = "<p class='text-muted-strong'>No subscriptions found.</p>";`
- [ ] Line 1609: `container.innerHTML = getSidebarLoadingMarkup("Fetching subscriptions…");`
- [ ] Line 1637: `container.innerHTML = "<p class='text-muted-strong'>Subscriptions are unavailable right now.</p>";`
- [ ] Line 1717: `container.innerHTML = "<p class='text-muted-strong'>Unable to load subscriptions right now.</p>";`
- [ ] Line 1907: `container.innerHTML = ...` (template literal with `p` class `flex justify-center...`)
- [ ] Line 1945: `container.innerHTML = getSidebarLoadingMarkup(copy, { showSpinner: false });`