Skip to content

fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap - #735

Merged
fclairamb merged 25 commits into
mainfrom
security/scan-specs-2026-11-01
Nov 1, 2026
Merged

fclairamb merged 25 commits into
mainfrom
security/scan-specs-2026-11-01

Conversation

@fclairamb

@fclairamb fclairamb commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

No description provided.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fclairamb and others added 4 commits November 1, 2026 07:31
…e flattening against the memory budget

Closes the four paths of security finding AB1 (spec 2026-11-01-01):
- typed arrays built from an array-like read its length once (pinned
  behind a Proxy), so a length getter can no longer answer 0 to the charge
  and a huge value to the allocation;
- concat, slice, splice and filter are charged for their result; flat and
  flatMap are rewritten in Go and charge each element before appending it;
- String.prototype.split is charged per part, replace/replaceAll for their
  output (the replacement goes to goja as a Go function that measures and
  charges each expansion, or is charged up front for a string pattern);
- Function.prototype.apply, Reflect.apply and Reflect.construct check an
  array-like argument list;
- stopgap for the + rope: a rope over 64 KiB is charged when a
  String.prototype or RegExp method, a binding (arguments scanned deeply)
  or the result export would flatten it. Other flatten paths (s[0], ==,
  Number(s)) still need process isolation, a follow-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ne call allocate GBs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.14660% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.79%. Comparing base (f44707a) to head (689eb11).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
server/internal/checkers/checkjs/memory_strings.go 91.21% 21 Missing ⚠️
server/internal/checkers/checkjs/memory.go 93.52% 9 Missing ⚠️
...erver/internal/checkers/checkjs/memory_bindings.go 90.00% 6 Missing ⚠️
server/internal/handlers/auth/service.go 93.02% 3 Missing ⚠️
...rver/internal/handlers/auth/discord_link_notice.go 86.66% 2 Missing ⚠️
server/internal/handlers/auth/handler.go 33.33% 2 Missing ⚠️
server/internal/handlers/auth/personal_org.go 90.00% 1 Missing ⚠️
...er/internal/handlers/auth/registration_ip_limit.go 94.73% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #735      +/-   ##
==========================================
+ Coverage   89.77%   89.79%   +0.02%     
==========================================
  Files        1306     1310       +4     
  Lines      132987   133514     +527     
==========================================
+ Hits       119389   119891     +502     
- Misses      13589    13614      +25     
  Partials        9        9              
Files with missing lines Coverage Δ
server/internal/app/server.go 92.03% <100.00%> (+<0.01%) ⬆️
server/internal/checkers/checkjs/checker.go 92.20% <100.00%> (+0.36%) ⬆️
server/internal/checkers/checkjs/regexp.go 100.00% <100.00%> (ø)
server/internal/email/supportreply.go 92.68% <ø> (ø)
server/internal/entitlements/service.go 93.28% <100.00%> (+0.09%) ⬆️
server/internal/handlers/auth/discord_link.go 83.33% <100.00%> (+0.72%) ⬆️
server/internal/handlers/auth/factor_reauth.go 89.53% <100.00%> (+0.12%) ⬆️
server/internal/handlers/auth/passkey_handler.go 94.69% <100.00%> (ø)
server/internal/handlers/auth/passkey_notice.go 100.00% <100.00%> (ø)
server/internal/handlers/base/base.go 96.49% <ø> (ø)
... and 13 more

... and 22 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

fclairamb and others added 20 commits November 1, 2026 07:49
goja runs patterns with lookarounds or backreferences on regexp2, whose
default match timeout is forever, and vm.Interrupt cannot reach a running
match. A nested quantifier on a non-matching input pinned a core per run.
regexp2.DefaultMatchTimeout is now 2 s process-wide; a timed-out match is
reported as ERROR "regular expression took too long" without echoing the input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ktrack forever

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l the owner

link-start now asks for the current password (or a recent sign-in for a
passwordless account), like a new TOTP or passkey, and a successful link
enqueues a discord-linked.html security notice (en, fr, de, es).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uthentication and sends no notice

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…call in e2e

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…uthentication and sends no notice

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…h keyed lookups

Invitations: 50 live per org (or the seat cap when lower), 409 TOO_MANY_INVITATIONS.
Sign-in finds pending invitations through invite_email:<hash>:<org>.
Registrations: per-IP slots and token lookup are keyed (registration_ip:, registration_token:).
Invitations and registrations pending before the upgrade have no new keys: invitees use their link, sign-ups re-submit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tions of the instance, and outstanding invitations are uncapped

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tions of the instance, and outstanding invitations are uncapped

Cap test asserts no email job on refusal (with positive control) and uses a real past expires_at. Migration decision (no fallback) recorded in the spec.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
They copy up to the whole budget before being refused (0.3-0.5s of CPU), so
among ~50 parallel subtests on a loaded runner they hit the 2s script timeout
and the 1s bound. Assertions unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ot a fixed second

The refusal is exact and comes before the refused allocation, but goja has
really copied what the budget still allowed (up to ~2M slots) by then, which
took 1.7s on a loaded runner. Time the permitted part of each script as a
baseline and require the refusal within 3x of it plus a margin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The test revoked list.Data[0], which is ordered by random token hash, so
about 1 run in 50 it revoked invites[1] (the one it then expires), leaving
the org at the cap.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fclairamb
fclairamb enabled auto-merge (squash) November 1, 2026 14:49
@fclairamb
fclairamb merged commit 6026b05 into main Nov 1, 2026
32 checks passed
@fclairamb
fclairamb deleted the security/scan-specs-2026-11-01 branch November 1, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant