Hardening: API request handling, random IDs, and plugin loading#7906
Conversation
- pad_utils.randomString: generate the random IDs via crypto.getRandomValues (CSPRNG) instead of Math.random. - OAuth2Provider: constant-time password comparison and a uniform failure delay on the OIDC interaction login; own-property user lookup. - API.appendChatMessage: require the pad to already exist (getPadSafe), consistent with the other content API methods. - RestAPI /api/2: forward only the authorization header rather than merging all request headers into the API field set. - LinkInstaller: validate plugin dependency names before building filesystem paths from them. - admin file server: return a generic error message and log details server-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Code Review by Qodo
1.
|
Review Summary by QodoHarden API request handling, token generation, and plugin loading
WalkthroughsDescription• Replace Math.random with crypto.getRandomValues for secure ID generation • Implement constant-time password comparison in OAuth2Provider • Add uniform failure delay to OIDC login failures • Require pad existence before appending chat messages • Forward only authorization header in /api/2 requests • Validate plugin dependency names before filesystem operations • Return generic error messages from admin file server Diagramflowchart LR
A["Security Improvements"] --> B["Token Generation"]
A --> C["Authentication"]
A --> D["API Validation"]
B --> B1["crypto.getRandomValues<br/>rejection sampling"]
C --> C1["Constant-time comparison"]
C --> C2["Failure delay"]
D --> D1["Pad existence check"]
D --> D2["Header filtering"]
D --> D3["Dependency validation"]
File Changes1. src/static/js/pad_utils.ts
|
- OAuth2Provider.constantTimeEquals: compare raw UTF-8 bytes with crypto.timingSafeEqual instead of hashing them first. Resolves the CodeQL "password hash with insufficient computational effort" alert while keeping a content-independent comparison (length difference is covered by the uniform failure delay). - RestAPI /api/2: fall back to the authorization header whenever the field is falsy (not only null), matching the openapi.ts handler so the two routers authenticate identically (Qodo). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| // Without this, sendChatMessageToPadClients -> padManager.getPad() would | ||
| // create the pad on demand with default content, so the documented | ||
| // {code:1,"padID does not exist"} result would never be returned. | ||
| await getPadSafe(padID, true); |
There was a problem hiding this comment.
Or we just throw an error. That way somebody can send chat messages to random pads and they are created.
There was a problem hiding this comment.
getPadSafe(padID, true) already throws padID does not exist when the pad is missing (API.ts:1029) — it does not create it — so chat can no longer materialise random pads. It's the same existence check getText/setText/getHTML use, and it also validates the padID string/format.
Happy to swap it for an explicit throw new CustomError('padID does not exist', 'apierror') if you'd rather not fetch the pad here — functionally identical, just let me know which you'd prefer to merge.
There was a problem hiding this comment.
Ah okay. Thought getPadSafe would also create if missing. Then let's be more explicit with an error like you suggested throw new CustomError('padID does not exist', 'apierror')
There was a problem hiding this comment.
Done — switched to the explicit throw new CustomError('padID does not exist', 'apierror') (guarded by padManager.doesPadExists), so no pad is fetched or created. Chat spec still green.
Per review: replace the getPadSafe(padID, true) existence check with an
explicit `throw new CustomError('padID does not exist', 'apierror')` so chat
messages can't create pads, without fetching the pad.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A second small hardening pass, covering API request handling, random-ID generation, and plugin loading. Companion to #7905.
Changes
pad_utils.randomString— generate the random IDs (author/session/readonly) viacrypto.getRandomValues(a CSPRNG) instead ofMath.random, with rejection sampling to avoid modulo bias. Output format/length is unchanged.OAuth2Provider— constant-time password comparison and a uniform failure delay on the OIDC interaction login; look the user up by own property only.API.appendChatMessage— require the pad to already exist (getPadSafe), consistent with the other content API methods.RestAPI(/api/2) — forward only theauthorizationheader instead of merging all request headers into the API field set (matches theopenapi.tshandler).LinkInstaller— validate plugin dependency names before using them to build filesystem paths.appendChatMessagenow returnspadID does not existfor a non-existent pad instead of silently creating it. Callers that relied on the implicit pad creation must callcreatePad(orcreateGroupPad) first. This aligns it withsetText/getText/etc./api/2no longer exposes arbitrary request headers as API parameters. Onlyauthorizationis forwarded. This only affects a caller that passed an API parameter via an HTTP header of the same name (not a documented usage); send such values as query/body parameters instead.The remaining changes (
randomString,LinkInstaller, admin error text) are non-breaking.Testing
chat,api,jwtAdminClaim,LinkInstaller, author-token,webaccess,tokenTransfer,OIDCAdapter).randomStringvalidated in a real browser via theauthor_token_cookiechromium spec (HttpOnly token, stable authorID across reload, unique per context).🤖 Generated with Claude Code