From 0cbd0a19e84d4a8fc6b9005fc450ec2b66f206fd Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Apr 2024 16:03:38 +0800 Subject: [PATCH 01/12] feat: add lint for anonymous users & docs --- docs/0011_auth_allow_anonymous_sign_ins.md | 31 +++++++++++ lints/0011_auth_allow_anonymous_sign_ins.sql | 56 ++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 docs/0011_auth_allow_anonymous_sign_ins.md create mode 100644 lints/0011_auth_allow_anonymous_sign_ins.sql diff --git a/docs/0011_auth_allow_anonymous_sign_ins.md b/docs/0011_auth_allow_anonymous_sign_ins.md new file mode 100644 index 0000000..cc3a35c --- /dev/null +++ b/docs/0011_auth_allow_anonymous_sign_ins.md @@ -0,0 +1,31 @@ +Level: INFO + +### Rationale + +Anonymous users use the same `authenticated` Postgres role as permanent users when accessing the database. If you have enabled anonymous sign-in for your project, existing RLS policies may allow unintended access to an anonymous user's JWT. + +### Difference between an anonymous user and a permanent user + +An anonymous user is a user created through Supabase Auth. It is just like a permanent user, except the user can't access their account if they sign out, clear browsing data or use another device. An anonymous user can be differentiated from a permanent user by checking if the `is_anonymous` claim is true. These claims are returned by the `auth.jwt()` function. + +### How to Resolve + +To mitigate the risk, determine if existing row level security (RLS) policies are meant to allow access to anonymous users. Affected policies include those that are associated to the `authenticated` or `public` roles, and members of those roles that inherit privileges. + +For example, consider the policy: + +```sql +create policy "allow_access_to_authenticated" on documents +as restrictive +to authenticated +using (true); +``` + +In this policy, any JWT that contains the authenticated role will be allowed to access the documents table. If we want to restrict access to permanent users only, we can modify the policy to: + +```sql +create policy "allow_access_to_permanent_users" on documents +as restrictive +to authenticated +using ( (select (auth.jwt()->>'is_anonymous')::boolean) is false ); +``` diff --git a/lints/0011_auth_allow_anonymous_sign_ins.sql b/lints/0011_auth_allow_anonymous_sign_ins.sql new file mode 100644 index 0000000..43bf024 --- /dev/null +++ b/lints/0011_auth_allow_anonymous_sign_ins.sql @@ -0,0 +1,56 @@ +create view lint."0011_auth_allow_anonymous_sign_ins" as + +with recursive role_members as ( + select + roleid, + member + from pg_catalog.pg_auth_members + where roleid = (select oid from pg_roles where rolname = 'authenticated') + union + select + am.roleid, + am.member + from pg_catalog.pg_auth_members as am + inner join role_members as rm on am.roleid = rm.member +), + +member_names as ( + select r.rolname from pg_roles as r + inner join role_members as m on r.oid = m.member +) + +select + 'auth_allow_anonymous_sign_ins' as name, + 'INFO' as level, + 'EXTERNAL' as facing, + 'Detects row level security (RLS) policies that allow access to anonymous users.' as description, + 'https://supabase.github.io/splinter/0011_auth_allow_anonymous_sign_ins' as remediation, + format( + 'Table \`%s.%s\` has policies enforced on roles that allow access to anonymous users. Policies include \`%s\`', + n.nspname, + c.relname, + array_agg(p.policyname order by p.policyname) + ) as detail, + jsonb_build_object( + 'schema', n.nspname, + 'name', c.relname, + 'type', 'table' + ) as metadata, + format( + 'auth_allow_anonymous_sign_ins_%s_%s', + n.nspname, + c.relname + ) as cache_key +from pg_catalog.pg_policies as p +inner join pg_catalog.pg_class as c on p.tablename = c.relname +inner join pg_catalog.pg_namespace as n on c.relnamespace = n.oid +where + ( + p.roles = array['public'::name] -- public roles + or p.roles = array['authenticated'::name] -- authenticated roles + or exists ( + select rolname from member_names where rolname = any(roles) + ) -- roles that are members of authenticated + ) + and replace(p.qual, ' ', '') !~ 'auth\.jwt\(\)->>''is_anonymous''::text' +group by n.nspname, c.relname From 172f6f21d544ded57bfb283404e4a99ffe6416ce Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Apr 2024 16:04:04 +0800 Subject: [PATCH 02/12] fix: add tests --- test/expected/0011_auth_allow_anonymous_sign_ins.out | 8 ++++++++ test/sql/0011_auth_allow_anonymous_sign_ins.sql | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 test/expected/0011_auth_allow_anonymous_sign_ins.out create mode 100644 test/sql/0011_auth_allow_anonymous_sign_ins.sql diff --git a/test/expected/0011_auth_allow_anonymous_sign_ins.out b/test/expected/0011_auth_allow_anonymous_sign_ins.out new file mode 100644 index 0000000..60e4808 --- /dev/null +++ b/test/expected/0011_auth_allow_anonymous_sign_ins.out @@ -0,0 +1,8 @@ +begin; + -- 0 issues + select * from lint."0011_auth_allow_anonymous_sign_ins"; + name | level | facing | description | detail | remediation | metadata | cache_key +------+-------+--------+-------------+--------+-------------+----------+----------- +(0 rows) + +rollback; diff --git a/test/sql/0011_auth_allow_anonymous_sign_ins.sql b/test/sql/0011_auth_allow_anonymous_sign_ins.sql new file mode 100644 index 0000000..0ab5aa4 --- /dev/null +++ b/test/sql/0011_auth_allow_anonymous_sign_ins.sql @@ -0,0 +1,6 @@ +begin; + + -- 0 issues + select * from lint."0011_auth_allow_anonymous_sign_ins"; + +rollback; From 1a3098b4ba5cd52d46be0176c8d8270bc3e7fd2c Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Apr 2024 16:22:12 +0800 Subject: [PATCH 03/12] update installcheck --- bin/installcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/installcheck b/bin/installcheck index f7b5808..2f4e30a 100755 --- a/bin/installcheck +++ b/bin/installcheck @@ -49,7 +49,7 @@ else fi # Execute the test fixtures -psql -v ON_ERROR_STOP= -f test/fixtures.sql -f lints/0001*.sql -f lints/0002*.sql -f lints/0003*.sql -f lints/0004*.sql -f lints/0005*.sql -f lints/0006*.sql -f lints/0007*.sql -f lints/0008*.sql -f lints/0009*.sql -f lints/0010*.sql -d contrib_regression +psql -v ON_ERROR_STOP= -f test/fixtures.sql -f lints/0001*.sql -f lints/0002*.sql -f lints/0003*.sql -f lints/0004*.sql -f lints/0005*.sql -f lints/0006*.sql -f lints/0007*.sql -f lints/0008*.sql -f lints/0009*.sql -f lints/0010*.sql -f lints/0011*.sql-d contrib_regression # Run tests ${REGRESS} --use-existing --dbname=contrib_regression --inputdir=${TESTDIR} ${TESTS} From 32a07382e1db5d666edf19c3764066b52f5c5db0 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Apr 2024 16:22:18 +0800 Subject: [PATCH 04/12] update splinter --- splinter.sql | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/splinter.sql b/splinter.sql index 8733dad..017d081 100644 --- a/splinter.sql +++ b/splinter.sql @@ -541,4 +541,60 @@ where 'security_invoker=yes', 'security_invoker=on' ] - )) \ No newline at end of file + )) +union all +( +with recursive role_members as ( + select + roleid, + member + from pg_catalog.pg_auth_members + where roleid = (select oid from pg_roles where rolname = 'authenticated') + union + select + am.roleid, + am.member + from pg_catalog.pg_auth_members as am + inner join role_members as rm on am.roleid = rm.member +), + +member_names as ( + select r.rolname from pg_roles as r + inner join role_members as m on r.oid = m.member +) + +select + 'auth_allow_anonymous_sign_ins' as name, + 'INFO' as level, + 'EXTERNAL' as facing, + 'Detects row level security (RLS) policies that allow access to anonymous users.' as description, + 'https://supabase.github.io/splinter/0011_auth_allow_anonymous_sign_ins' as remediation, + format( + 'Table \`%s.%s\` has policies enforced on roles that allow access to anonymous users. Policies include \`%s\`', + n.nspname, + c.relname, + array_agg(p.policyname order by p.policyname) + ) as detail, + jsonb_build_object( + 'schema', n.nspname, + 'name', c.relname, + 'type', 'table' + ) as metadata, + format( + 'auth_allow_anonymous_sign_ins_%s_%s', + n.nspname, + c.relname + ) as cache_key +from pg_catalog.pg_policies as p +inner join pg_catalog.pg_class as c on p.tablename = c.relname +inner join pg_catalog.pg_namespace as n on c.relnamespace = n.oid +where + ( + p.roles = array['public'::name] -- public roles + or p.roles = array['authenticated'::name] -- authenticated roles + or exists ( + select rolname from member_names where rolname = any(roles) + ) -- roles that are members of authenticated + ) + and replace(p.qual, ' ', '') !~ 'auth\.jwt\(\)->>''is_anonymous''::text' +group by n.nspname, c.relname) \ No newline at end of file From a22f13faedeaad9bdd2f45a12585ac02817ae528 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Wed, 3 Apr 2024 16:29:25 +0800 Subject: [PATCH 05/12] update installcheck --- bin/installcheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/installcheck b/bin/installcheck index 2f4e30a..189a15b 100755 --- a/bin/installcheck +++ b/bin/installcheck @@ -49,7 +49,7 @@ else fi # Execute the test fixtures -psql -v ON_ERROR_STOP= -f test/fixtures.sql -f lints/0001*.sql -f lints/0002*.sql -f lints/0003*.sql -f lints/0004*.sql -f lints/0005*.sql -f lints/0006*.sql -f lints/0007*.sql -f lints/0008*.sql -f lints/0009*.sql -f lints/0010*.sql -f lints/0011*.sql-d contrib_regression +psql -v ON_ERROR_STOP= -f test/fixtures.sql -f lints/0001*.sql -f lints/0002*.sql -f lints/0003*.sql -f lints/0004*.sql -f lints/0005*.sql -f lints/0006*.sql -f lints/0007*.sql -f lints/0008*.sql -f lints/0009*.sql -f lints/0010*.sql -f lints/0011*.sql -d contrib_regression # Run tests ${REGRESS} --use-existing --dbname=contrib_regression --inputdir=${TESTDIR} ${TESTS} From 20473e65d28a4c8e791c7baf54ddb3d3dbfbee04 Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Wed, 3 Apr 2024 08:00:15 -0500 Subject: [PATCH 06/12] re-align test output --- test/expected/0012_auth_allow_anonymous_sign_ins.out | 7 ++++--- test/sql/0012_auth_allow_anonymous_sign_ins.sql | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/expected/0012_auth_allow_anonymous_sign_ins.out b/test/expected/0012_auth_allow_anonymous_sign_ins.out index 60e4808..5abb7c6 100644 --- a/test/expected/0012_auth_allow_anonymous_sign_ins.out +++ b/test/expected/0012_auth_allow_anonymous_sign_ins.out @@ -1,8 +1,9 @@ begin; -- 0 issues - select * from lint."0011_auth_allow_anonymous_sign_ins"; - name | level | facing | description | detail | remediation | metadata | cache_key -------+-------+--------+-------------+--------+-------------+----------+----------- + select * from lint."0012_auth_allow_anonymous_sign_ins"; + name | level | facing | description | remediation | detail | metadata | cache_key +------+-------+--------+-------------+-------------+--------+----------+----------- (0 rows) + rollback; diff --git a/test/sql/0012_auth_allow_anonymous_sign_ins.sql b/test/sql/0012_auth_allow_anonymous_sign_ins.sql index 0ab5aa4..4fd0834 100644 --- a/test/sql/0012_auth_allow_anonymous_sign_ins.sql +++ b/test/sql/0012_auth_allow_anonymous_sign_ins.sql @@ -1,6 +1,6 @@ begin; -- 0 issues - select * from lint."0011_auth_allow_anonymous_sign_ins"; + select * from lint."0012_auth_allow_anonymous_sign_ins"; rollback; From 2f65dec40ab8003e937814adbef746fa0c5ea37c Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Wed, 3 Apr 2024 08:08:42 -0500 Subject: [PATCH 07/12] add true positive test case --- .../0012_auth_allow_anonymous_sign_ins.sql | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/sql/0012_auth_allow_anonymous_sign_ins.sql b/test/sql/0012_auth_allow_anonymous_sign_ins.sql index 4fd0834..c19defc 100644 --- a/test/sql/0012_auth_allow_anonymous_sign_ins.sql +++ b/test/sql/0012_auth_allow_anonymous_sign_ins.sql @@ -2,5 +2,28 @@ begin; -- 0 issues select * from lint."0012_auth_allow_anonymous_sign_ins"; + + create table public.documents( id int primary key ); + + -- Create a policy for the authenticated role that would allow access to anonymous login users + -- if that feature is enabled + create policy "allow_access_to_authenticated" on public.documents + as restrictive + to authenticated + using (true); + + -- 1 issues + select * from lint."0012_auth_allow_anonymous_sign_ins"; + + drop policy "allow_access_to_authenticated" on public.documents; + + -- Resolve the issue by excluding anonymous login users + create policy "allow_access_to_permanent_users" on documents + as restrictive + to authenticated + using ( (select (auth.jwt() ->> 'is_anonymous')::boolean) is false ); + + -- 0 issues + select * from lint."0012_auth_allow_anonymous_sign_ins"; rollback; From f1d1018bdc8d7aa6a0d715c04108c7f10dad2fdd Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Wed, 3 Apr 2024 08:12:48 -0500 Subject: [PATCH 08/12] update anonymous login docs url --- lints/0012_auth_allow_anonymous_sign_ins.sql | 2 +- splinter.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lints/0012_auth_allow_anonymous_sign_ins.sql b/lints/0012_auth_allow_anonymous_sign_ins.sql index e447a14..8826821 100644 --- a/lints/0012_auth_allow_anonymous_sign_ins.sql +++ b/lints/0012_auth_allow_anonymous_sign_ins.sql @@ -24,7 +24,7 @@ select 'INFO' as level, 'EXTERNAL' as facing, 'Detects row level security (RLS) policies that allow access to anonymous users.' as description, - 'https://supabase.github.io/splinter/0011_auth_allow_anonymous_sign_ins' as remediation, + 'https://supabase.github.io/splinter/0012_auth_allow_anonymous_sign_ins' as remediation, format( 'Table \`%s.%s\` has policies enforced on roles that allow access to anonymous users. Policies include \`%s\`', n.nspname, diff --git a/splinter.sql b/splinter.sql index 81afd7e..2f7f23a 100644 --- a/splinter.sql +++ b/splinter.sql @@ -609,7 +609,7 @@ select 'INFO' as level, 'EXTERNAL' as facing, 'Detects row level security (RLS) policies that allow access to anonymous users.' as description, - 'https://supabase.github.io/splinter/0011_auth_allow_anonymous_sign_ins' as remediation, + 'https://supabase.github.io/splinter/0012_auth_allow_anonymous_sign_ins' as remediation, format( 'Table \`%s.%s\` has policies enforced on roles that allow access to anonymous users. Policies include \`%s\`', n.nspname, From 3bb49183637f1b556f6d88c95c7f36ce6da68998 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Apr 2024 14:55:01 +0800 Subject: [PATCH 09/12] add test for case sensitivity --- lints/0012_auth_allow_anonymous_sign_ins.sql | 2 +- test/expected/0012_auth_allow_anonymous_sign_ins.out | 12 +++++++++++- test/sql/0012_auth_allow_anonymous_sign_ins.sql | 11 ++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lints/0012_auth_allow_anonymous_sign_ins.sql b/lints/0012_auth_allow_anonymous_sign_ins.sql index 8826821..fa5d7e6 100644 --- a/lints/0012_auth_allow_anonymous_sign_ins.sql +++ b/lints/0012_auth_allow_anonymous_sign_ins.sql @@ -52,5 +52,5 @@ where select rolname from member_names where rolname = any(roles) ) -- roles that are members of authenticated ) - and replace(p.qual, ' ', '') !~ 'auth\.jwt\(\)->>''is_anonymous''::text' + and replace(p.qual, ' ', '') not like '%auth.jwt()%->>%is_anonymous%' group by n.nspname, c.relname diff --git a/test/expected/0012_auth_allow_anonymous_sign_ins.out b/test/expected/0012_auth_allow_anonymous_sign_ins.out index 60e1136..d558334 100644 --- a/test/expected/0012_auth_allow_anonymous_sign_ins.out +++ b/test/expected/0012_auth_allow_anonymous_sign_ins.out @@ -31,5 +31,15 @@ begin; ------+-------+--------+-------------+-------------+--------+----------+----------- (0 rows) - + -- Check if policy definition passes with case sensitive characters + create policy "allow_access_to_permanent_users_case_senstive" on documents + as restrictive + to authenticated + using ( (select (AUTH.JWT() ->> 'is_anonymous')::boolean) is false ); + -- 0 issues + select * from lint."0012_auth_allow_anonymous_sign_ins"; + name | level | facing | description | remediation | detail | metadata | cache_key +------+-------+--------+-------------+-------------+--------+----------+----------- +(0 rows) + rollback; diff --git a/test/sql/0012_auth_allow_anonymous_sign_ins.sql b/test/sql/0012_auth_allow_anonymous_sign_ins.sql index c19defc..0c4779c 100644 --- a/test/sql/0012_auth_allow_anonymous_sign_ins.sql +++ b/test/sql/0012_auth_allow_anonymous_sign_ins.sql @@ -25,5 +25,14 @@ begin; -- 0 issues select * from lint."0012_auth_allow_anonymous_sign_ins"; - + + -- Check if policy definition passes with case sensitive characters + create policy "allow_access_to_permanent_users_case_senstive" on documents + as restrictive + to authenticated + using ( (select (AUTH.JWT() ->> 'is_anonymous')::boolean) is false ); + + -- 0 issues + select * from lint."0012_auth_allow_anonymous_sign_ins"; + rollback; From 6a5f390e0610401a7c28ed7d71395e2967b16e57 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Apr 2024 15:04:18 +0800 Subject: [PATCH 10/12] update installcheck to output diff --- bin/installcheck | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bin/installcheck b/bin/installcheck index e1608fd..a9274dd 100755 --- a/bin/installcheck +++ b/bin/installcheck @@ -53,3 +53,8 @@ psql -v ON_ERROR_STOP= -f test/fixtures.sql -f lints/0001*.sql -f lints/0002*.sq # Run tests ${REGRESS} --use-existing --dbname=contrib_regression --inputdir=${TESTDIR} ${TESTS} + +if [ -s /home/splinter/regression.diffs ] +then + cat /home/splinter/regression.diffs +fi From fbf0ea8cc2385a14510e6e153963fc6961c5c63b Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Thu, 4 Apr 2024 15:07:41 +0800 Subject: [PATCH 11/12] update build artifacts --- splinter.json | 2 +- splinter.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/splinter.json b/splinter.json index f228d7c..95577f3 100644 --- a/splinter.json +++ b/splinter.json @@ -10,6 +10,6 @@ "0009_duplicate_index": "(\nselect\n 'duplicate_index' as name,\n 'WARN' as level,\n 'EXTERNAL' as facing,\n 'Detects cases where two ore more identical indexes exist.' as description,\n format(\n 'Table \\`%s.%s\\` has identical indexes %s. Drop all except one of them',\n n.nspname,\n c.relname,\n array_agg(pi.indexname order by pi.indexname)\n ) as detail,\n 'https://supabase.github.io/splinter/0009_duplicate_index' as remediation,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', case\n when c.relkind = 'r' then 'table'\n when c.relkind = 'm' then 'materialized view'\n else 'ERROR'\n end,\n 'indexes', array_agg(pi.indexname order by pi.indexname)\n ) as metadata,\n format(\n 'duplicate_index_%s_%s_%s',\n n.nspname,\n c.relname,\n array_agg(pi.indexname order by pi.indexname)\n ) as cache_key\nfrom\n pg_catalog.pg_indexes pi\n join pg_catalog.pg_namespace n\n on n.nspname = pi.schemaname\n join pg_catalog.pg_class c\n on pi.tablename = c.relname\n and n.oid = c.relnamespace\n left join pg_catalog.pg_policy p\n on p.polrelid = c.oid\nwhere\n c.relkind in ('r', 'm') -- tables and materialized views\n and n.nspname not in (\n 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'pgsodium'\n )\ngroup by\n n.nspname,\n c.relkind,\n c.relname,\n replace(pi.indexdef, pi.indexname, '')\nhaving\n count(*) > 1)", "0010_security_definer_view": "(\nselect\n 'security_definer_view' as name,\n 'WARN' as level,\n 'EXTERNAL' as facing,\n 'Detects views that are SECURITY DEFINER meaning that they ignore row level security (RLS) policies.' as description,\n format(\n 'View \\`%s.%s\\` is SECURITY DEFINER',\n n.nspname,\n c.relname\n ) as detail,\n 'https://supabase.github.io/splinter/0010_security_definer_view' as remediation,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'view'\n ) as metadata,\n format(\n 'security_definer_view_%s_%s',\n n.nspname,\n c.relname\n ) as cache_key\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on n.oid = c.relnamespace\nwhere\n c.relkind = 'v'\n and n.nspname = 'public'\n\tand not (\n\t\tlower(coalesce(c.reloptions::text,'{}'))::text[]\n\t\t&& array[\n\t\t\t'security_invoker=1',\n\t\t\t'security_invoker=true',\n\t\t\t'security_invoker=yes',\n\t\t\t'security_invoker=on'\n\t\t]\n\t))", "0011_function_search_path_mutable": "(\nselect\n 'function_search_path_mutable' as name,\n 'WARN' as level,\n 'EXTERNAL' as facing,\n 'Detects functions with a mutable search_path parameter which could fail to execute sucessfully for some roles.' as description,\n format(\n 'Function \\`%s.%s\\` has a role mutable search_path',\n n.nspname,\n p.proname\n ) as detail,\n 'https://supabase.github.io/splinter/0011_function_search_path_mutable' as remediation,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', p.proname,\n 'type', 'function'\n ) as metadata,\n format(\n 'function_search_path_mutable_%s_%s_%s',\n n.nspname,\n p.proname,\n md5(p.prosrc) -- required when function is polymorphic\n ) as cache_key\nfrom\n pg_catalog.pg_proc p\n join pg_catalog.pg_namespace n\n on p.pronamespace = n.oid\nwhere\n n.nspname not in (\n 'pg_catalog', 'information_schema', 'auth', 'storage', 'vault', 'pgsodium', 'graphql', 'graphql_public'\n )\n -- Search path not set to ''\n and not coalesce(p.proconfig, '{}') && array['search_path=\"\"'])", - "0012_auth_allow_anonymous_sign_ins": "(\nwith recursive role_members as (\n select\n roleid,\n member\n from pg_catalog.pg_auth_members\n where roleid = (select oid from pg_roles where rolname = 'authenticated')\n union\n select\n am.roleid,\n am.member\n from pg_catalog.pg_auth_members as am\n inner join role_members as rm on am.roleid = rm.member\n),\n\nmember_names as (\n select r.rolname from pg_roles as r\n inner join role_members as m on r.oid = m.member\n)\n\nselect\n 'auth_allow_anonymous_sign_ins' as name,\n 'INFO' as level,\n 'EXTERNAL' as facing,\n 'Detects row level security (RLS) policies that allow access to anonymous users.' as description,\n 'https://supabase.github.io/splinter/0012_auth_allow_anonymous_sign_ins' as remediation,\n format(\n 'Table \\`%s.%s\\` has policies enforced on roles that allow access to anonymous users. Policies include \\`%s\\`',\n n.nspname,\n c.relname,\n array_agg(p.policyname order by p.policyname)\n ) as detail,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as metadata,\n format(\n 'auth_allow_anonymous_sign_ins_%s_%s',\n n.nspname,\n c.relname\n ) as cache_key\nfrom pg_catalog.pg_policies as p\ninner join pg_catalog.pg_class as c on p.tablename = c.relname\ninner join pg_catalog.pg_namespace as n on c.relnamespace = n.oid\nwhere\n (\n p.roles = array['public'::name] -- public roles \n or p.roles = array['authenticated'::name] -- authenticated roles \n or exists (\n select rolname from member_names where rolname = any(roles)\n ) -- roles that are members of authenticated\n )\n and replace(p.qual, ' ', '') !~ 'auth\\.jwt\\(\\)->>''is_anonymous''::text'\ngroup by n.nspname, c.relname)", + "0012_auth_allow_anonymous_sign_ins": "(\nwith recursive role_members as (\n select\n roleid,\n member\n from pg_catalog.pg_auth_members\n where roleid = (select oid from pg_roles where rolname = 'authenticated')\n union\n select\n am.roleid,\n am.member\n from pg_catalog.pg_auth_members as am\n inner join role_members as rm on am.roleid = rm.member\n),\n\nmember_names as (\n select r.rolname from pg_roles as r\n inner join role_members as m on r.oid = m.member\n)\n\nselect\n 'auth_allow_anonymous_sign_ins' as name,\n 'INFO' as level,\n 'EXTERNAL' as facing,\n 'Detects row level security (RLS) policies that allow access to anonymous users.' as description,\n 'https://supabase.github.io/splinter/0012_auth_allow_anonymous_sign_ins' as remediation,\n format(\n 'Table \\`%s.%s\\` has policies enforced on roles that allow access to anonymous users. Policies include \\`%s\\`',\n n.nspname,\n c.relname,\n array_agg(p.policyname order by p.policyname)\n ) as detail,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as metadata,\n format(\n 'auth_allow_anonymous_sign_ins_%s_%s',\n n.nspname,\n c.relname\n ) as cache_key\nfrom pg_catalog.pg_policies as p\ninner join pg_catalog.pg_class as c on p.tablename = c.relname\ninner join pg_catalog.pg_namespace as n on c.relnamespace = n.oid\nwhere\n (\n p.roles = array['public'::name] -- public roles \n or p.roles = array['authenticated'::name] -- authenticated roles \n or exists (\n select rolname from member_names where rolname = any(roles)\n ) -- roles that are members of authenticated\n )\n and replace(p.qual, ' ', '') not like '%auth.jwt()%->>%is_anonymous%'\ngroup by n.nspname, c.relname)", "0013_rls_disabled_in_public": "(\nselect\n 'rls_disabled_in_public' as name,\n 'ERROR' as level,\n 'EXTERNAL' as facing,\n 'Detects cases where row level security (RLS) has not been enabled on a table in the `public` schema.' as description,\n format(\n 'Table \\`%s.%s\\` is in the `public` but RLS has not been enabled.',\n n.nspname,\n c.relname\n ) as detail,\n 'https://supabase.github.io/splinter/0013_rls_disabled_in_public' as remediation,\n jsonb_build_object(\n 'schema', n.nspname,\n 'name', c.relname,\n 'type', 'table'\n ) as metadata,\n format(\n 'rls_disabled_in_public_%s_%s',\n n.nspname,\n c.relname\n ) as cache_key\nfrom\n pg_catalog.pg_class c\n join pg_catalog.pg_namespace n\n on c.relnamespace = n.oid\nwhere\n c.relkind = 'r' -- regular tables\n and n.nspname = 'public'\n -- RLS is disabled\n and not c.relrowsecurity)" } \ No newline at end of file diff --git a/splinter.sql b/splinter.sql index 3dea876..0b2745d 100644 --- a/splinter.sql +++ b/splinter.sql @@ -637,7 +637,7 @@ where select rolname from member_names where rolname = any(roles) ) -- roles that are members of authenticated ) - and replace(p.qual, ' ', '') !~ 'auth\.jwt\(\)->>''is_anonymous''::text' + and replace(p.qual, ' ', '') not like '%auth.jwt()%->>%is_anonymous%' group by n.nspname, c.relname) union all ( From 42d60173878505038a3fbc2423d15a705b37eca0 Mon Sep 17 00:00:00 2001 From: Kang Ming Date: Sat, 27 Apr 2024 01:26:15 +0800 Subject: [PATCH 12/12] Update docs/0012_auth_allow_anonymous_sign_ins.md --- docs/0012_auth_allow_anonymous_sign_ins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/0012_auth_allow_anonymous_sign_ins.md b/docs/0012_auth_allow_anonymous_sign_ins.md index cc3a35c..9b24ec6 100644 --- a/docs/0012_auth_allow_anonymous_sign_ins.md +++ b/docs/0012_auth_allow_anonymous_sign_ins.md @@ -10,7 +10,7 @@ An anonymous user is a user created through Supabase Auth. It is just like a per ### How to Resolve -To mitigate the risk, determine if existing row level security (RLS) policies are meant to allow access to anonymous users. Affected policies include those that are associated to the `authenticated` or `public` roles, and members of those roles that inherit privileges. +Determine if existing row level security (RLS) policies are meant to allow access to anonymous users. Affected policies include those that are associated to the `authenticated` or `public` roles, and members of those roles that inherit privileges. For example, consider the policy: