feat(books): select-all-visible + deselect-all controls in action bar (bookshelf-pts) #205
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-pts"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
selectAllVisible()action tobooks_select_controller.jsthat iteratesrowCheckboxTargets, adds each ID+title to the set, persists to sessionStorage, and re-rendersTest plan
e2e/browser/books_select_all_test.gocovers all the aboveCloses bead bookshelf-pts on merge.
Security Review — bookshelf-pts (select-all-visible)
Scope: template + JS only (3 files, no new endpoints). Confirmed.
XSS — book title flow
Server → template:
{{.Title}}indata-book-title="{{.Title}}"is Go'shtml/template, which HTML-attribute-escapes the value at render time. Safe.data attribute → JS:
cb.dataset.bookTitlereads the (already-escaped) string. It is written tothis._titles[id](a plain JS object) and serialised into sessionStorage viaJSON.stringify. Never passed toinnerHTML,insertAdjacentHTML, or a template literal that builds markup.sessionStorage → DOM: The only write-back to the DOM is
countLabelTarget.textContent = selected + " selected"— a numeric count, not the title. Titles stored in sessionStorage are not rendered back to HTML anywhere in this diff.No XSS path introduced.
CSP
New template lines (
books_index.html): a single<button>withdata-action="click->books-select#selectAllVisible". No inline<script>, noonXxxattributes, nojavascript:URIs. Clean.sessionStorage DoS
Titles are appended individually during
selectAllVisible(and the existing toggle/restore paths). All items on the current visible page are added in one call. The existing_saveToStoragealready catchesQuotaExceededErrorand degrades gracefully. The new method calls_saveToStorage()the same way — no new unbounded write path. No change to risk posture.localStorage
No
localStorageusage introduced. OnlysessionStorage(pre-existing pattern).Template change
The added
<button>emits a ✓ literal character (U+2713). This is a text node, not markup, so no escaping concern. Thearia-labelandtitleattributes are static strings.REVIEW VERDICT: 0 blocker, 0 major, 0 minor
CODE REVIEW: APPROVED
Phase 0: DEMO Verification
No explicit DEMO block in the bead. The bead's deliverable #4 is 'e2e browser tests confirming selection state across page navigation' — CI is green at SHA
dc4220e42bd61834ccc6e94532fdc1cd88265001, meaning the 5 e2e specs ine2e/browser/books_select_all_test.goran and passed in CI (ubuntu-latest, MySQL service container). This is accepted as equivalent verification.Phase 1: Spec Compliance
Bead deliverables checked against diff:
Phase 2: Code Quality
No BLOCKER or MAJOR findings.
[MINOR] static/js/controllers/books_select_controller.js — selectAllVisible() uses function(cb) {...}, this callback style while the rest of the controller uses arrow functions. Works correctly (forEach thisArg binding is valid), but inconsistent with the surrounding style.
[MINOR] templates/pages/books_index.html — the new button uses a unicode checkmark (✓) as its visible label. The pre-existing clear button also uses unicode (✕). These are visually consistent with each other; minor nit only.
XSS check: data-book-title={{.Title}} is rendered by html/template which HTML-attribute-escapes the value. The title is read back via cb.dataset.bookTitle into this._titles (a plain JS object stored as JSON in sessionStorage) and only surfaced via countLabelTarget.textContent (line 299). No innerHTML anywhere in the controller. No XSS vector.
Aria: aria-label='Select all visible books' present on the new button. Good.
Test structure: Each It block has exactly one Expect. Consistent with project conventions.
REVIEW VERDICT: 0 blocker, 0 major, 2 minor
dc4220e42b685dfc0a21