feat(viewer): add session compare page (#1104) - #1175
Open
chethanuk wants to merge 1 commit into
Open
Conversation
`ocr session compare` had no equivalent in the web viewer (alibaba#1104). Adds GET /r/{repo}/compare?before=&after= rendering the same four buckets the CLI prints, plus a compare link on each session-list row pointing at the next-older session. Findings render with the existing/suggested code panels session.html already uses, so a finding whose fix is a patch shows the patch and not only the prose around it. The reviewed-path partition (manifest Completed + Reused) moves to an exported session.ReviewedPaths that the CLI now delegates to, so the two cannot drift. The viewer's own Summary.FilesReviewed is Coverage.Selected - the intended set - which would report files an interrupted run never reached as clean. Route registration moves out of StartServer into newMux, so the literal "compare" segment beating the {sessionID} wildcard is covered by a test that dispatches through the real ServeMux instead of calling the handler directly. The repo guard there now rejects "\" alongside "/": ServeMux unescapes each path segment, and "\" is a separator on Windows. encodeRepoPath maps both path separators to "-", so two distinct working directories ("/home/a/b" and "/home/a-b") can share one on-disk repo directory, and a compare request could otherwise pair sessions from different repos. handleCompare (internal/viewer/handler.go:185) rejects that case by comparing the two sessions' Summary.CWD, the same check the CLI does in cmd/opencodereview/session_cmd.go's runSessionCompare, tested at internal/viewer/compare_test.go:272. LoadSession (internal/viewer/store.go) now re-validates encodedRepo and sessionID itself through a new safeSegment helper and joins the value safeSegment returns rather than its own copy of the argument, instead of only trusting that every caller had already checked them with unsafeSegment. Both current callers (handleSession's route and handleCompare's before/after loop) already rejected traversal before reaching LoadSession, but a validate-then-use-the-original-argument shape across a function boundary is not something CodeQL's path-injection query can credit as a sanitizer, and it left LoadSession unsafe to call from anywhere that skipped that pre-check. ListSessions's sort by Timestamp alone was not a total order: two sessions with an equal (or both-zero) timestamp could swap position between calls, and sessions.html's "compare" link pairs row i with row i+1 as chronologically adjacent. The sort now breaks ties on SessionID so that pairing is deterministic. One divergence from the CLI is stated in the viewer docs: an empty bucket still renders as "New (0)" instead of being skipped. Entry points are plain links, never a form: the viewer's CSP sets form-action 'none', which blocks GET submissions with no server-side error. compare.html declares lang="en". The three older viewer templates still say lang="zh-CN" while serving English; fixing those is a separate change. Inherited limitation: session.Compare keys a finding on its current path, so a file renamed between the two runs reads as one resolved plus one new. The page shows what Compare returns and does not correct it. Signed-off-by: ChethanUK <chethanuk@outlook.com>
Contributor
|
✅ OpenCodeReview: Review complete: 0 finding(s) across 7 selected item(s). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #1104.
ocr session comparehad no equivalent in the web viewer. This addsGET /r/{repo}/compare?before=&after=, rendering the same four buckets the CLI prints, plus a compare link on each session-list row pointing at the next-older session. Findings render with the existing/suggested code panelssession.htmlalready uses, so a finding whose fix is a patch shows the patch and not only the prose around it.The reviewed-path partition (manifest
Completed+Reused) moves to an exportedsession.ReviewedPathsthat the CLI now delegates to, so the two cannot drift. The viewer's ownSummary.FilesReviewedisCoverage.Selected, the set a run intended to review, which would report files an interrupted run never reached as clean.Route registration moves out of
StartServerintonewMux, so the literalcomparesegment beating the{sessionID}wildcard is covered by a test that dispatches through the realServeMuxinstead of calling the handler directly. The repo guard there now rejects\alongside/:ServeMuxunescapes each path segment, and\is a separator on Windows.Entry points are plain links, never a form. The viewer's CSP sets
form-action 'none', which blocks a GET submission with no server-side error.Divergences and limitations
New (0), where the CLI skips a zero-count section. On a web page a heading that has vanished is indistinguishable from a broken render, so the viewer keeps it. Stated in the viewer docs, which are updated in all five locales./r/{repo}looks like it already partitions by repo, butencodeRepoPathmaps both separators to-, so/home/a/band/home/a-bland in one viewer directory. The CLI errors on that pair; so does the page.session.Comparekeys a finding on its current path, so a file renamed between the two runs reads as one resolved plus one new. That is pre-existing ininternal/session/compare.go; the page shows whatComparereturns and does not correct it.LoadSession, which does not replay resume semantics the way the CLI'ssession.LoadCommentsdoes: a later checkpoint for the same fingerprint doesn't supersede an earlier one, and a subsequentreview_item_faileddoesn't drop it. On a session with no resumes this is a no-op; on a resumed or partially-failed run the compare page can show a finding the CLI's own comparison would already exclude. Pre-existing inLoadSession, not introduced here, but this page is the first place the gap can produce a visibly wrong bucket.{#three-pages}becomes{#four-pages}. No in-repo link uses it, but an external link to#three-pageswill stop resolving.unsafeSegmentalso tightens two routes that already existed, so/r/my%5Creponow returns 400 where it used to 404 (internal/viewer/compare_test.go:435). That is a behavior change on old routes inside a feature PR. Splitting it out would mean two ordered PRs for about ten lines the new route needs regardless, so it is here — say the word and I will separate it.compare.htmldeclareslang="en". The three older viewer templates still saylang="zh-CN"while serving English; fixing those is a separate change.Type of Change
How Has This Been Tested?
make testpasses locallymake test(-race -count=1, 23 packages, 0 failures),make coverage(91.2% against the 90% threshold), andmake checkall pass on the rebased branch.TestNewMux_RouteDispatchsends nine paths through the real mux, including/r/{repo}/compareagainst the{sessionID}wildcard, so the segment precedence is asserted rather than assumed.unsafeSegmentguard on the query parameters flips the traversal cases (../s1,%2e%2e%2fs1,a%2fb,a\b) from 400 to 404, which is the escape reachingfilepath.Join.TestHandleComparecovers the four buckets, a self-compare where three of them come back empty, missing and unknown session ids, HTML escaping of finding text, and a cross-repo pair (400).TestReviewedPathsininternal/sessionpins the partition both callers now share; a case withselected:[a.go,b.go], onlyb.gocompleted, is what catches a handler that readsCoverage.Selectedinstead.Checklist
go fmt,go vet)Related Issues
Closes #1104
Update (post-review): the commit was amended (
39acc8e) to fix aLoadSessionpath-injection false-positive CodeQL flagged (internal/viewer/store.go): validation now happens in asafeSegmenthelper whose returned value is what's joined, rather than checking a boolean and then joining the original argument — this is what let CodeQL's dataflow see the sanitizer. Also added aSessionIDtie-breaker toListSessions's sort so thesessions.html"compare" link's row-i/row-i+1pairing is deterministic when two sessions share a timestamp. No functional/behavioral change to the routes described above; seeqa-report.mdfor full detail.