* security: harden against XSS, ReDoS, path traversal, and injection
Defensive fixes across the server, storage, and viewer:
- XSS (CWE-79): sanitise rendered notebooks with DOMPurify, escape file
names interpolated into AngularJS expressions (escapeNgString), set
Mermaid securityLevel to 'strict', and stop urlRel2abs from returning
javascript:/vbscript:/data:text/html URLs.
- Path traversal / zip-slip (CWE-22/23/24): validate URL-derived path
components before they reach the storage layer (file/webview routes +
StorageBase.assertSafePath) and sanitise zip entry names on extract for
both the filesystem and S3 backends.
- ReDoS (CWE-1333): escape anonymization terms with catastrophic
backtracking shapes to literals instead of compiling them as regexes.
- Secret hardening (CWE-798): require SESSION_SECRET / OAuth creds / DB
password in production, random dev SESSION_SECRET fallback.
- Rate-limit spoofing (CWE-290): derive request.ip via trust-proxy hop
count instead of the client-settable cf-connecting-ip header.
- NoSQL injection (CWE-943): allow only plain field paths as admin sort keys.
- Reject malformed streamer requests missing required string fields.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ui): make gists reachable/visible and clarify the ZIP button
- Gist & PR routes now accept a trailing slash (/gist/:id/:path*?), so the
dashboard links (which end in "/") resolve to the gist/PR page instead of
falling through to the 404 route (#725).
- Gist viewer picks the default tab after content loads, defaulting to
"files" when files exist; previously the ng-init ran before the async
load and a files-only gist rendered blank under the hidden comments tab.
- Explorer toolbar: relabel ZIP to "Full repo ZIP" with a tooltip, and add
tooltips to Raw/Download clarifying they apply to the current file (#721).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix: report SAML-enforced orgs clearly instead of "token expired"
When a repo's organization enforces SAML SSO, GitHub returns a 403 whose
message differs from the OAuth-App-restriction case. That 403 fell through
to the generic handler and surfaced as "token_expired", pushing users to
re-login when the real fix is authorizing their token for the org. Detect
the "SAML enforcement" message and raise a dedicated, actionable error
instead (#379, #550).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* security: catch nested quantified groups in ReDoS guard and backslash path traversal
- hasCatastrophicBacktracking now scans across nested parens ([\s\S]*?)
so shapes like ((a+))+ are detected; comment reframed as a heuristic
backstop rather than a proof.
- file route path-traversal check now rejects backslash separators and a
leading backslash, covering Windows-style "..\" payloads (CWE-22/25).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(dev): track dev-proxy script, ignore .DS_Store and .claude/
scripts/dev-proxy.js is referenced by the "dev:ui" npm script but was
never committed, breaking the command on a fresh clone. Add it and
ignore local-only macOS/Claude Code files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add admin endpoints to ban and activate users, block banned users
from all auth flows (OAuth, token login, bearer auth), and invalidate
existing sessions on next request. Includes frontend translation and
user detail page ban/activate buttons.
Refresh button now always updates the commit to the latest SHA instead
of preserving the stale one in edit mode. Both create and update routes
verify the commit still exists on GitHub before persisting.
Entering an IP address (e.g. 192.168.1.1) or any term with regex
metacharacters made the form invalid because the "regex characters
detected" hint was wired up via $setValidity('terms', 'regex', false).
The text in the UI labels it as a warning, but the form treated it as
an error and refused to save.
Track the warning as a plain $scope flag and show it via ng-show on
that flag, so the form stays valid (#430).
A term entered as "Anonymous=>ABC" now scrubs "Anonymous" to "ABC"
instead of "XXXX-N". Lets users keep anonymized identifiers valid in
source code (no hyphen) and align tokens between paper text and repo.
Indexing for default-mask terms is unchanged: a list of
"Alpha=>AAA", "Beta" still produces XXXX-2 for Beta.
Fixes#285.
The Generate button silently no-op'd because ng-model="newTokenName" inside
an ng-if block wrote to a child scope, leaving $scope.newTokenName empty.
Use $scope.tokenForm.{name,plaintext} so prototypal lookup resolves to the
controller scope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Remove action in the dashboard dropdown was gated on
status == 'ready', so expired repos showed no way to be removed and
stuck on the front page. The backend DELETE route already accepts any
non-'removed' status, so widen the ng-show to include 'expired' and
'error'.
Fixes#463.
* Fix horizontal overflow causing page content to be cut off on mobile
- Add overflow-x: hidden to html/body and ng-view to prevent horizontal
scrolling across all pages
- Restore .container.page mobile padding to 15px to match Bootstrap .row
negative margins (-15px), which previously caused 5px overflow per side
- Add max-width: 100% constraints to prevent content from exceeding viewport
https://claude.ai/code/session_01L2xhJCKkjghMDBuwXpSHzi
* Fix Ko-fi widget overlapping hamburger menu and simplify desktop layout
- Move Ko-fi "Support me" button from top-right (where it hid the navbar
hamburger) to bottom-right corner
- Remove card styling (border, background, border-radius) from dashboard
quota section for a flatter, cleaner look
- Remove fixed max-width: 960px from dashboard-page so it uses Bootstrap's
standard container widths, consistent with other pages like admin
https://claude.ai/code/session_01L2xhJCKkjghMDBuwXpSHzi
* Redesign anonymize page: centered landing, reorganized form layout
- No URL state: centered input in the middle of the page for a clean
initial experience
- URL provided state: preview on the left, form settings on the right
in a fixed-width sidebar panel (380px on desktop)
- Reorganized form sections into logical groups:
Source (branch/commit) → Identity (ID/conference) → Anonymization
(terms) → Display (checkboxes, no longer hidden in accordion) →
Expiration
- Removed card/accordion wrappers for a flatter, more scannable form
- Mobile: form stacks below preview with sticky submit bar
https://claude.ai/code/session_01L2xhJCKkjghMDBuwXpSHzi
* Reduce navbar height on desktop
- Reduce navbar padding from default .5rem to 4px vertical
- Shrink nav icons from 30px/40px to 20px/28px
- Reduce nav-link font size to 0.9rem with tighter padding
- Shrink navbar-brand font size to 1rem
https://claude.ai/code/session_01L2xhJCKkjghMDBuwXpSHzi
---------
Co-authored-by: Claude <noreply@anthropic.com>
Compatibility with GitHub Pages is limited: Jekyll (and other static
site generators) are not supported. This change documents this
limitation on the home page and FAQs.
Although Markdown files are converted to HTML and thus accessible when
anonymized GitHub Pages is enabled for the repository, the Markdown to
HTML conversion includes only the content -- i.e., there is no special
formatting (as would be available when using Jekyll).
For anyone who wants to use Jekyll, a potential workaround is to build
the site locally and commit the generated site to the repository.
Closes#269
Co-authored-by: Joel Coffman <joel.coffman@acm.org>