mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
[security] replace innerHTML usages in js/ui/subscriptionHistoryController.js
- Migrated 6 out of 7 innerHTML assignments to safe DOM APIs (createElement, textContent, replaceChildren). - Reduced innerHTML baseline for the file from 7 to 1. - Updated scripts/check-innerhtml.mjs baseline. - Verified with lint, unit tests, and frontend verification. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
9e6884f6dc
commit
16de3802ab
29 files changed
+13380
-17
No files matched your search
+17
@@ -0,0 +1,17 @@
|
||||
# innerhtml-migration-agent Completion Log
|
||||
|
||||
**Date:** 2026-02-16_18-47-18
|
||||
**Agent:** innerhtml-migration-agent
|
||||
**Status:** Completed
|
||||
|
||||
## Summary
|
||||
Migrated `js/ui/subscriptionHistoryController.js` from 7 `innerHTML` usages to 1 (static template load).
|
||||
Updated baseline in `scripts/check-innerhtml.mjs`.
|
||||
|
||||
## Artifacts
|
||||
Artifacts moved to `docs/agents/task-logs/daily/artifacts/2026-02-16_innerhtml/`.
|
||||
|
||||
## Verification
|
||||
- Lint: Passed
|
||||
- Unit Tests: Passed
|
||||
- Frontend Verification: Passed (Modal opens, list renders via DOM APIs)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+2
@@ -0,0 +1,2 @@
|
||||
Target File: js/ui/subscriptionHistoryController.js
|
||||
Reason: High innerHTML count (7). Most usages are clear/reset or simple template literals that can be converted to DOM APIs. One usage is template loading which might remain.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
@@ -0,0 +1,7 @@
|
||||
- fetchHistory(): Replaced listContainer.innerHTML = "" with replaceChildren().
|
||||
- renderList(): Replaced listContainer.innerHTML = "" with replaceChildren().
|
||||
- renderList(): Replaced item.innerHTML template literal with createElement/appendChild construction. All variables (isBackup, dateStr, timeAgo) are trusted or boolean, but textContent is safer anyway.
|
||||
- selectEvent(): Replaced selectedChannelsList.innerHTML = "" with replaceChildren().
|
||||
- selectEvent(): Replaced error message innerHTML with createElement('p') and textContent.
|
||||
- renderChannels(): Replaced selectedChannelsList.innerHTML = "" with replaceChildren().
|
||||
- load(): template.innerHTML = html left as-is (static template loading from file).
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+10943
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
@@ -0,0 +1,7 @@
|
||||
- [ ] load(): template.innerHTML = html (Line 26) - Assess if replaceable or keep.
|
||||
- [ ] fetchHistory(): this.listContainer.innerHTML = "" (Line 118) -> replaceChildren() or textContent=""
|
||||
- [ ] renderList(): this.listContainer.innerHTML = "" (Line 134) -> replaceChildren() or textContent=""
|
||||
- [ ] renderList(): item.innerHTML = ... (Line 155) -> Convert to createElement/appendChild
|
||||
- [ ] selectEvent(): this.selectedChannelsList.innerHTML = "" (Line 185) -> replaceChildren()
|
||||
- [ ] selectEvent(): this.selectedChannelsList.innerHTML = error msg (Line 226) -> createElement
|
||||
- [ ] renderChannels(): this.selectedChannelsList.innerHTML = "" (Line 234) -> replaceChildren()
|
||||
@@ -0,0 +1,34 @@
|
||||
// Updated BASELINE — copy this into check-innerhtml.mjs:
|
||||
const BASELINE = {
|
||||
"js/app.js": 1,
|
||||
"js/app/feedCoordinator.js": 1,
|
||||
"js/docsView.js": 8,
|
||||
"js/exploreView.js": 1,
|
||||
"js/forYouView.js": 1,
|
||||
"js/historyView.js": 4,
|
||||
"js/index.js": 1,
|
||||
"js/kidsView.js": 1,
|
||||
"js/ui/components/DeleteModal.js": 3,
|
||||
"js/ui/components/EditModal.js": 1,
|
||||
"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,
|
||||
"js/ui/dm/Composer.js": 1,
|
||||
"js/ui/dm/ConversationList.js": 1,
|
||||
"js/ui/dm/DMRelaySettings.js": 1,
|
||||
"js/ui/dm/DMSettingsModalController.js": 3,
|
||||
"js/ui/dm/MessageThread.js": 1,
|
||||
"js/ui/dm/NotificationCenter.js": 1,
|
||||
"js/ui/loginModalController.js": 3,
|
||||
"js/ui/moreMenuController.js": 1,
|
||||
"js/ui/profileModalController.js": 1,
|
||||
"js/ui/subscriptionHistoryController.js": 1,
|
||||
"js/ui/videoListViewController.js": 1,
|
||||
"js/ui/views/VideoListView.js": 3,
|
||||
"js/utils/qrcode.js": 2,
|
||||
"js/viewManager.js": 3,
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"date": "2026-02-16",
|
||||
"locked": [
|
||||
"docs-alignment-agent",
|
||||
"docs-code-investigator"
|
||||
],
|
||||
"available": [
|
||||
"audit-agent",
|
||||
"ci-health-agent",
|
||||
"const-refactor-agent",
|
||||
"content-audit-agent",
|
||||
"decompose-agent",
|
||||
"deps-security-agent",
|
||||
"design-system-audit-agent",
|
||||
"docs-agent",
|
||||
"innerhtml-migration-agent",
|
||||
"known-issues-agent",
|
||||
"load-test-agent",
|
||||
"nip-research-agent",
|
||||
"onboarding-audit-agent",
|
||||
"perf-agent",
|
||||
"prompt-curator-agent",
|
||||
"scheduler-update-agent",
|
||||
"style-agent",
|
||||
"test-audit-agent",
|
||||
"todo-triage-agent"
|
||||
],
|
||||
"lockCount": 2,
|
||||
"locks": [
|
||||
{
|
||||
"agent": "docs-code-investigator",
|
||||
"eventId": "a981ab8bfdbdd79493078ddbe85730a20654eb2e04222d74a799be8ced382f7a",
|
||||
"createdAt": "2026-02-16T17:53:11.000Z",
|
||||
"expiresAt": "2026-02-16T19:53:11.000Z",
|
||||
"platform": "jules"
|
||||
},
|
||||
{
|
||||
"agent": "docs-alignment-agent",
|
||||
"eventId": "af2f3c4b8efb9de54f76e30afd1adc6ed42f5791bde78f731fe0c7b10586d626",
|
||||
"createdAt": "2026-02-16T16:50:36.000Z",
|
||||
"expiresAt": "2026-02-16T18:50:36.000Z",
|
||||
"platform": "jules"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
innerHTML usage: 60 assignments across 31 files
|
||||
|
||||
js/docsView.js: 8
|
||||
js/ui/subscriptionHistoryController.js: 7
|
||||
js/historyView.js: 4
|
||||
js/ui/components/DeleteModal.js: 3
|
||||
js/ui/components/ShareNostrModal.js: 3
|
||||
js/ui/dm/DMSettingsModalController.js: 3
|
||||
js/ui/loginModalController.js: 3
|
||||
js/ui/views/VideoListView.js: 3
|
||||
js/viewManager.js: 3
|
||||
js/utils/qrcode.js: 2
|
||||
js/app/feedCoordinator.js: 1
|
||||
js/app.js: 1
|
||||
js/exploreView.js: 1
|
||||
js/forYouView.js: 1
|
||||
js/index.js: 1
|
||||
js/kidsView.js: 1
|
||||
js/ui/components/EditModal.js: 1
|
||||
js/ui/components/EmbedVideoModal.js: 1
|
||||
js/ui/components/EventDetailsModal.js: 1
|
||||
js/ui/components/UploadModal.js: 1
|
||||
js/ui/components/VideoModal.js: 1
|
||||
js/ui/components/nip71FormManager.js: 1
|
||||
js/ui/dm/AppShell.js: 1
|
||||
js/ui/dm/Composer.js: 1
|
||||
js/ui/dm/ConversationList.js: 1
|
||||
js/ui/dm/DMRelaySettings.js: 1
|
||||
js/ui/dm/MessageThread.js: 1
|
||||
js/ui/dm/NotificationCenter.js: 1
|
||||
js/ui/moreMenuController.js: 1
|
||||
js/ui/profileModalController.js: 1
|
||||
js/ui/videoListViewController.js: 1
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
5278
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 193 KiB |
+40
@@ -0,0 +1,40 @@
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
def run(playwright):
|
||||
browser = playwright.chromium.launch(headless=True)
|
||||
context = browser.new_context()
|
||||
page = context.new_page()
|
||||
|
||||
# Go to app with test mode enabled
|
||||
page.goto("http://localhost:8000/?__test__=1")
|
||||
|
||||
# Wait for the app to load
|
||||
page.wait_for_load_state("networkidle")
|
||||
|
||||
# Wait for bitvidApp to be available on window
|
||||
page.wait_for_function("window.bitvidApp !== undefined")
|
||||
|
||||
# Wait for profileController to be initialized
|
||||
page.wait_for_function("window.bitvidApp.profileController !== undefined")
|
||||
page.wait_for_function("window.bitvidApp.profileController.subscriptionHistoryController !== undefined")
|
||||
|
||||
# Open the subscription history modal with a dummy pubkey
|
||||
# Use the nested controller
|
||||
page.evaluate("""
|
||||
const pubkey = 'bce1bf7313e788f8d702cfa23759cfde9d980c70a6ba839e02d4f872541b1828'; // dummy hex
|
||||
window.bitvidApp.profileController.subscriptionHistoryController.show(pubkey);
|
||||
""")
|
||||
|
||||
# Wait for modal to be visible
|
||||
page.wait_for_selector("#subscriptionHistoryModal:not(.hidden)")
|
||||
|
||||
# Wait a bit for potential fetch/render (it will show empty or error)
|
||||
page.wait_for_timeout(2000)
|
||||
|
||||
# Take screenshot of the modal
|
||||
page.screenshot(path="verification/subscription_history_modal.png")
|
||||
|
||||
browser.close()
|
||||
|
||||
with sync_playwright() as playwright:
|
||||
run(playwright)
|
||||
@@ -117,7 +117,7 @@ export class SubscriptionHistoryController {
|
||||
async fetchHistory() {
|
||||
if (!this.loadingIndicator) return;
|
||||
this.loadingIndicator.classList.remove("hidden");
|
||||
this.listContainer.innerHTML = "";
|
||||
this.listContainer.replaceChildren();
|
||||
this.emptyIndicator.classList.add("hidden");
|
||||
|
||||
try {
|
||||
@@ -133,7 +133,7 @@ export class SubscriptionHistoryController {
|
||||
}
|
||||
|
||||
renderList() {
|
||||
this.listContainer.innerHTML = "";
|
||||
this.listContainer.replaceChildren();
|
||||
if (!this.historyEvents.length) {
|
||||
this.emptyIndicator.classList.remove("hidden");
|
||||
return;
|
||||
@@ -154,17 +154,30 @@ export class SubscriptionHistoryController {
|
||||
});
|
||||
const timeAgo = formatTimeAgo(event.created_at);
|
||||
|
||||
item.innerHTML = `
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-text">${
|
||||
isBackup ? "Backup" : "Standard List"
|
||||
}</p>
|
||||
<p class="text-xs text-muted">${dateStr}</p>
|
||||
</div>
|
||||
<span class="text-xs text-muted whitespace-nowrap ml-2">${timeAgo}</span>
|
||||
</div>
|
||||
`;
|
||||
const divFlex = document.createElement("div");
|
||||
divFlex.className = "flex justify-between items-start";
|
||||
|
||||
const divLeft = document.createElement("div");
|
||||
|
||||
const pTitle = document.createElement("p");
|
||||
pTitle.className = "text-sm font-medium text-text";
|
||||
pTitle.textContent = isBackup ? "Backup" : "Standard List";
|
||||
|
||||
const pDate = document.createElement("p");
|
||||
pDate.className = "text-xs text-muted";
|
||||
pDate.textContent = dateStr;
|
||||
|
||||
divLeft.appendChild(pTitle);
|
||||
divLeft.appendChild(pDate);
|
||||
|
||||
const spanTime = document.createElement("span");
|
||||
spanTime.className = "text-xs text-muted whitespace-nowrap ml-2";
|
||||
spanTime.textContent = timeAgo;
|
||||
|
||||
divFlex.appendChild(divLeft);
|
||||
divFlex.appendChild(spanTime);
|
||||
|
||||
item.appendChild(divFlex);
|
||||
|
||||
item.addEventListener("click", () => {
|
||||
this.listContainer.querySelectorAll("li").forEach((li) => {
|
||||
@@ -184,7 +197,7 @@ export class SubscriptionHistoryController {
|
||||
this.selectedEvent = event;
|
||||
this.detailsEmpty.classList.add("hidden");
|
||||
this.detailsContainer.classList.remove("hidden");
|
||||
this.selectedChannelsList.innerHTML = "";
|
||||
this.selectedChannelsList.replaceChildren();
|
||||
this.decryptingIndicator.classList.remove("hidden");
|
||||
|
||||
const date = new Date(event.created_at * 1000);
|
||||
@@ -225,7 +238,10 @@ export class SubscriptionHistoryController {
|
||||
this.renderChannels(pubkeys);
|
||||
} catch (error) {
|
||||
userLogger.error("Failed to decrypt event:", error);
|
||||
this.selectedChannelsList.innerHTML = `<p class="text-critical text-sm text-center w-full mt-4">Decryption failed or content is invalid.</p>`;
|
||||
const errorP = document.createElement("p");
|
||||
errorP.className = "text-critical text-sm text-center w-full mt-4";
|
||||
errorP.textContent = "Decryption failed or content is invalid.";
|
||||
this.selectedChannelsList.replaceChildren(errorP);
|
||||
this.selectedMeta.textContent = "Unknown content";
|
||||
} finally {
|
||||
this.decryptingIndicator.classList.add("hidden");
|
||||
@@ -233,7 +249,7 @@ export class SubscriptionHistoryController {
|
||||
}
|
||||
|
||||
async renderChannels(pubkeys) {
|
||||
this.selectedChannelsList.innerHTML = "";
|
||||
this.selectedChannelsList.replaceChildren();
|
||||
|
||||
const app = getApplication();
|
||||
if (app && typeof app.batchFetchProfiles === "function") {
|
||||
|
||||
@@ -48,7 +48,7 @@ const BASELINE = {
|
||||
"js/ui/loginModalController.js": 3,
|
||||
"js/ui/moreMenuController.js": 1,
|
||||
"js/ui/profileModalController.js": 1,
|
||||
"js/ui/subscriptionHistoryController.js": 7,
|
||||
"js/ui/subscriptionHistoryController.js": 1,
|
||||
"js/ui/videoListViewController.js": 1,
|
||||
"js/ui/views/VideoListView.js": 3,
|
||||
"js/utils/qrcode.js": 2,
|
||||
|
||||
Reference in new issue
Block a user