Commit Graph
114 Commits
Author SHA1 Message Date
Thomas Durieux a05f42ad9f Merge pull request #801 from tdurieux/t3code/archive-legacy-spaced-ids
fix: archive legacy repository IDs containing spaces
2026-09-08 02:46:38 -10:00
tdurieux c7eed1a6b4 perf: parallelize credential migration owner analysis 2026-09-08 14:28:46 +02:00
tdurieux 0c808a8675 fix: recover conflicting credentials by verifying the recorded owner 2026-09-08 14:21:16 +02:00
tdurieux 90f14b19b7 fix: archive legacy repository IDs containing spaces 2026-09-08 14:08:48 +02:00
Thomas Durieux 530e388e46 fix: archive ownerless repositories without assigning ownership (#800) 2026-09-08 14:03:09 +02:00
Thomas Durieux 8de4a536cc fix: recover legacy repository owners through GitHub (#799) 2026-09-08 13:39:39 +02:00
Thomas Durieux 3fb376dd53 fix: encrypt stored GitHub credentials (#797) 2026-09-08 13:04:28 +02:00
tdurieux b070d7e281 fix: evaluate document creation timestamps per instance 2026-09-06 09:19:33 +02:00
tdurieux f312105ec3 fix: fetch private pull request diffs through authenticated API 2026-09-06 09:19:28 +02:00
tdurieux 4aecc1d92f fix: fetch complete content for truncated Gist files 2026-09-06 09:19:23 +02:00
tdurieux 38a09188ad fix: expire conference repositories before completing expiration 2026-09-06 09:18:42 +02:00
tdurieux 6777537d44 fix: restrict S3 deletion to exact keys and descendants 2026-09-06 09:18:37 +02:00
tdurieux 5e84bfaa5b fix: surface partial S3 object deletion failures 2026-09-06 09:18:32 +02:00
tdurieux 4643e9f838 fix: preserve and anonymize truncated folder warnings 2026-09-06 09:18:28 +02:00
tdurieux 8d2921b406 fix: respect backpressure when resolving Git LFS pointers 2026-09-06 09:18:24 +02:00
tdurieux c14a548cb9 fix: enforce content policies using original file types 2026-09-06 09:17:42 +02:00
tdurieux 11eb160d2b fix: prevent stale downloads from reviving removed repositories 2026-09-06 09:17:33 +02:00
tdurieux 2af66cd1b6 fix: authorize coauthors by stable GitHub identity 2026-09-06 09:17:01 +02:00
tdurieux 8ec656c201 fix: authorize access before returning cached GitHub metadata 2026-09-06 09:16:57 +02:00
tdurieux 50d606d0c1 fix: bound regex execution during anonymization 2026-09-06 09:16:48 +02:00
tdurieux b3f86513dd fix: prevent anonymization leaks across stream boundaries 2026-09-06 09:16:44 +02:00
tdurieux 900516f225 fix: omit source content length for rewritten file responses 2026-09-06 09:16:23 +02:00
Thomas Durieux 35c3b1804b Fix repository cleanup after removal and expiration (#782) 2026-08-20 12:52:19 +02:00
Thomas Durieux 7aa9557905 perf: cut MongoDB scans in repository workflows (#780)
* Add repository name index

* Optimize repository file counts

* Optimize MongoDB query workloads
2026-08-20 12:29:26 +02:00
Thomas Durieux aab6ccf7fc fix: harden anonymization privacy paths (#754) 2026-07-22 04:28:01 +02:00
tdurieux 1c1993f972 fix: try to fix gist 2026-07-02 14:53:26 +03:00
Thomas Durieux 839582c657 Fix .bat anonymization, truncated-tree misses, submodule warning, account deletion (#742)
* fix: anonymize Windows batch scripts (#735)

mime-types maps .bat to application/x-msdownload, the same MIME type as
.exe/.dll, so batch scripts were classified as binary and streamed
through without any anonymization. Special-case .bat/.cmd as text before
the MIME lookup, keeping .exe/.dll binary.

* fix: recover files missing from truncated tree listings (#738)

GitHub truncates tree listings of very large repositories. Folders whose
listing was truncated are recorded in truncatedFolders, but files that
fell outside the listing never reached the database, so requesting them
returned 404 file_not_found even though they exist on GitHub — and a
force refresh could not help.

When a file lookup misses and its directory is under a truncated folder,
fetch the file metadata directly from GitHub's contents API (object
media type, so it works past the 1MB inline limit), cache it in the
database, and serve it normally.

* feat: warn when a repository uses git submodules (#737)

GitHub archives and tree listings never include submodule contents, so
submodules end up as empty folders in the anonymized repository, which
surprises users. Detect a root .gitmodules file and show a warning
banner in the explorer explaining that submodule contents are not
included.

* feat: allow users to delete their account (#741)

Add DELETE /api/user: removes all anonymized repositories, gists, and
pull requests owned by the user, best-effort revokes the GitHub OAuth
grant, and scrubs personal data (username, emails, tokens, GitHub id,
photo) from the user record. The record itself is kept with a
placeholder username so removed repoIds stay reserved and owner
references remain resolvable.

The settings page gains an Account section with a confirmed delete
button.

* fix: add missing error translations for token_expired and job_is_active

The error-code coverage test failed because both backend codes had no
frontend translation.
2026-07-02 13:35:48 +02:00
Thomas DurieuxandClaude Opus 4.8 e4ffd74068 Security hardening + gist UI fixes (#731)
* 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>
2026-06-18 13:50:55 +02:00
tdurieux 39fadd6cf0 fix: auth issue & profile save issue 2026-05-18 16:04:09 +03:00
tdurieux e73ad48115 fix: file download large repo 2026-05-12 21:00:47 +03:00
tdurieux 898f18919e fix rate limit 2026-05-12 20:51:08 +03:00
tdurieux 427e26062e fix 2026-05-12 20:34:32 +03:00
tdurieux 03e18fd572 repo change + daily stat improvements 2026-05-11 12:10:17 +03:00
tdurieux 3eeed23609 handle memory issues 2026-05-07 21:01:07 +03:00
tdurieux b37a814f3a improve queue 2026-05-07 14:58:36 +03:00
tdurieux 2de08c3df3 Add missing error handlers on stream pipelines
- AnonymizedFile.anonymizedContent(): propagate content errors to the
  anonymizer so callers see the failure instead of hanging.
- AnonymizedFile.send() local path: add error handler on the anonymizer
  transform between content and response pipes.
- S3.send(): handle errors on the S3 body stream to avoid unhandled
  emits crashing the process.
- S3.archive() / FileSystem.archive(): propagate read-stream errors
  to the file transformer so archiver sees the failure.
- Add frontend translations for new error codes.
2026-05-07 07:47:29 +03:00
tdurieux 7a163f2d35 Fix streamer crash and misclassified transient GitHub errors
Add missing error handler on the anonymizer transform stream in the
streamer route — without it, an upstream error tears down the pipe and
the anonymizer emits an unhandled error that crashes the process
(surfacing as ECONNRESET to the main server).

Classify transient network errors (ReadError, ECONNRESET, ETIMEDOUT)
as upstream_error/502 instead of file_not_found/404 so they are
distinguishable in logs and don't cache-poison downstream.

Update handleError tests to match the existing sanitization behavior
that returns internal_error for non-AnonymousError instances.
2026-05-07 07:44:15 +03:00
tdurieux 4ab8e0d1cd Handle GitHub 409 "repository is empty" error in getCommitInfo
When a GitHub repo has no commits, the API returns 409 which was
unhandled, causing raw HttpError warnings. Now throws repo_empty
AnonymousError consistent with the existing convention.
2026-05-07 07:42:05 +03:00
tdurieux 9292c19392 Handle GitHub 422 errors as commit_not_found and sanitize error responses 2026-05-07 05:54:51 +03:00
tdurieux 9403f15ac3 Align error logging with admin dashboard field conventions
All warn/error log calls now use field names the dashboard's decorate()
function recognizes: `code` for the error code pill, `httpStatus` for the
status badge and severity bucket, `url` for the sidebar link, and
`repoId` for the repository link.

Key changes:
- Streamer errors surface code, httpStatus, url, and nested err in Raw tab
- Nested `{ err: serializeError(e) }` replaced with spread pattern so
  error fields (name, message, status) appear at the top level
- Raw Error objects in catch blocks now go through serializeError()
- Rate limit, token, and PR 404 warnings include code + httpStatus
- Dashboard stack walker traverses both `cause` and `err` chains
- Dashboard Raw tab renders repoId, filePath, upstream*, err, and cause
- trimRawArg recursively trims stacks in nested err/cause chains
- clampPayload strips heavy nested fields before falling back to
  truncated placeholder, preserving flat diagnostic fields
2026-05-07 05:54:18 +03:00
tdurieux b8cfe293ea Fix BullMQ "Custom Id cannot be integers" error by prefixing jobId 2026-05-07 05:53:26 +03:00
tdurieux 8fc7ac5175 Add user ban/activate feature
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.
2026-05-07 05:41:12 +03:00
tdurieux bd8656206a fix persistance bugs 2026-05-06 20:00:59 +03:00
tdurieux 67cb2538b1 fix old github download repos 2026-05-06 19:37:16 +03:00
tdurieux da78708b7b Improve error handling 2026-05-06 18:43:36 +03:00
tdurieux aae6eae6eb handle rate limit 2026-05-06 17:50:01 +03:00
tdurieux 3b27816702 fix incremental 2026-05-06 17:12:58 +03:00
tdurieux 804bbffb7a Improve error handling 2026-05-06 17:03:19 +03:00
tdurieux cf2f172aca fix(gist): set gist subpaths individually to avoid CastError
Mongoose treats `gist` as a nested path, not a sub-schema, so
set("gist", payload) mis-casts the inner subdoc arrays and fails
validation with 'Cast to [string] failed' at gist.files.0. Set each
subpath individually so the files/comments arrays cast correctly.
2026-05-06 16:52:48 +03:00
tdurieux dcb524c8c1 Improve error handling 2026-05-06 16:45:22 +03:00