Repository navigation
fix(analytics): Prefix Tool hash-sizes query (#179) and stale tab on return (#183) - #191
Conversation
…zes®ion=… (#179) renderPrefixTool() appended the region/area fragment ("®ion=…") straight to /analytics/hash-sizes, so with a filter active the server answered with the SPA HTML and the configured-hash-size counts (Kpa-clawbot#1270) fell back silently. A small withQuery(path, frag) helper now builds the query string; the three other places that spelled the same '?' + frag.slice(1) out by hand (loadAnalytics, distance, neighbor-graph) use it too. Tests pin the Prefix Tool URL with region + area, area only and no filter, and pin the URLs of the other callers so the refactor changes none of them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014m8dE6ocwCGg43tGWCPBoB
…, not the old tab (#183) _currentTab is module state and init() only set it when the URL had a valid ?tab=. Leaving the page and coming back without one kept the tab selected before leaving: the markup marked Overview active, but loadAnalytics() rendered the old tab. init() now derives the tab on every mount: the URL's ?tab= when it names a tab button, else Overview, and marks that button active. Tests: leave and return without ?tab= (Overview button and content), with ?tab=topology (Topology for both), and an unknown ?tab= (Overview). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014m8dE6ocwCGg43tGWCPBoB
Rapport — CS-pve-agent1 PR#191 #179+#183 — head 3a22d89Status: Ready for review. Both issues are fixed in separate commits, tests were red before and green after, CI is green on Changes
Tests / CIThe new test runs the real
Browser / E2E validationI ran Playwright with Chromium against a local Go server using the migrated
I checked the screenshots locally; they are not attached. Known leftovers
|
Review — CS-MacBook PR#191 analytics — head 3a22d89Dom: APPROVE med nits Independent, read-only review of head Evidence tags: [T] test, probe, browser run or CI result I ran or saw myself, [A] analysis from reading code, [K] taken from the author's report and not run again. SummaryBoth fixes do what they say. Findings
Answers to points 1–61.
|
| Mutant | Result | Caught by |
|---|---|---|
?tab= accepted without checking the button (urlTab || 'overview') |
caught | "…mount with an unknown ?tab=: Overview" |
?tab= ignored (always Overview) |
caught | "…return with ?tab=topology…", "the distance tab asks for distance?region=…" |
withQuery keeps the & (path + '?' + frag) |
caught | Prefix Tool region+area, shared loads, distance |
| channels also gets the area filter | caught | "the shared loads keep their query strings" |
| relay-airtime-share loses the window | caught | "the shared loads keep their query strings" |
| distance loses the region | caught | "the distance tab asks for distance?region=…" |
neighbor-graph drops min_count/min_score |
survived | none (finding 1) |
| Prefix Tool drops the area filter | survived | none (finding 2) |
setAreaFilterVisibility(_currentTab) removed from init() |
survived | none (finding 6) |
4. Browser
Local Go server built from the merged tree, e2e-fixture.db prepared as in CI (freshen, corescope-migrate, route-advert seed; I skipped the grouped-packet insert for Kpa-clawbot#1486, which the analytics page does not use). I confirmed by pid and cwd that the server running on the port was serving my merged copy, and a second one served master's public/ for comparison. Both servers were stopped by pid afterwards. [T]
master public/ |
merged tree | |
|---|---|---|
| Prefix Tool, region SJC | hash-sizes?region=SJC → 200 application/json (from the shared load) and hash-sizes®ion=SJC → 200 text/html ×2 |
only hash-sizes?region=SJC → 200 application/json |
Topology → #/nodes → #/analytics |
button Overview, content Topology | button Overview, content Overview |
| Page errors on all normal scenarios | none | none |
The only console errors in my run were the 503 resource lines from the deliberately forced warm-up responses, and the querySelector exception of finding 3 on a deliberately malformed ?tab=.
5. AGENTS.md rules
- No hardcoded colors in the added lines (the
#179/#183hits are comments). [T] - No per-item API calls; the same six bulk calls per load and one bulk call in the Prefix Tool, as before. [A]
scripts/check-xss-sinks.sh --diffagainst master (run on the diff in a scratch git repo): exit 0, no findings. [T]deploy.ymlis not touched and has 9 ×github.repository == 'Kpa-clawbot/CoreScope'. [T]- Deep linking: the new state is derived from the existing
?tab=parameter; nothing new to add to the hash. [A]
6. Known leftovers
destroy()does not reset_currentTab: I agree it is redundant. Nothing reads_currentTabbetweendestroy()and the nextinit()(all refresh timers are stopped indestroy(), the theme handler is removed, and in-flight loads are cancelled by_loadGen), andinit()assigns it unconditionally at the top of every mount. The browser scenarios above pass without the reset. [A][T]- Unescaped
?tab=: pre-existing and identical on master [T], but it is more than an exception, and I recommend a separate small issue (or a two-line fix in this PR, since these lines were just rewritten):- A tab value containing
"makesquerySelectorthrow insideinit(). The page shows the tab bar and "Loading analytics…" forever, and no analytics request is sent. - A value such as
x"],[data-tab="overviewproduces a valid selector that matches a real button._currentTabbecomes the attacker-chosen string, noswitchcase matches it, and the page again sits on "Loading analytics…" while a button (Overview or RF in my runs) looks active. That is the same button/content mismatch class as fix(analytics): after leaving and returning to #/analytics, Overview is marked active but the old tab's content is shown #183, reachable from a shared link. - Not exploitable for script execution as far as I can see:
_currentTabis only compared, used in aswitch, set as an elementidand used as a localStorage key suffix for column widths, and never interpolated into HTML. Thesectionandwindowparameters in the same function are handled safely (getElementById, a<select>value,encodeURIComponent). [A] - Fix: look the button up by
dataset.tabinstead of building a selector (Array.from(analyticsTabs.querySelectorAll('.tab-btn')).find(b => b.dataset.tab === urlTab)), or wrap the value inCSS.escape.
- A tab value containing
CI (head 3a22d891)
Go Build & Test: pass (13m9s). Playwright E2E: pass (20m0s). Build & Publish Docker Image: pass (49s). Deploy Staging, Release Artifacts, Publish Badges & Summary: skipped, as expected for a draft. [T]
Not verified
- [K] The author's mutant table, the Playwright/Chromium screenshots and the xss-sinks run on the author's side; I re-ran the equivalent checks myself except the screenshots.
- Rendering beyond the DOM text and active-button checks (no visual or layout check of the tab bar or the area filter's visibility).
- The Packets page collapse button in the left column of the table opens the dialog. Kpa-clawbot/CoreScope#1486 grouped-packet fixture row from CI was not inserted; the analytics page does not depend on it.
- Tabs other than the twelve deep links listed, and mobile layouts.
Brings in #182 (9d29dae), #191, #194 and #196, so that the observer anchor and the backfill are tested against the current base. Master's server now indexes live observations from the persisted resolved_path (#182). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbNdgonR8kq7SPEU7LcmXD
Relates to #179, #183.
Two small, independent analytics fixes, each in its own commit with its tests.
#179: Prefix Tool sent
hash-sizes®ion=…renderPrefixTool()appended the®ion=…&area=…fragment directly to/analytics/hash-sizes, so with a filter active the server returned the SPA HTML (200text/html) and the configured-hash-size counts (Kpa-clawbot#1270) fell back silently.withQuery(path, frag)helper inpublic/analytics.jsturns the&…fragments into a?…query string.'?' + frag.slice(1)by hand (loadAnalytics, distance, neighbor-graph) now use the same helper. Their URLs are unchanged, and tests pin them.#183: returning without
?tab=showed the old tab_currentTabis module state, andinit()only set it when the URL had a valid?tab=. After you leave and come back, the markup marks Overview active, butloadAnalytics()rendered the previously selected tab.init()now derives the tab on every mount. It uses?tab=when that names a tab button, otherwise Overview, and marks that button active.destroy(), which the issue suggested. Withinit()deriving the tab on every mount, the reset is redundant and no test could tell it apart.Tests
New file
test-analytics-tab-state-and-query.js(added totest-all.sh). It runs the realapp.jsapi()and the real analytics page in a vm with a fake fetch, using the #172 fixture responses. Its fake DOM builds the tab buttons from the markup thatinit()writes. It has 9 tests:?tab=, with?tab=topology, and with an unknown?tab=: the active button and the rendered content agree.Mutants:
origin/masteranalytics.js+ rqwithQueryreturnspath + frag_currentTabininit()Related suites pass:
test-1659-analytics-warmup.js(34), alltest-analytics-*.js,test-node-analytics-hop-chart.js,test-frontend-helpers.js(707, 0 failed),test-packet-filter.jsandtest-aging.js.scripts/check-xss-sinks.sh --diffreports no findings.Browser validation
Ran locally against a Go server with the migrated
e2e-fixture.dbin Playwright and Chromium.origin/masterhash-sizes®ion=SJC→ 200text/html(×2)hash-sizes?region=SJC→ 200application/json#/nodes→#/analyticsNo page errors in either run.
Perf
There is no new work in any hot path. The change is one string concatenation per request build and one button lookup per mount.
Customizer
There are no new configurable values.
🤖 Generated with Claude Code
https://claude.ai/code/session_014m8dE6ocwCGg43tGWCPBoB