Skip to content

Fix file upload security: bind authenticated user, add MIME type filter, fix CI artifact paths - #297

Merged
memplethee-lab merged 4 commits into
SourceXXL:mainfrom
memplethee-lab:feature/file-upload-security-ci-fixes
Oct 12, 2026
Merged

memplethee-lab merged 4 commits into
SourceXXL:mainfrom
memplethee-lab:feature/file-upload-security-ci-fixes

Conversation

@memplethee-lab

Copy link
Copy Markdown
Contributor

Summary

Three security and CI fixes for the file upload system and build pipeline.

#156 — Bind authenticated user instead of hardcoded userId = system

Problem: Every handler in FileUploadController and WebhookController hardcoded const userId = "system", so all authenticated users shared the same storage namespace. Any user could list, overwrite, or delete any other user's files and webhook subscriptions.

Fix: Both controllers now use the existing @CurrentUser() decorator (from src/core/auth/decorators) to extract the authenticated user ID from the request, mirroring the pattern already used in PortfolioController. A BadRequestException is thrown if the user identity is missing.

#157 — Add a fileFilter allowlist to the multer interceptor

Problem: POST /files/upload accepted any MIME type up to 50 MB — executables, HTML, SVG, shell scripts — creating a stored XSS and malware distribution vector.

Fix: Added a fileFilter to the FileInterceptor options backed by a new FILE_ALLOWED_MIME_TYPES environment variable. The default allowlist covers images, documents, video, audio, and archives (mirroring FileValidationService). Disallowed types are rejected with 400 Unsupported file type: <mime> before the file reaches storage.

#158 — Fix CI artifact upload paths

Problem: The openapi job generated SDKs into sdks/typescript and sdks/python, but the artifact upload step pointed at client/ — a directory the workflow never created. The Python client was never uploaded at all.

Fix: Changed the TypeScript client upload path from client/ to sdks/typescript and added a new step to upload the Python client from sdks/python. A push to main now produces three non-empty artifacts: openapi-spec, typescript-client, and python-client.

Testing

  • All existing tests pass
  • The @CurrentUser() pattern is reused from the existing codebase — no new abstractions

Notes

  • The FILE_ALLOWED_MIME_TYPES default matches the FileValidationService allowlist exactly, so the multer filter and the post-upload validation are consistent
  • The fileSize limit remains unchanged at 50 MB
  • The CI job continues to run only on push events (the if: github.event_name == 'push' guard is preserved)

Closes #156
Closes #157
Closes #158

…ct paths

Three security and CI fixes:

Issue SourceXXL#156 - Replace hardcoded userId=system with authenticated user identity
- FileUploadController: use @currentuser() decorator to extract user.id
- WebhookController: same pattern for all 8 endpoints
- Both controllers now throw BadRequestException if user identity missing

Issue SourceXXL#157 - Add fileFilter allowlist to multer interceptor
- Added FILE_ALLOWED_MIME_TYPES env var with comprehensive default
- fileFilter rejects disallowed MIME types with 400 before storage
- Configurable via environment without code changes

Issue SourceXXL#158 - Fix CI artifact upload paths
- Changed TypeScript client path from client/ to sdks/typescript
- Added Python client artifact upload step for sdks/python
- All three artifacts now upload correctly

Closes SourceXXL#156
Closes SourceXXL#157
Closes SourceXXL#158
- Import ConfigService from @nestjs/config instead of @nestjs/common
- Add null guard for file object in fileFilter callback
- Reorder parameters so required @currentuser comes before optional query params
TypeScript control flow cannot narrow file to non-null across cb() call boundaries. The guard clause already ensures file is non-null, so the assertion is safe.
TypeScript could not narrow file to non-null across cb() boundaries. Extracting to a local const f ensures proper control flow narrowing.
@memplethee-lab
memplethee-lab merged commit 843ea52 into SourceXXL:main Oct 12, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant