fix(s3): trim whitespace in bucket name + creds so uploads can't break on a stray space#1338
Conversation
…k on a stray space A trailing space in the prod S3_BUCKET_NAME env var made every presigned PUT target the invalid bucket "codu.uploads " -> S3 returned 400 InvalidBucketName, taking uploads down entirely. The whitespace is invisible in the Vercel UI, and S3_BUCKET_NAME/ACCESS_KEY/SECRET_KEY aren't in config/env.js validation, so the bad value failed silently at runtime instead of at boot. Trim these values at the point of use (getPresignedUrl bucket name, s3 client credentials) so stray whitespace on any of them can't break signing or the PUT. Adds a regression test that fails without the trim (URL came out as .../codu.uploads%20/...) and passes with it. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughWhitespace trimming is added to ChangesS3 env var whitespace hardening
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- restore process.env in afterAll so the suite doesn't lean on neighbours - remove the misleading `// @fix TS ERROR` comment (no actual error) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
server/common/getPresignedUrl.test.ts (1)
8-8: ⚡ Quick winReplace
anywith proper typing.Using
anydisables type checking forgetPresignedUrl. Consider using the actual type from the import ortypeof:let getPresignedUrl: typeof import("`@/server/common/getPresignedUrl`").getPresignedUrl;Or simply:
let getPresignedUrl: ( fileType: string, fileSize: number, config: { kind: string; userId?: string } ) => Promise<string>;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/common/getPresignedUrl.test.ts` at line 8, The `getPresignedUrl` variable is declared with the `any` type, which disables TypeScript's type checking. Replace the `any` type annotation with a proper type by either using `typeof` to reference the actual function type from the getPresignedUrl module import, or by explicitly defining the function signature with its parameters (fileType: string, fileSize: number, config object) and return type (Promise<string>). This will restore type safety and catch potential type errors during development.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/common/getPresignedUrl.test.ts`:
- Around line 10-16: Add an afterAll hook to restore the environment variables
that are modified during test setup. Store the original values of
process.env.ACCESS_KEY, process.env.SECRET_KEY, and process.env.S3_BUCKET_NAME
before the beforeAll hook modifies them, then restore those original values (or
delete the variables if they were not originally set) in the new afterAll hook
to prevent test pollution.
---
Nitpick comments:
In `@server/common/getPresignedUrl.test.ts`:
- Line 8: The `getPresignedUrl` variable is declared with the `any` type, which
disables TypeScript's type checking. Replace the `any` type annotation with a
proper type by either using `typeof` to reference the actual function type from
the getPresignedUrl module import, or by explicitly defining the function
signature with its parameters (fileType: string, fileSize: number, config
object) and return type (Promise<string>). This will restore type safety and
catch potential type errors during development.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 9213f097-192a-4635-96ee-4c3e79a5b862
📒 Files selected for processing (3)
server/common/getPresignedUrl.test.tsserver/common/getPresignedUrl.tsutils/s3helpers.ts
| beforeAll(async () => { | ||
| // Whitespace-padded credentials, set before import so the s3 client (built | ||
| // at module load) sees them. | ||
| process.env.ACCESS_KEY = "AKIATESTKEY1234567890 "; | ||
| process.env.SECRET_KEY = "secretsecretsecretsecretsecretsecret1234\n"; | ||
| ({ getPresignedUrl } = await import("@/server/common/getPresignedUrl")); | ||
| }); |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add cleanup to restore environment variables.
The beforeAll hook modifies process.env.ACCESS_KEY, process.env.SECRET_KEY, and the tests modify process.env.S3_BUCKET_NAME, but these changes are never reverted. This can pollute other tests if Vitest runs them in the same process or if other tests depend on these variables.
🧹 Add cleanup with afterAll
describe("getPresignedUrl whitespace hardening", () => {
// eslint-disable-next-line `@typescript-eslint/no-explicit-any`
let getPresignedUrl: any;
+ const originalEnv = {
+ ACCESS_KEY: process.env.ACCESS_KEY,
+ SECRET_KEY: process.env.SECRET_KEY,
+ S3_BUCKET_NAME: process.env.S3_BUCKET_NAME,
+ };
beforeAll(async () => {
// Whitespace-padded credentials, set before import so the s3 client (built
// at module load) sees them.
process.env.ACCESS_KEY = "AKIATESTKEY1234567890 ";
process.env.SECRET_KEY = "secretsecretsecretsecretsecretsecret1234\n";
({ getPresignedUrl } = await import("`@/server/common/getPresignedUrl`"));
});
+
+ afterAll(() => {
+ process.env.ACCESS_KEY = originalEnv.ACCESS_KEY;
+ process.env.SECRET_KEY = originalEnv.SECRET_KEY;
+ process.env.S3_BUCKET_NAME = originalEnv.S3_BUCKET_NAME;
+ });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@server/common/getPresignedUrl.test.ts` around lines 10 - 16, Add an afterAll
hook to restore the environment variables that are modified during test setup.
Store the original values of process.env.ACCESS_KEY, process.env.SECRET_KEY, and
process.env.S3_BUCKET_NAME before the beforeAll hook modifies them, then restore
those original values (or delete the variables if they were not originally set)
in the new afterAll hook to prevent test pollution.
Problem
Uploads were totally broken on production. Root cause: a trailing space in the prod
S3_BUCKET_NAMEenv var. Every presigned PUT targeted the invalid bucket name"codu.uploads ", so S3 returned400 InvalidBucketName.The space is invisible in the Vercel UI, and
S3_BUCKET_NAME/ACCESS_KEY/SECRET_KEYaren't inconfig/env.jsvalidation — so the bad value failed silently at runtime instead of at boot. The bucket, CORS, public-read policy and IAM creds were all verified healthy; only the env value was bad.Fix
Trim these values at the point of use so stray whitespace on any of them can't break uploads:
getPresignedUrl.ts→Bucket: process.env.S3_BUCKET_NAME?.trim()utils/s3helpers.ts→ trimACCESS_KEY/SECRET_KEYbefore they reach the SigV4 credentialTest
server/common/getPresignedUrl.test.ts— fails without the trim (URL came out as.../codu.uploads%20/...withX-Amz-Credential=...%20), passes with it. Full unit suite green.Note
This is defence-in-depth — the live fix is also to retype the prod env value with no whitespace and redeploy. Follow-up worth considering: add the three S3 vars to
config/env.jsso bad values fail at boot.🤖 Generated with Claude Code