-
Notifications
You must be signed in to change notification settings - Fork 6
fix: profile editor media sync #947
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
|
||
| export class AddIsPublicToFiles1775700000000 implements MigrationInterface { | ||
| name = 'AddIsPublicToFiles1775700000000' | ||
|
|
||
| public async up(queryRunner: QueryRunner): Promise<void> { | ||
| await queryRunner.query(`ALTER TABLE "files" ADD "isPublic" boolean NOT NULL DEFAULT false`); | ||
| } | ||
|
|
||
| public async down(queryRunner: QueryRunner): Promise<void> { | ||
| await queryRunner.query(`ALTER TABLE "files" DROP COLUMN "isPublic"`); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -80,6 +80,7 @@ app.get("/api/auth/offer", authController.getOffer); | |
| app.post("/api/auth", authController.login); | ||
| app.get("/api/auth/sessions/:id", authController.sseStream); | ||
| app.post("/api/webhook", webhookController.handleWebhook); | ||
| app.get("/api/public/files/:id", fileController.publicPreview); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Public route requires access control in service layer. This route correctly bypasses authentication middleware, but the underlying Before merging, either:
🤖 Prompt for AI Agents |
||
|
|
||
| // Protected routes (auth required) | ||
| app.use(authMiddleware); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,6 +114,20 @@ export class FileService { | |
| return savedFile; | ||
| } | ||
|
|
||
| async getFileByIdPublic(id: string): Promise<File | null> { | ||
| const file = await this.fileRepository.findOne({ | ||
| where: { id, isPublic: true }, | ||
| }); | ||
| if (!file || file.name === SOFT_DELETED_FILE_NAME) { | ||
| return null; | ||
| } | ||
| return file; | ||
| } | ||
|
Comment on lines
+117
to
+125
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Search for isPublic or public flag in File entity
rg -n 'isPublic|public' platforms/file-manager/api/src/database/entities/File.ts
# Check if there's any authorization in the public preview flow
ast-grep --pattern 'getFileByIdPublic($_) {
$$$
}'Repository: MetaState-Prototype-Project/prototype Length of output: 63 🏁 Script executed: # Check if File.ts exists and read its structure
find platforms/file-manager -name "File.ts" -o -name "File.entity.ts" -o -name "file.entity.ts" | head -20Repository: MetaState-Prototype-Project/prototype Length of output: 137 🏁 Script executed: # Read the File entity to see its actual properties
cat -n platforms/file-manager/api/src/database/entities/File.ts | head -100Repository: MetaState-Prototype-Project/prototype Length of output: 2400 🏁 Script executed: # Read FileService.ts around the method to see full context and any authorization
cat -n platforms/file-manager/api/src/services/FileService.ts | sed -n '100,150p'Repository: MetaState-Prototype-Project/prototype Length of output: 1857 🏁 Script executed: # Search for public endpoints related to files
rg -n 'getFileByIdPublic|public/files' platforms/file-manager/api/src/controllers/ -A 5 -B 2Repository: MetaState-Prototype-Project/prototype Length of output: 839 🏁 Script executed: # Search for any isPublic, isPrivate, visibility, or permission fields in File entity
rg -n 'Column\|@' platforms/file-manager/api/src/database/entities/File.tsRepository: MetaState-Prototype-Project/prototype Length of output: 63 🏁 Script executed: # Read FileController around the public endpoint
cat -n platforms/file-manager/api/src/controllers/FileController.ts | sed -n '570,600p'Repository: MetaState-Prototype-Project/prototype Length of output: 1404 🏁 Script executed: # Search for route decorators/middleware before the handler
rg -B 10 'getFileByIdPublic' platforms/file-manager/api/src/controllers/FileController.ts | head -30Repository: MetaState-Prototype-Project/prototype Length of output: 413 🏁 Script executed: # Check if there's a route definition with auth decorators
rg -B 5 'publicPreview' platforms/file-manager/api/src/controllers/FileController.ts | grep -E '@|router\.'Repository: MetaState-Prototype-Project/prototype Length of output: 63 🏁 Script executed: # Search for any middleware or auth decorators on the route
rg 'router.*publicPreview|app.*publicPreview|@' platforms/file-manager/api/src -A 2 -B 2Repository: MetaState-Prototype-Project/prototype Length of output: 38106 🏁 Script executed: # Check route definitions
find platforms/file-manager -name "*route*" -o -name "*router*" | head -10Repository: MetaState-Prototype-Project/prototype Length of output: 121 Critical: Unauthenticated access to all files without distinguishing public vs. private. The The controller's own comment acknowledges this relies on "unguessable capability tokens" (UUIDs). However, security through obscurity is insufficient; file IDs may leak through URLs, logs, client-side code, or error messages, exposing private user documents. Add an Recommended fix async getFileByIdPublic(id: string): Promise<File | null> {
const file = await this.fileRepository.findOne({
- where: { id },
+ where: { id, isPublic: true },
});
if (!file || file.name === SOFT_DELETED_FILE_NAME) {
return null;
}
return file;
}🤖 Prompt for AI Agents |
||
|
|
||
| async setFilePublic(id: string, isPublic: boolean): Promise<void> { | ||
| await this.fileRepository.update(id, { isPublic }); | ||
| } | ||
|
|
||
| async getFileById(id: string, userId: string): Promise<File | null> { | ||
| const file = await this.fileRepository.findOne({ | ||
| where: { id }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { MigrationInterface, QueryRunner } from "typeorm"; | ||
|
|
||
| export class RenameAvatarBannerColumns1775600000000 implements MigrationInterface { | ||
| name = 'RenameAvatarBannerColumns1775600000000' | ||
|
|
||
| public async up(queryRunner: QueryRunner): Promise<void> { | ||
| await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "avatarFileId" TO "avatar"`); | ||
| await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "bannerFileId" TO "banner"`); | ||
| } | ||
|
|
||
| public async down(queryRunner: QueryRunner): Promise<void> { | ||
| await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "avatar" TO "avatarFileId"`); | ||
| await queryRunner.query(`ALTER TABLE "users" RENAME COLUMN "banner" TO "bannerFileId"`); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security concern inherited from service layer.
This endpoint exposes the security issue identified in
FileService.getFileByIdPublic— any non-deleted file becomes publicly accessible without authorization. See the review comment onFileService.tsfor the recommended fix.🤖 Prompt for AI Agents