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
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Every release corresponds to a `staging` to `main` pull request and a matching
`vX.Y.Z` tag on `main`.

## [0.2.1] - 2026-07-01

### Security

- Resolved Supabase Security Advisor warnings originating from our schema:
`set_updated_at` is now `security invoker` with an empty `search_path` (fixes
"function search path mutable"), and the public contact-form insert policy is
bounded (non-empty, sane lengths) instead of `with check (true)` (fixes "RLS
policy always true"). Added as `supabase/migrations/0002_security_advisor.sql`.

## [0.2.0] - 2026-07-01

### Added
Expand Down Expand Up @@ -124,6 +134,7 @@ marketing site rebrand.
- Invalid nested `<a>` in template cards (hydration error).
- Tolerate a missing `text_align` column before the migration runs.

[0.2.1]: https://github.com/BiSemaphore/binarysemaphore.com/releases/tag/v0.2.1
[0.2.0]: https://github.com/BiSemaphore/binarysemaphore.com/releases/tag/v0.2.0
[0.1.4]: https://github.com/BiSemaphore/binarysemaphore.com/releases/tag/v0.1.4
[0.1.3]: https://github.com/BiSemaphore/binarysemaphore.com/releases/tag/v0.1.3
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "binarysemaphore",
"version": "0.2.0",
"version": "0.2.1",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
41 changes: 41 additions & 0 deletions supabase/migrations/0002_security_advisor.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Address Supabase Security Advisor warnings that originate from our schema.
-- Safe to re-run (idempotent).

-- 1) "Function Search Path Mutable" on public.set_updated_at.
-- Pin the function to security invoker with an empty search_path so it can't be
-- hijacked by a caller's search_path. now() is in pg_catalog (always resolved).
create or replace function public.set_updated_at()
returns trigger
language plpgsql
security invoker
set search_path = ''
as $$
begin
new.updated_at = now();
return new;
end;
$$;

-- 2) "RLS Policy Always True" on public.contact_messages.
-- Replace `with check (true)` with bounded validation. Still an open, insert-only
-- public form (anon + authenticated), but the payload must be non-empty and sane.
drop policy if exists "Anyone can submit a contact message" on public.contact_messages;
create policy "Anyone can submit a contact message"
on public.contact_messages
for insert
to anon, authenticated
with check (
char_length(name) between 1 and 200
and char_length(email) between 3 and 320
and char_length(message) between 1 and 5000
);

-- Note: warnings for public.rls_auto_enable() (SECURITY DEFINER, world-executable)
-- are NOT from this repo — that function was created directly in the dashboard.
-- If it is unused, drop it in the SQL editor after confirming:
-- drop function if exists public.rls_auto_enable();
-- Or, to keep it but stop anon/authenticated from executing it:
-- revoke execute on function public.rls_auto_enable() from anon, authenticated, public;
--
-- "Leaked Password Protection Disabled" is an Auth setting (Authentication ->
-- Policies): a dashboard toggle, low risk here since sign-in is OAuth-only.
16 changes: 14 additions & 2 deletions supabase/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ create policy "Anyone can submit a contact message"
on public.contact_messages
for insert
to anon, authenticated
with check (true);
-- Bounded instead of `true`: still an open, insert-only public form, but the
-- payload must be non-empty and within sane sizes (blocks empty/huge spam and
-- satisfies the "RLS policy always true" advisor).
with check (
char_length(name) between 1 and 200
and char_length(email) between 3 and 320
and char_length(message) between 1 and 5000
);

-- Resume builder (resume.binarysemaphore.com). One row per saved resume; the
-- editable document lives in `content` (jsonb, shape = ResumeContent in
Expand Down Expand Up @@ -83,10 +90,15 @@ create policy "Owners can delete their resumes"
on public.resumes for delete to authenticated
using (auth.uid() = user_id);

-- Keep updated_at current on every write.
-- Keep updated_at current on every write. `security invoker` + an empty
-- search_path pin the function so it can't be hijacked via a mutable search
-- path (resolves the "function search path mutable" advisor). now() lives in
-- pg_catalog, which is always resolved, so no qualification is needed.
create or replace function public.set_updated_at()
returns trigger
language plpgsql
security invoker
set search_path = ''
as $$
begin
new.updated_at = now();
Expand Down