no-public-storage-bucket
Do not create a Supabase storage bucket as public
Do not create a Supabase storage bucket as public.
- CWE: CWE-732 — Incorrect Permission Assignment for Critical Resource
- OWASP: A01:2021 — Broken Access Control
- CVSS: 7.5 (High)
- Recommended: off — enabled in
strict
Why
A public bucket serves every object in it to anyone with the URL: no session, no RLS check, no expiry. That is a decision about the bucket's whole future contents, taken once, in one argument, at creation — long before anyone uploads the file that should not have been public.
Object names are guessable far more often than teams expect: sequential ids, e-mail addresses, original filenames from a user's disk. "Nobody knows the URL" is not access control.
Rule details
Fires only in files importing a @supabase/* package. Reports createBucket() or updateBucket() whose options object sets public: true — written inline, or through a same-file const options object, flag or spread.
Not in recommended, on purpose: a bucket of site assets is legitimately public, and this rule cannot tell that from a bucket of invoices. It is in strict, where the intent is to review every one.
Abstains on a spread whose source this file cannot see and on a value that is not true through a const — both are unreadable from the call site. As at runtime, the last write wins: { public: true, ...overrides } is whatever overrides says.
Incorrect
import { createClient } from '@supabase/supabase-js';
await db.storage.createBucket('user-documents', { public: true });Correct
import { createClient } from '@supabase/supabase-js';
await db.storage.createBucket('user-documents');
// Hand out time-limited access per object instead.
const { data } = await db.storage
.from('user-documents')
.createSignedUrl(path, 60);Further reading
Use this rule
npm install -D oxlint-plugin-supabase-security{ "jsPlugins": ["oxlint-plugin-supabase-security"], "rules": { "supabase-security/no-public-storage-bucket": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-supabase-security'], rules: { 'supabase-security/no-public-storage-bucket': 'error', }, },});oxlint-plugin-supabase-security re-exports eslint-plugin-supabase-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.