Repository navigation
fix(security): JS check memory budget residuals, regexp call time and process isolation, crawl link scheme, subscriber list role, reset links on password change - #742
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (33.50%) is below the target coverage (87.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #742 +/- ##
==========================================
- Coverage 89.79% 88.97% -0.83%
==========================================
Files 1311 1317 +6
Lines 133570 134677 +1107
==========================================
- Hits 119940 119827 -113
- Misses 13621 14841 +1220
Partials 9 9
🚀 New features to boost your workflow:
|
…rd thrown and deep ropes JSON.stringify (charged as it writes, via a replacer of its own), Array/TypedArray toLocaleString, String.prototype.normalize, String.raw, encodeURI, encodeURIComponent and escape now draw from the 64 MiB budget. JSON.parse, parseInt/parseFloat, the decode/encode functions, unescape and RegExp/String/Number put the flatten guard in front of their argument. An uncaught rope (thrown string or Error message) is refused before err.Error() flattens it, the result's error text is cut at 16 KiB, and a value too deep or too large for the rope scan is refused instead of passed. Behaviour change: String, Number and RegExp globals are Proxies, so "".constructor === String is false; a binding argument or a result nested more than 64 levels deep or holding over 1 Mi values is refused. Spec: specs/todos/2026-11-02-01-js-checker-memory-budget-residuals.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cess isolation spec Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s deadline A global replace/replaceAll/match, matchAll and every split ran their match loop natively in one goja call, each match with a fresh 2 s regexp2 limit: N slow matches ran N x 2 s past the check's timeout (spec 2026-11-02-02, scan finding AC3). RegExp.prototype.exec and test now check the run's deadline first. A global Symbol.replace / Symbol.match runs goja's spec loop (one exec per match) on a Proxy of the regexp; Symbol.split runs a Go port of goja's split, driven by exec on a global copy of the pattern (and the per-position loop for a splitter goja cannot shortcut). Results are unchanged; a run overshoots its timeout by one match at most and reports `timeout`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….txt and sitemap indexes A Sitemap: line or a sitemap index <loc> with a javascript:/data: URL was fetched and its failure stored verbatim as a sitemap_error finding. 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>
A viewer or user member (or their PAT) now gets 403 FORBIDDEN on GET /orgs/:org/status-pages/:uid/subscribers: the rows carry third parties' email addresses. Removal keeps the write floor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Non-admins see a Permission Denied notice in place of the list (also on a 403); adding a webhook/Slack delivery stays available. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… email change Reset entries are now indexed per user (password_reset_user:<userUID>:<tokenHash>, same TTL). purgePasswordResets deletes them all after a reset, a password change or first-password set, and an email change (self or super admin). Each entry also stores a hash of the address the link was mailed to; the reset is refused once the account's email differs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fclairamb
marked this pull request as draft
November 2, 2026 07:29
A js script now runs in a child of the same binary (SP_JS_CHILD=1, the hidden subcommand). The child has a heap limit (256 MiB by default, SP_JS_MEMORY_LIMIT_MB), an RLIMIT_AS/RLIMIT_DATA cap on Linux, an optional delegated cgroup (SP_JS_CGROUP), and is killed at the timeout plus 1 s. The bindings that need the check process (http, solidping.*, browser, rdp, vnc, tcp, udp, websocket, and so the egress guard, allowlists and repair hold) are served by the parent over the child's stdin/stdout. A child killed by its limit is an error result naming the limit; one killed at the timeout is a timeout. SP_JS_ISOLATION=off restores the in-process run. Spec: 2026-11-02-06 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…isolation An empty PluginConfig was dropped by omitempty, so the child ran the plugin as a plain js check (no warning status). The docs gain a Process isolation section with the SP_JS_* operator variables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…limited child process Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…(batch gate) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fclairamb
marked this pull request as ready for review
November 2, 2026 07:57
fclairamb
enabled auto-merge (squash)
November 2, 2026 08:17
…into wt/security/scan-specs-2026-11-02
…ded runner Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…into wt/security/scan-specs-2026-11-02
… allocation may overflow' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
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.
Specs filed from the daily security scans. Each spec is the tracker for its finding.
What changed (specs implemented by this PR)
AC2 (medium):
specs/todos/2026-11-02-01-js-checker-memory-budget-residuals.md. Even after fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735, one native call in a JS check can allocate GBs:JSON.stringify,toLocaleString,normalize,String.raw, the URI encoders, and ropes flattened byJSON.parse, by a thrown error (which also stores a huge error string with the result) or hidden deeper than the scan's depth cap. Now charged, with the rest of the amplifying builtins; the thrown error text is capped at 16 KiB. Process isolation is filed as AC7.AC3 (medium):
specs/todos/2026-11-02-02-js-checker-regexp-total-time.md. The 2 s regexp limit of fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735 applies to each match. A globalreplacechains slow matches into hours of CPU in one call that the timeout cannot stop. Now bounded for the whole run.AC4 (low):
specs/todos/2026-11-02-03-crawl-finding-url-scheme.md. Sitemap URLs fromrobots.txtand sitemap indexes skip the http(s) filter, so a crawled site can plant ajavascript:link in a check's findings. The dashboard CSP blocks it today. Now filtered in the crawler, and dash0 renders only http(s) links.AC5 (low):
specs/todos/2026-11-02-04-status-page-subscribers-admin-only.md. Viewers can list a status page's subscriber email addresses. The list is now admin-only (breaking for viewer/user API clients).AC6 (low):
specs/todos/2026-11-02-05-password-reset-tokens-purged-on-change.md. Outstanding password-reset links survive a password change, a reset and an email change. Reset links are now deleted on each of these.AC7 (medium):
specs/todos/2026-11-02-06-js-checker-process-isolation.md. Each JS script now runs in a memory-limited child process of the same binary (heap watchdog,RLIMIT_AS, optional cgroup, kill at timeout); bindings stay served by the parent. A killed child gives anerror/timeoutresult, the parent never crashes.SP_JS_ISOLATION=off,SP_JS_MEMORY_LIMIT_MB,SP_JS_CGROUPdocumented. About 11-27 ms overhead per run on macOS.Decided by autopilot
usermembers keep the list?": no, admin only (recommended answer).Earlier scan specs (implemented, in
specs/done/2026/10/andspecs/done/2026/11/)2026-11-01-01-js-checker-memory-budget-gaps.md. The JS memory budget missed ropes, dense-array copies,split,replaceand typed arrays. Fixed in fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735; AC2 covers what is left.2026-11-01-02-js-checker-regexp-timeout.md. A backtracking JS regexp had no time limit. Fixed in fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735 with a per-match limit; AC3 bounds a whole call.2026-11-01-03-discord-link-reauth.md. Linking Discord needed no re-authentication. Fixed in fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735.2026-11-01-04-invitation-cap-and-keyed-lookups.md. Uncapped invitations and instance-wide invitation scans at sign-in. Fixed in fix(security): JS check memory budget gaps and regexp time limit, Discord link re-authentication, invitation cap #735.2026-10-31-01-js-checker-memory-budget.md. A JS check had no memory budget: one native builtin call could OOM-kill a shared worker. Fixed in fix(security): JS check memory budget, re-authentication for new sign-in factors #728; AB1 closes its residuals.2026-10-31-02-sign-in-factor-enrollment-reauth.md. Turning on TOTP and registering a passkey needed no re-authentication. Fixed in fix(security): JS check memory budget, re-authentication for new sign-in factors #728; AB3 covers the Discord link.2026-10-30-02-bug-report-authenticated-org-membership.md. Signed-in non-members skipped the bug-report screenshot budget. Fixed in fix(security): bug-report budget for non-members, retention sparing crawl reports #715.2026-10-30-03-report-retention-spares-crawl-reports.md. The report retention sweep also deleted website crawl reports. Fixed in fix(security): bug-report budget for non-members, retention sparing crawl reports #715.2026-10-29-01-bug-report-upload-instance-budget.md. Anonymous bug-report screenshots were capped per org, not per instance, and never deleted. Fixed in fix(security): instance-wide budget and retention for anonymous bug-report screenshots #708; Z1 and Z2 are follow-ups.2026-10-28-01-pat-cannot-mint-sessions-or-sign-in-factors.md. A PAT could open sessions in its owner's other orgs and add sign-in factors. Fixed in fix(security): PAT org binding, subscribe throttle, invite lookup, SNMP secret #699.2026-10-28-02-status-subscribe-throttle-and-keep-confirmed.md. Unthrottled subscribe confirm emails, and re-subscribing un-confirmed a subscriber. Fixed in fix(security): PAT org binding, subscribe throttle, invite lookup, SNMP secret #699.2026-10-28-03-invite-lookup-without-org-scan.md. Invite lookup scanned every org. Fixed in fix(security): PAT org binding, subscribe throttle, invite lookup, SNMP secret #699.2026-10-28-04-snmp-community-secret-field.md. The SNMP community was not a secret field. Fixed in fix(security): PAT org binding, subscribe throttle, invite lookup, SNMP secret #699.2026-10-27-01-device-consent-phishing-guard.md. Device-flow consent phishing. Fixed in fix(security): device consent phishing guard and 2FA disable throttle #693.2026-10-27-02-disable-2fa-attempt-throttle.md.DELETE /auth/2fahad no attempt budget. Fixed in fix(security): device consent phishing guard and 2FA disable throttle #693.2026-10-26-02-ai-repair-hold-skips-param-only-secrets.md. Therepair: autohold applied only when the check'ssecretsmap had a value. Fixed in fix(ai): hold every scheduled repair-auto run to its granted hosts #683.2026-10-25-01-ai-repair-grant-dropped-by-reference-resolution.md. The repair grant was compared after${…}references were resolved. Fixed in fix(ai): hold the repair: auto host grant when the script or env holds a reference #678; U1 closed the remaining gap.2026-10-24-01-ai-repair-auto-apply-partial-runtime-host.md.repair: autoapplied a candidate that kept a literal host and added one built at runtime. Fixed in feat(ai): hold auto-applied repairs to the previous version's hosts (+ scan specs) #663.2026-10-23-02-ai-loop-secret-relay-through-model.md. AI loops relayed an encoded secret through the model.2026-10-22-01-ai-script-secret-host-guard-runtime.md. AI runs with real secrets were only host-checked by regex.2026-10-20-01-sender-response-body-caps.md. Notification senders and RabbitMQ read responses uncapped.2026-10-19-01-a2a-response-size-cap.md. A2A responses were read uncapped.2026-10-18-01-plugin-schema-file-ref.md. Plugin schema$refcould load local files.2026-10-17-01-postgres-check-connection-config.md. Postgres conninfo escaping and fallback to the worker'sPG*env.2026-10-16-15-pat-cache-revocation.md. The PAT cache outlived revocation.2026-10-16-14-totp-on-federated-sign-in.md. No TOTP on federated sign-in.2026-10-16-13-atomic-auth-attempt-counters.md. Auth attempt counters were not atomic.2026-10-16-12-mcp-tokens-rejected-on-rest.md. MCP OAuth tokens worked on REST.2026-10-16-11-slack-signature-fail-closed.md. Slack signature check failed open.2026-10-16-10-discovery-scans-egress-guard.md. Discovery scans were outside the egress policy.2026-10-16-09-js-check-nested-docker-guard.md. A JSdockersub-check bypassed the gate.2026-10-16-08-status-page-components-cross-org.md. Status-page components could be edited across orgs.2026-10-16-07-guard-status-subscriber-and-publication-webhooks.md. Subscriber and publication webhooks were not guarded.2026-10-16-06-accept-invite-existing-account.md. Accepting an invite logged in as an existing account.2026-10-16-05-kubeconfig-exec-and-incluster.md. KubeconfigexecandinClusterwere allowed.2026-10-16-04-mysql-dsn-injection.md. MySQL DSN injection.2026-10-16-03-env-secret-reference-allowlist.md. No allowlist for${env:}references.2026-10-16-02-token-purpose-separation.md. Purpose tokens were accepted as access tokens.2026-10-16-01-microsoft-signin-unverified-email-linking.md. Microsoft nOAuth email linking.🤖 Generated with Claude Code