Skip to main content
interlace
SecurityPlugin: supabase-securityRules

no-public-storage-bucket

Do not create a Supabase storage bucket as public

Do not create a Supabase storage bucket as public.

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
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-supabase-security"],  "rules": {    "supabase-security/no-public-storage-bucket": "error"  }}
vite.config.ts
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.