Skip to content

imrpoved maintance page#143

Open
vvshk wants to merge 15 commits into
devfrom
improvement/pagination
Open

imrpoved maintance page#143
vvshk wants to merge 15 commits into
devfrom
improvement/pagination

Conversation

@vvshk

@vvshk vvshk commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@claude

claude Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 39s —— View job


Code Review — PR #143: improved maintenance page

Reviewed the diff against origin/dev. This is a substantial, well-structured upgrade to the maintenance page: server-side pagination, search, sorting, status-filter tabs, an inline edit modal, relative timestamps, WhatsApp links, skeleton loaders, and Excel export. Nice work overall — the code is readable and the URL-param syncing is a thoughtful touch. Below are the issues I found, grouped by severity.


🔴 High

1. Excel export likely breaks against the real (paginated) APImaintenance.js (exportToExcel)
fetchMaintenance reads the new paginated shape:

const requests = data.data.requests || data.data;

but exportToExcel reads the old shape:

const resData = await response.json();
records = resData.data || [];   // .data is now { requests, pagination, statusCounts }

If the backend returns the paginated object, records becomes a non-array object and records.map(...) throws → caught → "An error occurred while exporting requests." Please confirm what the fetch endpoint returns without page_size, and reuse the same data.data.requests || data.data unwrapping here.
Fix this →

2. DOM/reflected XSS via searchQuery and DB contentmaintenance.js (populateTable, highlightText, truncateText)
User/URL-controlled and DB values are injected into innerHTML without HTML-escaping:

matching "${searchQuery}"   // empty-state, searchQuery comes from the URL `search` param
highlightText(m.area_of_work, searchQuery)   // returns raw text, no escaping
truncateText(m.work_detail, searchQuery)

highlightText does String(text).replace(regex, '<mark>$1</mark>') with no entity-encoding, so any </>/" in the data or in ?search=... renders as live HTML. The raw-innerHTML pattern existed in the old code, but the new reflected searchQuery path makes this easy to trigger. Escape text before highlighting (e.g. an escapeHtml() helper applied to textStr and searchQuery) and escape searchQuery in the empty-state template.


🟡 Medium

3. status query param is not URL-encodedmaintenance.js (fetchMaintenance, exportToExcel)

...&status=${activeStatusFilter}

The status value in progress contains a space. search is correctly wrapped with encodeURIComponent, but status (and sort_by/sort_order) are not. Wrap activeStatusFilter in encodeURIComponent in both the fetch and export URLs.

4. Unpinned third-party CDN with no SRImaintenance.html:9

<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>

xlsx-latest means the bundle can change without notice, and there is no integrity/crossorigin attribute. Pin a specific version and add an SRI hash so a compromised/changed CDN asset can't execute arbitrary script in an authenticated admin context. Also note XLSX is assumed global in exportToExcel with no guard — if the CDN fails to load, the click throws a ReferenceError.

5. Go-To-page <select> builds one <option> per pagemaintenance.js (renderPaginationmakeSelectOptions)
For large datasets this generates thousands of options in two dropdowns on every fetch. Consider a number input + "Go" button, or capping the dropdown, for scalability.


🟢 Minor / Nits

  • Duplicated 45-row mock generator in fetchMaintenance and exportToExcel — extract a single generateMockData() helper to avoid drift.
  • Possible out-of-order responses: rapid sort/page/search clicks fire overlapping fetchMaintenance() calls with no request cancellation; a slow earlier response can overwrite a newer one. An AbortController or a request-sequence guard would harden this.
  • alert() for validation/errors (modal save, export) is consistent with existing code but a non-blocking toast would be nicer UX.
  • document keydown listener for ←/→ is global and never removed — fine for a full page load, just noting it.
  • Confirm the backend actually supports priority as a sort_by value (the default), since it's a synthesized order in the mock but must map to something server-side.

No blocking concerns on the HTML structure — element IDs line up with the JS lookups, and the null-safe ?. usage on CardDb is a good defensive improvement over the old m.CardDb.issuedto.

Items 1 and 2 are worth addressing before merge. Let me know if you'd like me to implement any of these fixes.

@claude

claude Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 28s —— View job


Code Review — PR #143: improved maintenance page (re-review after changes as per claude suggestion)

Re-reviewed maintenance.js / maintenance.html against origin/dev. Most of the previous feedback was addressed cleanly — nice turnaround. One High item from the prior review is still open, plus a couple of minor follow-ups. Details below.

✅ Fixed since last review

  • Shreyas | Adhyayan Changes | Create | List All | Update - Adhyayan #1 Export response shapeexportToExcel now unwraps resData.data.requests (maintenance.js:852), matching fetchMaintenance. 👍
  • Dev #3 URL-encodingstatus, sort_by, sort_order are now wrapped in encodeURIComponent in both fetch and export (maintenance.js:240, :842). 👍
  • changed ' to ` in api path #4 CDN/SRI — Pinned to xlsx/0.18.5 with an integrity hash + crossorigin (maintenance.html:23), and exportToExcel now guards typeof XLSX === 'undefined' (maintenance.js:797). 👍
  • Maintenance #5 Page navigation — Replaced the per-page <select> with a bounded numeric "Go to" input (maintenance.html:536, :582; maintenance.js:554-580). 👍

🔴 Still open (High)

#2 DOM/reflected XSS via searchQuery and DB contentmaintenance.js
This was flagged last round and is not yet fixed. highlightText still injects raw text into innerHTML:

// maintenance.js:47-54
const highlightText = (text, query) => {
  if (!text) return '';
  const textStr = String(text);          // never HTML-escaped
  if (!query || !query.trim()) return textStr;
  ...
  return textStr.replace(regex, '<mark class="highlight">$1</mark>');
};

highlightText / truncateText feed row.innerHTML for issuedto, department, area_of_work, work_detail, comments, and the WhatsApp display text (maintenance.js:350-365). A request whose work_detail is e.g. <img src=x onerror=alert(document.cookie)> executes in an authenticated admin session. The empty-state is also a reflected sink — ?search=... lands raw in innerHTML:

// maintenance.js:281
We couldn't find any requests matching "${searchQuery}". ...

Fix: add an escapeHtml() helper and escape textStr before inserting <mark> in highlightText, escape query everywhere it's interpolated, and escape searchQuery in the empty-state template.
Fix this →


🟡 Medium / worth confirming

A. Export omits page_size to fetch "all" rowsmaintenance.js:842
The export URL intentionally drops page=/page_size= to get the full filtered set. Please confirm the backend returns all matching rows when page_size is absent (rather than defaulting to one page) — otherwise the Excel export silently exports only the first page.

B. Excel formula injection in exportmaintenance.js:861-872
DB-controlled fields (work_detail, comments, area_of_work, issuedto) are written verbatim to cells. A value beginning with =, +, -, or @ is interpreted as a formula when the .xlsx is opened. Consider prefixing such values with a ' (or sanitizing leading formula chars) before building the rows.


🟢 Minor / nits (carried over, non-blocking)

  • Duplicated 45-row mock generator in fetchMaintenance (:148-162) and exportToExcel (:811-825) — extract a shared generateMockData() to avoid drift.
  • No request cancellation — rapid sort/search/page clicks fire overlapping fetchMaintenance() calls; a slow earlier response can still overwrite a newer one. An AbortController or sequence guard would harden this.
  • fetch error handling in fetchMaintenance (:252-256) logs to console but leaves the skeleton in place — the user sees a perpetual shimmer on failure. A small inline error/empty state would be friendlier.
  • Confirm the backend accepts priority as a sort_by value (the default at :17).

Summary: Items #1, #3, #4, #5 are resolved. The #2 XSS finding remains and is the one blocker I'd want addressed before merge; A and B are worth a quick confirm/fix. HTML/JS structure and ID wiring still look consistent. Let me know if you'd like me to implement the escapeHtml fix.

@claude

claude Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 47s —— View job


Code Review — PR #143 (re-review after pagination in gate reports)

Reviewed the latest commit (gate-report pagination across gateReport, totalGuest, totalMumukshu, totalPR, totalSeva) plus the still-open items from the maintenance review, against origin/dev. The gate pages got the same solid pagination/search/sort/export treatment as maintenance — consistent structure, good ?. null-safety, encoded query params, and an XLSX guard before export. The two findings below are the ones I'd address before merge; everything else is carried-over minor.


🔴 High

#2 (still open) — DOM/reflected XSS in maintenanceadmin/maintenance/maintenance.js
Unchanged since the last two reviews. highlightText still injects un-escaped text into innerHTML:

// maintenance.js:47-54
const highlightText = (text, query) => {
  if (!text) return '';
  const textStr = String(text);            // never HTML-escaped
  ...
  return textStr.replace(regex, '<mark class="highlight">$1</mark>');
};

This feeds row.innerHTML for issuedto, department, area_of_work, work_detail, comments and the WhatsApp text (:350-365, :87). The empty-state is also a reflected sink — ?search= lands raw in innerHTML (:281). A work_detail of <img src=x onerror=...> executes in an authenticated admin session.
Fix: add an escapeHtml() helper; escape textStr before inserting <mark>, escape query/searchQuery everywhere they're interpolated (:53, :281, renderWhatsAppLink).
Fix this →

NEW — same XSS class now in the gate-report pagesadmin/gate/*.js
The new code injects DB-controlled fields straight into innerHTML with no escaping:

// gateReport.js:213-220
row.innerHTML = `
  <td>${record.cardno}</td>
  <td>${name}</td>          // record.issuedto / CardDb.issuedto
  <td>${mobno}</td>
  <td>${record.status}</td>
  ...`;
// totalGuest.js:149-157
<td>${resident.issuedto}</td>
<td>${resident.mobno}</td>
<td><button ...>View History</button></td>  // attribute injection too

Same pattern in totalMumukshu.js, totalPR.js, totalSeva.js and the history-table rows (updatedBy, etc.). If any of issuedto/mobno/updatedBy can contain HTML/quotes, this is stored XSS. The data-name="${...issuedto}" case can also break out of the attribute on a name containing ".
Fix: route these values through the same escapeHtml() helper (or build cells with textContent/createElement instead of template innerHTML). Worth factoring escapeHtml into a shared util since all six files need it.
Fix this →


🟡 Medium / worth confirming (carried over, now applies to gate pages too)

A. Export drops page_size to fetch "all" rowsgateReport.js:371, maintenance.js:842
The export URL intentionally omits page/page_size. Please confirm the backend returns the full filtered set when page_size is absent (and not just a default first page) — otherwise the Excel export silently exports only one page.

B. Excel formula injection in exportgateReport.js:388-398, maintenance.js
DB fields (name, etc.) are written verbatim to cells (aoa_to_sheet). A value starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Prefix such values with ' (or strip a leading formula char) when building the rows.


🟢 Minor / nits (non-blocking, carried)

  • Duplicated mock generators in fetchMaintenance / exportToExcel — extract one helper to avoid drift.
  • No request cancellation — rapid sort/search/page clicks fire overlapping fetches; a slow earlier response can overwrite a newer one. An AbortController/sequence guard would harden this (applies to gate pages too).
  • Perpetual skeleton on fetch errorfetchMaintenance logs to console but leaves the shimmer up; gate pages alert() then leave the loader hidden — a small inline error/empty state would be friendlier.
  • Confirm the backend accepts priority as the default sort_by in maintenance.

Summary: Previously fixed (#1, #3, #4, #5) remain fixed. The XSS finding #2 is still open, and the same un-escaped-innerHTML pattern was added to all five gate-report files — that's the blocker I'd want resolved before merge. A and B are quick confirm/fixes. HTML↔JS ID wiring and pagination logic look consistent across the new pages. Happy to implement the escapeHtml fix across maintenance + gate if you'd like.

@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 37s —— View job


Code Review — PR #143 (re-review after imrpoved ui of gate reports)

The latest commit (imrpoved ui of gate reports) is a UI-polish pass — it does not touch the security findings raised in the previous three reviews. Status of each below.

Still open (High) — XSS, unchanged across maintenance + gate pages

#2 — DOM / reflected XSS in maintenance (admin/maintenance/maintenance.js)
Still un-escaped. highlightText (:47-54) injects raw text into innerHTML and never HTML-escapes textStr. This feeds row.innerHTML for issuedto, department, area_of_work, work_detail, comments, and the WhatsApp display text (:350-365, :87). The empty-state is also a reflected sink — ?search=… lands raw in innerHTML (:281). A work_detail of <img src=x onerror=alert(document.cookie)> executes in an authenticated admin session.

Same pattern in the gate pages (gateReport.js, totalGuest.js, totalMumukshu.js, totalPR.js, totalSeva.js)
displayGateRecords builds rows with raw interpolation — e.g. gateReport.js:218-225 injects record.cardno, name (issuedto), mobno directly into row.innerHTML. The totalGuest/Mumukshu/PR/Seva files do the same, including data-name="${...issuedto}" attributes that a name containing a " can break out of. If any of these DB fields can hold HTML/quotes, this is stored XSS.

Good news — the fix pattern already exists in this repo. admin/adhyayan/adhyayanAttendanceReport.js:401 defines an escapeHtml(str) helper and uses it for exactly this (${escapeHtml(row.name)}, etc.). Mirror that:

  • In maintenance.js: escape textStr before inserting <mark> in highlightText, escape query/searchQuery wherever interpolated (:53, :281, renderWhatsAppLink).
  • In the five gate files: route every DB field (cardno, issuedto/name, mobno, status, updatedBy, and the data-name attribute value) through escapeHtml before it reaches innerHTML.
  • Since all six files need it, factor escapeHtml into a shared util rather than copy-pasting.

Fix this →

Medium / worth confirming (carried over)

A. Export drops page_size to fetch "all" rowsmaintenance.js:842, gateReport.js export. The export URL intentionally omits page/page_size. Please confirm the backend returns the full filtered set when page_size is absent (and not just a default first page) — otherwise the export silently emits only one page.

B. Excel formula injectionmaintenance.js:861-872 and the gate exports write DB fields verbatim to cells via aoa_to_sheet. A value starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Prefix such values with ' (or strip the leading formula char) when building rows.

Minor / nits (non-blocking, carried)

  • Duplicated 45-row mock generator in fetchMaintenance (:148-162) and exportToExcel (:811-825) — extract one helper to avoid drift.
  • No request cancellation — rapid sort/search/page clicks fire overlapping fetches; a slow earlier response can overwrite a newer one. An AbortController/sequence guard would harden this (applies to gate pages too).
  • Perpetual skeleton on fetch errorfetchMaintenance (:252-256) logs to console but leaves the shimmer up on failure; a small inline error state would be friendlier.
  • Confirm the backend accepts priority as the default sort_by (maintenance.js:17).

Summary: Previously fixed items (#1, #3, #4, #5) remain fixed, and the gate-page pagination/UI work looks consistent (ID↔JS wiring, ?. null-safety, encoded params, XLSX guard). The XSS finding is still the one blocker — and the same un-escaped-innerHTML pattern is present in all five gate files. A reusable escapeHtml already exists at adhyayanAttendanceReport.js:401, so the fix is low-risk. Items A and B are quick confirm/fixes. Happy to implement the escapeHtml rollout across maintenance + gate if you'd like — just say the word.

@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 45s —— View job


Code Review — PR #143 (re-review after gate-report consolidation)

Reviewed the two new commits (consolidated report of gate for all statuses) against origin/dev. This is a nice consolidation: the four totalGuest / totalMumukshu / totalPR / totalSeva pages are replaced by a single residents.html + residents.js with a category-tab filter, and onPremise.js / index.html now route to it (residents.html?type=..., residents.html?status=onprem) with encodeURIComponent on the link param. Less duplication, consistent pagination/sort/search/export — good structural improvement.

The carried-over security blocker is still the headline, and the new consolidated file inherits it.


🔴 High — XSS still open across maintenance + gate (now including the new residents.js)

The escapeHtml helper already exists in the repo (admin/adhyayan/adhyayanAttendanceReport.js:401) but is still not used in any maintenance/gate file — confirmed by grep, it's the only file that references it.

admin/gate/residents.js (new) — DB fields go straight into innerHTML:

// residents.js:236-246
row.innerHTML = `
  <td>${resident.cardno}</td>
  <td>${resident.issuedto}</td>
  <td>${resident.mobno}</td>
  ...
  <td><button class="view-history-btn">🕒 History</button></td>`;

An issuedto containing " breaks out of the data-name attribute; one containing <img src=x onerror=...> executes. The history modal table has the same issue with record.updatedBy (:379). (The modal title correctly uses textContent at :312 — good; mirror that pattern for the cells.)

admin/maintenance/maintenance.js — unchanged since the first review. highlightText (:47-54) never escapes textStr before inserting <mark>, and it feeds row.innerHTML for issuedto, department, area_of_work, work_detail, comments, and the WhatsApp text (:350-358, :87). The empty-state is still a reflected sink — ?search=… lands raw in innerHTML (:281).

admin/gate/gateReport.jsdisplayGateRecords still injects record.cardno / name / mobno raw (:218-224).

Fix: factor escapeHtml into a shared util (all three files need it), then:

  • residents.js: escape cardno, issuedto, mobno, updatedBy, and the data-name value (or build cells via textContent).
  • maintenance.js: escape textStr before inserting <mark> in highlightText, and escape searchQuery in the empty-state at :281.
  • gateReport.js: escape cardno / name / mobno before they reach innerHTML.

Fix this →


🟡 Medium / worth confirming (carried over)

A. Export drops page_size to fetch "all" rowsresidents.js:544, gateReport.js, maintenance.js:842. The export URL omits page/page_size. Please confirm the backend returns the full filtered set when page_size is absent — otherwise the Excel export silently emits only the first page.

B. Excel formula injectionresidents.js:561-571 (and the maintenance/gate exports) write DB fields verbatim into cells via aoa_to_sheet. A value starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Prefix such values with ' (or strip the leading formula char) when building rows.


🟢 Minor / nits (non-blocking)

  • residents.js export status mapping: the table uses String(resident.status).toUpperCase() === 'ONPREM' (:224), but the export compares the raw lowercase r.status === 'onprem' (:567). If the API returns ONPREM, every exported row will read "Off Premise". Worth aligning the two (normalize with .toUpperCase() in both).
  • No request cancellation — rapid sort/search/page clicks fire overlapping fetches; a slow earlier response can overwrite a newer one. An AbortController/sequence guard would harden fetchResidentsReport / fetchMaintenance / fetchGateReport.
  • Silent skeleton/loader on fetch errorfetchResidentsReport (:206-209) logs to console only; a small inline error/empty state would be friendlier than a blank table.
  • Duplicated mock generator in maintenance (fetchMaintenance / exportToExcel) — extract one helper to avoid drift.
  • Confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The gate consolidation is a clean, well-structured refactor and the previously-resolved items (#1, #3, #4, #5) remain fixed. The XSS finding is still the one blocker — and it now also lives in the new residents.js. Since escapeHtml already exists in the repo, rolling it across maintenance.js, gateReport.js, and residents.js is low-risk. Item B and the residents.js status-mapping nit are quick fixes; A is a backend confirm. Happy to implement the escapeHtml rollout if you'd like — just say the word.

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 38s —— View job


Code Review — PR #143

  • Gather context (diff against origin/dev)
  • Review maintenance page changes
  • Review gate report / residents changes
  • Check status of previously-raised findings
  • Post review feedback

@claude

claude Bot commented Jun 19, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 51s —— View job


Code Review — PR #143 (re-review after wifi pagination and ui imrovement)

  • Gather context (diff against origin/dev)
  • Review new wifi pagination changes
  • Check status of previously-raised findings
  • Post review feedback

Reviewed the latest commit, which brings the same pagination / search / sort / status-tab / Excel-export treatment to the four wifi pages (permanentCodeRequests, wifiReport, uploadCodes, plus index/landing). Consistent with the maintenance + gate work — clean structure, URL-param syncing, debounced search, ?. null-safety, keyboard nav, and a thoughtful client-side approach where the backend has no pagination. One nice touch: uploadCodes.js displayTable correctly uses innerText for the Excel preview (:167, :176) — exactly the safe pattern. The headline blocker is unchanged: the un-escaped-innerHTML XSS pattern is still open everywhere, and the new wifi pages inherit it.


🔴 High — XSS still open across maintenance + gate, now also in both new wifi pages

Confirmed by grep: escapeHtml still lives only in admin/adhyayan/adhyayanAttendanceReport.js:401 and is used by no maintenance/gate/wifi file.

admin/wifi/permanentCodeRequests.js (new)highlightText (:514-524) never escapes its input, and displayRequests injects DB fields straight into row.innerHTML:

// :469-485
<td>${highlightText(req.cardno, searchQuery)}</td>
<td>${highlightText(req.CardDb?.issuedto || '-', searchQuery)}</td>
<td>${renderWhatsAppLink(req.CardDb?.mobno || '-', searchQuery)}</td>
<td>${highlightText(req.CardDb?.email || '-', searchQuery)}</td>
... username, ssid, code ...

An issuedto/code/ssid of <img src=x onerror=alert(document.cookie)> executes in an authenticated admin session. There's an extra sink here: showUploadResult (:1404-1517) renders Excel-upload / dry-run results raw into innerHTMLdata.error, err.error, ch.cardno, ch.changes[field].old/new (:1453-1455), item.cardno, item.username, item.code, item.ssid, item.status (:1481-1484). Since that content comes from an uploaded .xlsx, a crafted spreadsheet is a stored-XSS vector.

admin/wifi/wifiReport.js (new) — same highlightText (:481-491), and displayGateRecords injects record.password, record.issuedto, record.mobno raw (:443-452).

admin/maintenance/maintenance.js — unchanged: highlightText (:47-54) feeds row.innerHTML for issuedto, department, area_of_work, work_detail, comments and the empty-state reflects ?search=… raw (:281).

admin/gate/gateReport.js / admin/gate/residents.js — still inject cardno / issuedto / mobno / updatedBy and the data-name="${…}" attribute raw.

Fix: factor escapeHtml into a shared util (e.g. alongside style/js/exportToExcel.js) and use it in every innerHTML sink — escape textStr before inserting <mark> inside highlightText, escape search/searchQuery wherever interpolated, and escape the upload-result fields in showUploadResult. The repo already has the helper to copy.
Fix this →


🟡 Medium / worth confirming

B. Excel formula injection (carried)style/js/exportToExcel.js:19 (json_to_sheet) writes every value verbatim, and the wifi exports pass DB-controlled fields through it (password, issuedto, username, ssid, code in permanentCodeRequests.js:998-1011 and wifiReport.js:797-810). A value starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Since downloadExcelFromJSON is the shared export path, sanitizing leading formula chars there fixes it for every page at once.

C. Whole-dataset fetch on the wifi pageswifiReport.js:28 and permanentCodeRequests.js:266 fetch the full record set once and paginate/search/sort entirely client-side (applyFilters). That's a reasonable choice given "server-side pagination is not supported," and it sidesteps the export-page_size concern (the export uses the full filtered set, not one page 👍). Just flagging that memory/initial-load cost grows with the table — fine for now, worth revisiting if these tables get large.


🟢 Minor / nits (non-blocking)

  • A. (maintenance/gate only) Export still drops page/page_size to fetch "all" rows on the server-side pages — please confirm the backend returns the full filtered set when page_size is absent.
  • No request cancellation on the server-side pages (maintenance/gate) — rapid sort/search/page clicks fire overlapping fetches; an AbortController/sequence guard would harden them. (Not an issue on the wifi pages, which filter in memory.)
  • alert() for errors/validation across the wifi flows is consistent with existing code; a non-blocking toast would be nicer UX.
  • showStatusAlert / upload summaries interpolate result.message / err.message into innerHTML (uploadCodes.js:255-260) — low risk as these are app/HTTP strings, but textContent would be safer.
  • Confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The wifi pagination work is well-structured and consistent with the rest of the PR, and uploadCodes.js already uses the safe innerText pattern. Previously-resolved items (#1, #3, #4, #5) remain fixed. The un-escaped-innerHTML XSS is still the one blocker — now present in permanentCodeRequests.js (including the upload-result modal, a stored-XSS path) and wifiReport.js, on top of the still-open maintenance/gate sinks. Since escapeHtml already exists in the repo, a shared-util rollout is low-risk. Item B is best fixed once in exportToExcel.js. Happy to implement the escapeHtml rollout if you'd like — just say the word.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 25s —— View job


Code Review — PR #143 (re-review after made it responsive for phone screen)

  • Gather context (diff against origin/dev)
  • Check status of previously-raised findings (esp. XSS)
  • Review latest changes
  • Post review feedback

The latest commit (5c5f2b1) is a responsive-UI pass — it swaps the gate resStatusSelect dropdown for .category-tab buttons (gateReport.js:212-230), adds phone-screen layout/CSS across the maintenance, gate, and wifi HTML, and syncs the active tab from URL params on load. The tab-wiring is clean (data-category lookups, reset-filters re-activates the "All" tab) and the breakpoints look reasonable. It does not touch any of the security findings from the previous reviews.


🔴 High — XSS still open (the one blocker), unchanged

Confirmed by grep: escapeHtml still lives only in admin/adhyayan/adhyayanAttendanceReport.js and is used by no maintenance/gate/wifi file.

  • admin/maintenance/maintenance.jshighlightText (:47-54) still inserts raw textStr into the <mark> template with no escaping, feeding row.innerHTML for issuedto, department, area_of_work, work_detail, comments. The empty-state remains a reflected sink — ?search=… lands raw in innerHTML (:281).
  • admin/gate/gateReport.jsdisplayGateRecords still injects record.cardno / name (issuedto) / mobno raw into row.innerHTML.
  • admin/gate/residents.jscardno, issuedto, mobno, updatedBy and the data-name="${…}" attribute go in raw (an issuedto containing " breaks out of the attribute; <img src=x onerror=…> executes).
  • admin/wifi/permanentCodeRequests.jshighlightText + displayRequests inject DB fields raw, and showUploadResult renders uploaded-.xlsx content (ch.cardno, item.username, item.code, item.ssid, …) into innerHTML — a stored-XSS path via a crafted spreadsheet.
  • admin/wifi/wifiReport.js — same highlightText + raw password / issuedto / mobno cells.

A work_detail / issuedto of <img src=x onerror=alert(document.cookie)> executes in an authenticated admin session. The repo already has the helper to copy.

Fix: factor escapeHtml (from adhyayanAttendanceReport.js:401) into a shared util (e.g. alongside style/js/exportToExcel.js), then escape textStr before inserting <mark> in every highlightText, escape search/searchQuery wherever interpolated, escape the upload-result fields in showUploadResult, and escape (or build via textContent) every DB field/data-name attribute in the gate/residents/wifi row builders.
Fix this →


🟡 Medium / worth confirming (carried over)

  • B. Excel formula injectionstyle/js/exportToExcel.js (json_to_sheet/aoa_to_sheet) writes DB-controlled values verbatim; a cell starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Since this is the shared export path, prefixing leading formula chars with ' there fixes every page at once.
  • A. Export drops page/page_size on the server-side pages (maintenance.js:842, gateReport.js export) to fetch "all" rows — please confirm the backend returns the full filtered set when page_size is absent, else the export silently emits only the first page. (The wifi pages filter in memory, so they're unaffected.)

🟢 Minor / nits (non-blocking, carried)

  • No request cancellation on the server-side pages — rapid sort/search/page clicks fire overlapping fetches; an AbortController/sequence guard would harden them.
  • Silent loader/skeleton on fetch errorfetchMaintenance / fetchGateReport / fetchResidentsReport log to console and leave the shimmer/blank table up; a small inline error state would be friendlier.
  • showStatusAlert / upload summaries interpolate result.message / err.message into innerHTML (uploadCodes.js:255-260) — low risk (app/HTTP strings), but textContent is safer.
  • Confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The responsive-UI commit is a clean, isolated change (category-tab swap + phone layout) with no regressions to the pagination/sort/search wiring, and previously-resolved items (#1, #3, #4, #5) remain fixed. The un-escaped-innerHTML XSS is still the one blocker and is unchanged across maintenance.js, gateReport.js, residents.js, permanentCodeRequests.js (incl. the upload-result modal), and wifiReport.js. Since escapeHtml already exists in the repo, a shared-util rollout is low-risk. Item B is best fixed once in exportToExcel.js. Happy to implement the escapeHtml rollout if you'd like — just say the word.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 22s —— View job


Code Review — PR #143 (re-review after avt mgmt page improvement)

  • Gather context (diff against origin/dev)
  • Review new AVT management page changes
  • Check status of previously-raised findings (esp. XSS)
  • Post review feedback

The latest commit (3c04044) brings the same search / highlight / sort / client-side residence-filter / responsive-card treatment to the card management page (admin/avt/index.js, index.html). It's a solid upgrade — debounced search, /-to-focus and Esc-to-clear keyboard shortcuts, a click-to-zoom photo overlay with backdrop/Esc dismissal, graceful avatar fallback via onerror, and a clean empty state. Nicely, several cells are built with textContent (the badge text :239-251, the card-no fallback :227, the mobile location :452) — exactly the safe pattern. But the same un-escaped-innerHTML XSS that's been the blocker through every prior review is present here too, on top of the still-open maintenance/gate/wifi sinks.


🔴 High — XSS still open (the one blocker), now also in the AVT page

Confirmed by grep: escapeHtml still lives only in admin/adhyayan/adhyayanAttendanceReport.js and is used by no maintenance/gate/wifi/avt file.

admin/avt/index.js (new this commit)highlightText (:120-129) builds …<mark>${matchedText}</mark>… from raw, un-escaped substrings, and its output is assigned via innerHTML for the name (:219), card number (:225), email (:275, :509), WhatsApp display text (renderWhatsAppLink, :542-546), and the mobile-card name/card-no (:431, :462). The location subtext is also interpolated raw into innerHTML (:217item.city / item.center are DB-controlled). The empty state is a reflected sink too — the search query lands raw in innerHTML (:143):

We couldn't find any card records matching "${query}". ...

An issuedto / city of <img src=x onerror=alert(document.cookie)>, or a crafted ?-driven search term, executes in an authenticated admin session.

Still open from prior reviews (unchanged):

  • maintenance.jshighlightText (:47-54) never escapes textStr before the <mark>; feeds row.innerHTML for issuedto, department, area_of_work, work_detail, comments; empty-state reflects ?search=… raw (:281).
  • gateReport.js / residents.jscardno, issuedto, mobno, updatedBy, and the data-name="${…}" attribute go in raw (an issuedto with a " breaks out of the attribute).
  • permanentCodeRequests.jshighlightText + displayRequests inject DB fields raw, and showUploadResult renders uploaded-.xlsx content into innerHTML (a stored-XSS path).
  • wifiReport.jshighlightText + raw password / issuedto / mobno cells.

Fix: factor escapeHtml (from adhyayanAttendanceReport.js:401) into a shared util (e.g. alongside style/js/exportToExcel.js), then in highlightText escape textStr and query before building the <mark>, escape query/searchQuery in every empty-state template, and escape the location subtext / upload-result / gate-row DB fields wherever they reach innerHTML.
Fix this →


🟡 Medium / worth confirming (carried over)

  • B. Excel formula injectionstyle/js/exportToExcel.js (json_to_sheet/aoa_to_sheet) writes DB-controlled values verbatim; a cell starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Since this is the shared export path, prefixing leading formula chars with ' there fixes every page at once. (The AVT page has no export, so it's unaffected.)
  • A. Export drops page/page_size on the server-side pages (maintenance.js:842, gateReport.js export) to fetch "all" rows — please confirm the backend returns the full filtered set when page_size is absent.

🟢 Minor / nits (non-blocking)

  • admin/avt/index.js leftover console.log at :160 (Rendering ${fetchedData.length} records…) — harmless but worth removing before merge.
  • AVT search is whole-dataset, sort/filter in memory (:78-117, :592) — consistent with the wifi pages and fine for current sizes; just flagging the in-memory cost grows with the table.
  • No request cancellation on the server-side pages (maintenance/gate) — rapid sort/search/page clicks fire overlapping fetches; an AbortController/sequence guard would harden them.
  • Silent loader/blank table on fetch error across the fetch helpers — a small inline error state would be friendlier than the dimmed/empty table.
  • Confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The AVT management page is a clean, consistent addition (good textContent usage in several cells, nice keyboard/overlay UX) and the previously-resolved items (#1, #3, #4, #5) remain fixed. The un-escaped-innerHTML XSS is still the one blocker — it now also lives in admin/avt/index.js (incl. the reflected empty-state and DB-driven location subtext), alongside the still-open maintenance.js, gateReport.js, residents.js, permanentCodeRequests.js, and wifiReport.js sinks. Since escapeHtml already exists in the repo, a shared-util rollout is low-risk. Item B is best fixed once in exportToExcel.js. Happy to implement the escapeHtml rollout if you'd like — just say the word.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 1m 44s —— View job


Code Review — PR #143 (re-review after admin page improvement)

  • Gather context (diff against origin/dev)
  • Review new admin/sudo page changes
  • Check status of previously-raised findings (esp. XSS)
  • Post review feedback

The two latest commits (fc02543, 36e2c43) are a substantial, well-structured consolidation of the sudo admin pages: the six separate activateAdmin / deactivateAdmin / createAdmin / createRole / fetchRoles / updateAdminRoles pages are collapsed into a single admin/sudo/index.html + fetchAllAdmins.js dashboard with a Users/Roles tab switch, status-filter tabs, search-highlight, sortable headers, inline edit-roles / reset-password / create / assign / bulk-action modals, a card-number autocomplete with live CardDb validation, a password-strength meter + generator, and a responsive mobile-card layout. Lots of good defensive work here — IDs, statuses, badge labels, and role-checkbox labels are built with textContent (e.g. :359, :388, :471, :862, :1168), which is exactly the safe pattern. The headline blocker is the same one carried through every prior review, and the new file inherits it.


🔴 High — XSS still open (the one blocker), now also in the new fetchAllAdmins.js

Confirmed by grep: escapeHtml still lives only in admin/adhyayan/adhyayanAttendanceReport.js and is used by no maintenance / gate / wifi / avt / sudo file.

admin/sudo/fetchAllAdmins.js (new this commit)highlightText (:251-260) builds …<mark>${matchedText}</mark>… from raw, un-escaped substrings, and its output is assigned via innerHTML for the username (:364), card number (:369, :480), each role badge (:378, :490), and the mobile-card username (:466) and role description (:605). All of these are DB-controlled (admin.username, admin.cardno, role_name). The empty state is also a reflected sink — the search query lands raw in innerHTML (:643):

We couldn't find any results matching "${query}". ...

Two more raw-innerHTML sinks unique to this page, both fed by CardDb:

// card autocomplete — :1555
div.innerHTML = `<strong>${card.issuedto}</strong> (Card: ${card.cardno}) <span ...>- ${card.center || 'N/A'}</span>`;
// card validation badge — :1501 / :1504
validationDiv.innerHTML = `⚠️ Assigned to administrator: "${alreadyLinked.username}"`;
validationDiv.innerHTML = `✅ Validated: ${match.issuedto} (${match.center || 'No Center'})`;

An issuedto / username / center of <img src=x onerror=alert(document.cookie)> executes in an authenticated superAdmin session — the highest-privilege context in the app, which makes this sink particularly worth closing.

Still open from prior reviews (unchanged): maintenance.js (highlightText :47-54, empty-state :281), gateReport.js / residents.js (cardno / issuedto / mobno / updatedBy / data-name attribute), permanentCodeRequests.js (highlightText, displayRequests, and the uploaded-.xlsx-driven showUploadResult), wifiReport.js (highlightText + raw cells), and avt/index.js (highlightText, empty-state, location subtext).

Fix: factor escapeHtml (from adhyayanAttendanceReport.js:401) into a shared util (e.g. alongside style/js/exportToExcel.js), then in every highlightText escape textStr and query before building the <mark>, escape query/searchQuery in each empty-state template, and escape the autocomplete + validation-badge fields here (:1501, :1504, :1555).
Fix this →


🟡 Medium / worth confirming (carried over)

  • B. Excel formula injectionstyle/js/exportToExcel.js (json_to_sheet/aoa_to_sheet) writes DB-controlled values verbatim; a cell starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Since this is the shared export path, prefixing leading formula chars with ' there fixes every export page at once. (The sudo page has no export, so it's unaffected.)
  • A. Export drops page/page_size on the server-side pages (maintenance.js:842, gateReport.js export) — please confirm the backend returns the full filtered set when page_size is absent.

🟢 Minor / nits (non-blocking)

  • fetchAllAdmins.js — no loading skeleton/error state on fetch failure: loadInitialData (:244-247) logs to console and restores opacity, leaving an empty table with no message if /sudo/fetch_all_admins or /sudo/role fails. A small inline error state would be friendlier.
  • role-check-${roleName} element IDs (:854, :945, :1722) embed the raw role name into an id/for attribute. Not an injection risk (set via property, and getElementById isn't used on them), but a role name with spaces/quotes would make the label[for]↔checkbox association brittle — minor.
  • Sort mutates fetchedAdmins in place (:750) so the sort order persists across searches/filters — fine, just noting it's intentional state.
  • alert() / confirm() throughout the sudo flows is consistent with the existing codebase; non-blocking toasts/dialogs would be nicer UX.
  • Carried: no request cancellation on the server-side pages; confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The sudo-page consolidation is a clean, well-organized refactor (six pages → one dashboard) with genuinely good textContent usage in most cells, and the previously-resolved items (#1, #3, #4, #5) remain fixed. The un-escaped-innerHTML XSS is still the one blocker — it now also lives in fetchAllAdmins.js (the search-highlight cells, the reflected empty-state, and the CardDb-driven autocomplete + validation badge), executing in the highest-privilege superAdmin context, on top of the still-open maintenance / gate / wifi / avt sinks. Since escapeHtml already exists in the repo, a shared-util rollout is low-risk. Item B is best fixed once in exportToExcel.js. Happy to implement the escapeHtml rollout across all affected pages if you'd like — just say the word.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 2m 43s —— View job


Code Review — PR #143 (re-review after more admin changes)

  • Gather context (diff against origin/dev)
  • Review latest changes (053dcb7)
  • Check status of previously-raised findings (esp. XSS)
  • Post review feedback

The latest commit (053dcb7, more admin changes) is another solid pass on the sudo dashboard (admin/sudo/fetchAllAdmins.js, index.html, plus a one-line index.js change that stores username in sessionStorage). Genuinely nice additions: a getLoggedInUsername() self-protection guard so a superAdmin can't deactivate/delete their own account (🔒 Self/🔒 Delete), interactive statistics cards + dynamic count badges (all built with textContent — the safe pattern 👍), a promise-based custom confirm modal replacing the native confirm(), migration from alert() to inline showSuccessMessage/showErrorMessage banners, and an Excel export for the Users/Roles tabs. But this commit adds two new innerHTML XSS sinks on top of the still-open carried ones, and brings the sudo page into scope for the export-formula-injection item.


🔴 High — XSS, including two new sinks added in this commit

escapeHtml still lives only in admin/adhyayan/adhyayanAttendanceReport.js and is used by no maintenance / gate / wifi / avt / sudo file (confirmed by grep).

NEW — confirm-modal message is innerHTML with a DB-controlled usernamefetchAllAdmins.js
showConfirmModal assigns the message via innerHTML:

confirmMessage.innerHTML = message;   // showConfirmModal

and every caller interpolates a DB-controlled value into that message:

const confirmText = `Are you sure you want to ${action} admin user "${admin.username}"?`;       // toggleAdminStatus
const confirmText = `Are you sure you want to permanently delete administrator "${username}"? ...`; // triggerDeleteAdmin
const confirmText = `Are you sure you want to delete the role "${roleName}"?`;                    // triggerDeleteRole

An admin.username / roleName of <img src=x onerror=alert(document.cookie)> executes the moment a superAdmin opens the deactivate/delete dialog — in the highest-privilege context in the app.

NEW — success/error banners are innerHTML with the username interpolatedstyle/js/custom.js:93-103

function showSuccessMessage(message) { ... alert.innerHTML = message; ... }
function showErrorMessage(message)   { ... alert.innerHTML = message; ... }

The new callers now interpolate admin.username into these:

showSuccessMessage(`Admin user "${admin.username}" has been ${action}d successfully.`);

Same stored-XSS payload fires on the success path. (data.message from the API is lower-risk but flows through the same sink.)

Still open from prior reviews (unchanged):

  • fetchAllAdmins.jshighlightText (:389-397) injects raw matchedText into <mark> for username (:582), cardno (:587), role badges (:596, :729, :849) and the mobile card (:705, :719); reflected empty-state at :899 (...matching "${query}"); plus the CardDb-driven autocomplete + validation badge from the prior review.
  • maintenance.jshighlightText (:47-54), reflected empty-state (:281).
  • gateReport.js / residents.js — raw cardno/issuedto/mobno/updatedBy and data-name="${…}".
  • permanentCodeRequests.jshighlightText, displayRequests, and the uploaded-.xlsx-driven showUploadResult.
  • wifiReport.jshighlightText + raw cells.
  • avt/index.jshighlightText, empty-state, location subtext.

Fix: factor escapeHtml (from adhyayanAttendanceReport.js:401) into a shared util (e.g. alongside style/js/exportToExcel.js), then: escape admin.username/roleName/username before building confirmText (or set confirmMessage.textContent and only mark up the static wrapper); escape interpolated values before passing to showSuccessMessage/showErrorMessage (or switch those helpers to textContent); escape textStr/query in every highlightText; and escape query/searchQuery in each empty-state.
Fix this →


🟡 Medium / worth confirming (carried over)

  • B. Excel formula injection — now also on the sudo exportstyle/js/exportToExcel.js:18 (json_to_sheet) writes every value verbatim, and the new sudo export passes DB-controlled fields through it (admin.username, admin.cardno, role names, getRoleDescription(role) at fetchAllAdmins.js:1705-1712, :1755-1760). A cell starting with =, +, -, or @ is treated as a formula when the .xlsx opens. Since downloadExcelFromJSON is the shared export path, prefixing leading formula chars with ' there fixes every export page (maintenance, gate, wifi, sudo) at once.
  • A. Export drops page/page_size on the server-side pages (maintenance.js:842, gateReport.js) — please confirm the backend returns the full filtered set when page_size is absent. (Sudo filters in memory, so it's unaffected.)

🟢 Minor / nits (non-blocking)

  • getLoggedInUsername JWT decode (fetchAllAdmins.js) atob-decodes the token payload purely to read a display/self-guard username — fine, but note the self-protection is a client-side convenience only; the backend must still reject self-deactivation/-deletion independently.
  • Sudo export has no XLSX-undefined guard at the call site (the maintenance/wifi exports added typeof XLSX === 'undefined' checks). If the CDN fails to load, the export click throws inside downloadExcelFromJSON; a guard there would cover all callers.
  • Carried: no request cancellation on the server-side pages; silent loader/blank table on fetch error across the fetch helpers; confirm the backend accepts priority as the default sort_by in maintenance (:17).

Summary: The self-protection guard, stats cards, custom confirm modal, and inline banners are nice, well-built UX improvements, and previously-resolved items (#1, #3, #4, #5) remain fixed. But this commit adds two new XSS sinks — the confirm-modal message and the success/error banners, both now interpolating a DB-controlled username into innerHTML in the superAdmin context — alongside the still-open highlightText/empty-state sinks across maintenance.js, gateReport.js, residents.js, permanentCodeRequests.js, wifiReport.js, avt/index.js, and fetchAllAdmins.js. The un-escaped-innerHTML XSS remains the one blocker, and the new sudo export brings item B into scope here too. Since escapeHtml already exists in the repo, a shared-util rollout is low-risk, and item B is best fixed once in exportToExcel.js. Happy to implement the escapeHtml rollout across all affected pages if you'd like — just say the word.

@claude

claude Bot commented Jun 20, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 52s —— View job


Code Review — PR #143

  • Gather context (diff against origin/dev)
  • Check status of previously-raised findings (esp. XSS)
  • Review latest changes
  • Post review feedback

@claude

claude Bot commented Jun 21, 2026

Copy link
Copy Markdown

Claude finished @vvshk's task in 56s —— View job


Code Review — PR #143

  • Gather context (diff against origin/dev)
  • Check status of previously-raised findings (esp. XSS)
  • Review latest changes
  • Post review feedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants