Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This partial has {{ .my-var }}, {{ .another_var }}, and {{ .myVar123 }}.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This partial has {{ .var1 }}, {{ .var2 }}, and {{ .var3 }}.
69 changes: 66 additions & 3 deletions apps/docs/content/guides/database/hardening-data-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ We highly recommend creating a `private` schema for storing tables that you do n

If your `public` schema is used by other tools as a default space, you might want to lock down this schema. This helps prevent accidental exposure of data that's automatically added to `public`.

There are two levels of security hardening for the Data API:
There are several levels of security hardening for the Data API:

- Disabling the Data API entirely. This is recommended if you _never_ need to access your database via Supabase client libraries or the REST and GraphQL endpoints.
- Removing the `public` schema from the Data API and replacing it with a custom schema (such as `api`).
- [Disabling the Data API entirely](#disabling-the-data-api). This is recommended if you _never_ need to access your database via Supabase client libraries or the REST and GraphQL endpoints.
- [Exposing a custom schema](#exposing-a-custom-schema-instead-of-public) instead of `public`, giving you explicit control over what is accessible.
- [Automatically enabling RLS on new tables](#automatically-enabling-rls-on-new-tables) using an event trigger.
- [Adjusting table-level grants](#table-level-grants) to control which roles can access specific tables.

## Disabling the Data API

Expand Down Expand Up @@ -72,3 +74,64 @@ Any data, views, or functions that should be exposed need to be deliberately put
grant select on table api.<your_table> to anon;
grant select, insert, update, delete on table api.<your_table> to authenticated;
```

## Automatically enabling RLS on new tables

Tables created via the Supabase Dashboard have RLS enabled by default. However, if you or your team create tables using the SQL editor, migrations, or an external tool, RLS will not be enabled automatically.

You can use an [event trigger](/docs/guides/database/postgres/event-triggers#example-trigger-function---auto-enable-row-level-security) to automatically enable RLS whenever a new table is created in the `public` schema. This ensures that no table is accidentally left exposed without RLS protection.

## Table-level grants

By default, tables in the `public` schema are granted full access (`SELECT`, `INSERT`, `UPDATE`, `DELETE`) to the `anon` and `authenticated` roles. This allows the Data API to query those tables on behalf of users.

You can adjust these privileges on a per-table basis to restrict which operations each role can perform. For example, you might want to:

- Allow `anon` users to only `SELECT` from a table, preventing anonymous writes.
- Prevent `anon` users from accessing a table entirely, making it available only to authenticated users.
- Restrict `authenticated` users to `SELECT` and `INSERT` only, preventing updates and deletes.

<Admonition type="tip">

Table-level privileges work alongside [Row Level Security](/docs/guides/database/postgres/row-level-security). Privileges control _which operations_ are possible, while RLS policies control _which rows_ are accessible. For full protection, use both: restrict privileges to limit operation types, and use RLS policies to control row-level access.

</Admonition>

### Adjusting table-level grants via the Dashboard

<Admonition type="note">

Adjusting table-level privileges via the Dashboard is currently in beta and will be available via gradual roll-out.

</Admonition>

1. Go to [**Table Editor**](/dashboard/project/_/editor) in the Supabase Dashboard.
2. Select the table you want to configure.
3. Click the vertical dots icon to open the table menu and select "Edit table".
4. Under **Data API Access**, click the settings icon to open **Adjust API privileges per role**.
5. For each role (`anon` and `authenticated`), select or deselect the privileges you want to grant.
6. Click **Save**.

### Adjusting table-level grants via SQL

You can also adjust privileges using SQL. For example, to allow only `SELECT` access for `anon` on a table:

```sql
-- Revoke all existing privileges
revoke all on table public.your_table from anon;

-- Grant only SELECT
grant select on table public.your_table to anon;
```

To remove all access for `anon` from a table:

```sql
revoke all on table public.your_table from anon;
```

To restore full access:

```sql
grant select, insert, update, delete on table public.your_table to anon;
```
9 changes: 9 additions & 0 deletions apps/docs/content/guides/storage/uploads/file-limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@ Additionally, you can specify the maximum file size on a per [bucket level](/doc
## Per bucket restrictions

You can have different restrictions on a per bucket level such as restricting the file types (e.g. `pdf`, `images`, `videos`) or the maximum file size, which should be lower than the global limit. To apply these limits on a bucket level see [Creating Buckets](/docs/guides/storage/buckets/creating-buckets#restricting-uploads).

## File name restrictions

File names can only include the following characters:

- **Alphanumeric**: `A-Z`, `a-z`, `0-9`
- **Punctuation**: `_` (underscore), `-` (hyphen), `.` (dot), `'` (apostrophe), `,` (comma)
- **Special characters**: `!`, `*`, `&`, `$`, `@`, `=`, `;`, `:`, `+`, `?`, `(`, `)`
- **Whitespace**
38 changes: 29 additions & 9 deletions apps/docs/content/troubleshooting/database-api-42501-errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,40 @@ API roles cannot access certain schemas, most notably `auth` and `vault`. This r

If you created a custom schema, you will have to give the Database API permission to query it. Follow our [Using Custom Schemas guide](/docs/guides/api/using-custom-schemas) for more directions.

### Revoked object level access:
### Missing table-level privileges

In rare cases, users may accidentally revoke object-level access in `public` from their API roles. To regrant full visibility, run the below code:
If you see an error like `permission denied for table your_table`, the querying role may not have the required privilege for the operation.

By default, tables in the `public` schema are granted `SELECT`, `INSERT`, `UPDATE`, and `DELETE` to the `anon` and `authenticated` roles. However, these privileges can be adjusted via the [Dashboard Table Editor](/dashboard/project/_/editor) or via SQL.

To check the current privileges on a table:

```sql
select grantee, privilege_type
from information_schema.role_table_grants
where table_name = 'your_table';
```

To grant a specific privilege to a role:

```sql
grant select on table public.your_table to anon;
```

To grant all privileges:

```sql
grant usage on schema public to anon, authenticated, service_role;
grant all on all tables in schema public to anon, authenticated, service_role;
grant all on all routines in schema public to anon, authenticated, service_role;
grant all ON all sequences in schema public to anon, authenticated, service_role;
alter default privileges for role postgres in schema public grant all on tables to anon, authenticated, service_role;
alter default privileges for role postgres in schema public grant all on routines to anon, authenticated, service_role;
alter default privileges for role postgres in schema public grant all on sequences to anon, authenticated, service_role;
grant select, insert, update, delete on table public.your_table to anon, authenticated;
```

<Admonition type="note">

Granting privileges allows access to your table through the Data API, so you should ensure you [enable RLS](/docs/guides/database/postgres/row-level-security) and write appropriate policies to protect your data.

For more information, see [Adjusting table-level privileges](/docs/guides/database/hardening-data-api#adjusting-table-level-privileges).

</Admonition>

### Configured column-level restrictions

If you've set column-based access in the [Dashboard](/dashboard/project/_/database/column-privileges) or via SQL, queries will fail with a `42501` error when accessing restricted columns. This includes using `select *`, as it expands to include forbidden columns.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ topics = ["cli", "database", "functions"]
keywords = ["functions", "typescript", "deno"]
---

This guide shows how you can transfer your Edge Functions from one project to another using the Supabase CLI.
This guide shows how you can transfer your Edge Functions from one project to another using the Supabase CLI or the Supabase Dashboard.

## Pre-requisites

Expand All @@ -14,7 +14,7 @@ To follow through this guide, you need the following:
- You must have installed the [Supabase CLI tool](/docs/guides/local-development/cli/getting-started#installing-the-supabase-cli)
- Both source and target projects must be active

## Steps:
## Steps (using the Supabase CLI):

1. Login to your Supabase account (the account with the functions) using your terminal

Expand Down Expand Up @@ -57,3 +57,22 @@ supabase functions deploy --project-ref your_target_project_ref
```

This deploys all functions within the `supabase/functions` to the target project. You can confirm by checking your Edge Functions on [the project dashboard](/dashboard/project/_/functions)

## Steps (using the Supabase Dashboard):

1. In the source project, navigate to **Edge Functions** from the side menu

2. Using the `Download` button, download your desired function as zip:
![Download Edge Function](/docs/img/troubleshooting/download-edge-function-via-dashboard.gif)

3. In the target project, navigate to **Edge Functions** from the side menu

4. Click on the `Deploy a new function` button, select **Via Editor** operation

5. Drag and drop your downloaded function (the zip function from step 2) into the editor

6. Add your function name and click on the `Deploy function` button to deploy the function:

![Upload Edge Function](/docs/img/troubleshooting/upload-edge-function-via-dashboard.gif)

With this, you can transfer your edge functions between your Supabase projects.
111 changes: 111 additions & 0 deletions apps/docs/features/directives/Partial.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,115 @@ Some more text.
const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(/valid JSON/)
})

it('should error when required variable is missing', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/variables.mdx" />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(
/Missing required variable in \$Partial ".*variables\.mdx": "var"/
)
})

it('should error when unexpected variable is provided', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/variables.mdx" variables={{ "var": "correct", "extra": "unexpected" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(
/Unexpected variable in \$Partial ".*variables\.mdx": "extra"/
)
})

it('should error with detailed message for multiple missing variables', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/multiple-variables.mdx" variables={{ "var1": "value1" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(
/Missing required variables.*"var2".*"var3".*Expected variables.*"var1".*"var2".*"var3".*Provided variable: "var1"/s
)
})

it('should error with detailed message for multiple unexpected variables', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/variables.mdx" variables={{ "var": "correct", "extra1": "wrong", "extra2": "also wrong" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(
/Unexpected variables.*"extra1".*"extra2".*Expected variable: "var".*Provided variables.*"var".*"extra1".*"extra2"/s
)
})

it('should succeed when all variables match exactly', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/multiple-variables.mdx" variables={{ "var1": "first", "var2": "second", "var3": "third" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
const transformed = await partialsRemark()(mdast)
const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })

expect(output).toContain('first')
expect(output).toContain('second')
expect(output).toContain('third')
})

it('should support hyphenated variable names', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/hyphenated-variables.mdx" variables={{ "my-var": "hyphenated value", "another_var": "underscored value", "myVar123": "alphanumeric value" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
const transformed = await partialsRemark()(mdast)
const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] })

expect(output).toContain('hyphenated value')
expect(output).toContain('underscored value')
expect(output).toContain('alphanumeric value')
})

it('should error when hyphenated variable is missing', async () => {
const markdown = `
# Embed partial

<$Partial path="/_fixtures/hyphenated-variables.mdx" variables={{ "my-var": "value" }} />

Some more text.
`.trim()

const mdast = fromDocsMarkdown(markdown)
await expect(partialsRemark()(mdast)).rejects.toThrowError(
/Missing required variables.*"another_var".*"myVar123".*Expected variables.*"my-var".*"another_var".*"myVar123".*Provided variable: "my-var"/s
)
})
})
Loading
Loading