feat(security): promove secure-upload de public para authenticated#68
Conversation
…ardening)
Fecha vetor onde a edge usa SERVICE_ROLE_KEY mas aceitava chamadas anônimas.
MUDANÇAS
1. edge-authz-manifest.ts
- secure-upload: public → authenticated
- rationale atualizado: "exige JWT (uso de SERVICE_ROLE_KEY interno;
auditoria em file_scan_logs com user_id obrigatório)"
2. secure-upload/index.ts
- Adiciona authenticateRequest(req) no início do handler
- Retorna 401 imediato (via authErrorResponse) se anon ou JWT inválido
- Reusa auth.localServiceClient (já é SERVICE_ROLE) em vez de criar
supabaseAdmin manualmente
- Tipo ScanLog.user_id: string | null → string (sempre presente)
- Eventos do logger agora incluem user_id em todos:
request_start / security_check_failed / upload_blocked_malware /
upload_ok / upload_failed
- Novo evento auth_failed para auditoria de tentativas anônimas
COMPATIBILIDADE
- Callers no frontend (SecureUploadManager, ImageUploadButton) já
enviam JWT automaticamente via supabase.functions.invoke() —
sem mudança necessária.
- Body de resposta inalterado para callers autenticados.
- Anônimos antes obtinham 200 com user_id=null no log; agora obtem
401 com {error: "Token de autenticação ausente"}.
VALIDAÇÃO
- npm run typecheck: 0 erros
- check-edge-authorization: 85/85 (authenticated 24→25, public 22→21)
- check-edge-structured-logging: OK (84 legadas, 1 migrada)
- check-edge-request-id-propagation: OK (9 críticas validadas)
DEPENDS-ON: #67
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
Pull request overview
Promove a edge function secure-upload de public para authenticated, tornando obrigatória a autenticação via JWT antes de executar upload/scan (reduzindo superfície de abuso de Storage via chamada anônima), e alinhando a edge ao uso real pelos callers autenticados do frontend.
Changes:
- Move
secure-uploadnoedge-authz-manifest.tsdepublicparaauthenticated, com rationale atualizado. - Atualiza
secure-upload/index.tspara exigirauthenticateRequest(req)e reutilizarauth.localServiceClient(service-role) em vez de criar client manual. - Endurece auditoria tipando
ScanLog.user_idcomo obrigatório e adicionauser_idaos eventos relevantes do logger.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| supabase/functions/secure-upload/index.ts | Passa a exigir autenticação, remove lookup opcional de user e reforça logs/auditoria com user_id. |
| supabase/functions/_shared/edge-authz-manifest.ts | Reclassifica secure-upload como authenticated no SSOT de autorização. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const supabaseAdmin = auth.localServiceClient; | ||
| log.info("request_start", { user_id: auth.userId }); |
Contexto
No PR #67 a edge
secure-uploadfoi registrada no manifest comopublicpara não quebrar comportamento existente, mas com uma nota explícita sugerindo promoção futura paraauthenticated. Este PR é esse follow-up.Problema
A edge
secure-upload:SUPABASE_SERVICE_ROLE_KEY(bypassa RLS, escreve em qualquer bucket de Storage)SecureUploadManageremadmin/security,ImageUploadButtonem forms autenticados)Isso constitui um vetor: qualquer um na internet pode chamar a edge sem JWT e fazer upload pra
personalization-imagesouquarantine. O VirusTotal mitiga malware mas não impede storage abuse / cost amplification / fingerprinting.Solução
edge-authz-manifest.tssecure-upload/index.tsReusa o helper SSOT
authenticateRequest(mesmo usado por outras 24 edges authenticated):Bonus de limpeza
createClient(SERVICE_ROLE_KEY)— reusaauth.localServiceClientScanLog.user_id: string | null→string(sempre presente; antes podia ser null para anônimos)user_id(correlation com auth.users)auth_failedpara auditoria de tentativas não autenticadasCompatibilidade
SecureUploadManager.tsx(admin/security)ImageUploadButton.tsx(forms autenticados)user_id: nullno log{error: "Token de autenticação ausente"}Nenhum caller real é afetado — todos já enviam
Authorization: Bearer <session.access_token>automaticamente quando o usuário está logado.Validação
Por que stacked sobre o #67
Este PR depende das mudanças do #67 (entrada no manifest + structured logger). Quando o #67 mergear no main, eu faço rebase deste PR e ele vira um diff limpo de ±23 linhas.
DEPENDS-ON
Sugestões para PRs futuros (não cobertos aqui)
tests/security/edge-authz-bypass.test.tspara também varrer categoriaauthenticated(hoje só cobresupervisor/dev). Isso garantiria que regressões como esta sejam capturadas automaticamente.runBotProtection(presente em outras edges públicas comoimage-proxyeanalyze-logo-colors) com rate-limit por usuário emsecure-upload, para limitar abuso por usuário comprometido.🤖 Gerado pelo Claude (Opus 4.7) operando via container
claude-codena VPS Atomica + Portainer MCP.