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
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,24 @@ jobs:
- if: ${{ failure() }}
run: cat regression.diffs

validate-sql-snapshot:
runs-on: macos-15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Install Nix
uses: cachix/install-nix-action@c134e4c9e34bac6cab09cf239815f9339aaaf84e # v31.5.1
with:
nix_path: nixpkgs=channel:nixos-unstable

- name: Use Cachix Cache
uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
with:
name: nxpg
authToken: ${{ secrets.CACHIX_AUTH_TOKEN }}

- name: Run snapshot
run: nix develop --command "./scripts/dump_extension.sh"

- name: Check for diffs in supabase_vault.sql snapshot
run: nix-shell --run "git diff --exit-code ./supabase_vault.sql"
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
35 changes: 35 additions & 0 deletions scripts/dump_extension.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#! /usr/bin/env bash
xpg psql \
--file=<(cat << 'EOF'
DO $$
DECLARE
extension_objects text[];
extension_object text;
BEGIN

SELECT array_agg(pg_catalog.pg_describe_object(classid, objid, 0)) into extension_objects
FROM pg_catalog.pg_depend
INNER JOIN (
SELECT e.oid
FROM pg_catalog.pg_extension e
WHERE e.extname OPERATOR(pg_catalog.=) 'supabase_vault'
COLLATE pg_catalog.default
ORDER BY 1
) AS extension_oid
ON extension_oid.oid = refobjid
WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass
AND deptype = 'e'
ORDER BY 1;

FOR extension_object IN select unnest(extension_objects) LOOP
BEGIN
EXECUTE 'alter extension supabase_vault drop ' || extension_object;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'failed on %: %', extension_object, sqlerrm;
END;
END LOOP;

END;
$$;
EOF) \
--command="\\! pg_dump --restrict-key=0 --schema=vault --user=postgres --schema-only | grep -v '^\\\\restrict 0$' | grep -v '^\\\\unrestrict 0$' | grep -v '^SET .*;' | grep -v 'SELECT pg_catalog.set_config' | pgpp > supabase_vault.sql"
1 change: 1 addition & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pkgs.mkShell {
};
})
pkgs.libsodium
pkgs.python313Packages.pglast
style
styleCheck
];
Expand Down
134 changes: 134 additions & 0 deletions supabase_vault.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
CREATE SCHEMA vault;

ALTER SCHEMA vault OWNER TO postgres;

CREATE FUNCTION vault._crypto_aead_det_decrypt(message bytea
, additional bytea
, key_id bigint
, context bytea = CAST('\x7067736f6469756d' AS bytea)
, nonce bytea = CAST(NULL AS bytea))
RETURNS bytea
LANGUAGE c IMMUTABLE AS '$libdir/supabase_vault'
, 'pgsodium_crypto_aead_det_decrypt_by_id';

ALTER FUNCTION vault._crypto_aead_det_decrypt (message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) OWNER TO postgres;

CREATE FUNCTION vault._crypto_aead_det_encrypt(message bytea
, additional bytea
, key_id bigint
, context bytea = CAST('\x7067736f6469756d' AS bytea)
, nonce bytea = CAST(NULL AS bytea))
RETURNS bytea
LANGUAGE c IMMUTABLE AS '$libdir/supabase_vault'
, 'pgsodium_crypto_aead_det_encrypt_by_id';

ALTER FUNCTION vault._crypto_aead_det_encrypt (message bytea, additional bytea, key_id bigint, context bytea, nonce bytea) OWNER TO postgres;

CREATE FUNCTION vault._crypto_aead_det_noncegen()
RETURNS bytea
LANGUAGE c IMMUTABLE AS '$libdir/supabase_vault'
, 'pgsodium_crypto_aead_det_noncegen';

ALTER FUNCTION vault._crypto_aead_det_noncegen () OWNER TO postgres;

CREATE FUNCTION vault.create_secret(new_secret text
, new_name text = CAST(NULL AS text)
, new_description text = CAST('' AS text)
, new_key_id uuid = CAST(NULL AS uuid))
RETURNS uuid
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'pg_catalog', 'pg_temp'
AS $$
DECLARE
rec record;
BEGIN
INSERT INTO vault.secrets (secret, name, description)
VALUES (
new_secret,
new_name,
new_description
)
RETURNING * INTO rec;
UPDATE vault.secrets s
SET secret = encode(vault._crypto_aead_det_encrypt(
message := convert_to(rec.secret, 'utf8'),
additional := convert_to(s.id::text, 'utf8'),
key_id := 0,
context := 'pgsodium'::bytea,
nonce := rec.nonce
), 'base64')
WHERE id = rec.id;
RETURN rec.id;
END
$$;

ALTER FUNCTION vault.create_secret (new_secret text, new_name text, new_description text, new_key_id uuid) OWNER TO postgres;

CREATE FUNCTION vault.update_secret(secret_id uuid
, new_secret text = CAST(NULL AS text)
, new_name text = CAST(NULL AS text)
, new_description text = CAST(NULL AS text)
, new_key_id uuid = CAST(NULL AS uuid))
RETURNS void
LANGUAGE plpgsql SECURITY DEFINER SET search_path TO 'pg_catalog', 'pg_temp'
AS $$
DECLARE
decrypted_secret text := (SELECT decrypted_secret FROM vault.decrypted_secrets WHERE id = secret_id);
BEGIN
UPDATE vault.secrets s
SET
secret = CASE WHEN new_secret IS NULL THEN s.secret
ELSE encode(vault._crypto_aead_det_encrypt(
message := convert_to(new_secret, 'utf8'),
additional := convert_to(s.id::text, 'utf8'),
key_id := 0,
context := 'pgsodium'::bytea,
nonce := s.nonce
), 'base64') END,
name = coalesce(new_name, s.name),
description = coalesce(new_description, s.description),
updated_at = now()
WHERE s.id = secret_id;
END
$$;

ALTER FUNCTION vault.update_secret (secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid) OWNER TO postgres;

GRANT USAGE
ON SCHEMA vault
TO bob
WITH GRANT OPTION;

REVOKE ALL PRIVILEGES
ON FUNCTION vault._crypto_aead_det_decrypt (message bytea, additional bytea, key_id bigint, context bytea, nonce bytea)
FROM PUBLIC;

GRANT ALL PRIVILEGES
ON FUNCTION vault._crypto_aead_det_decrypt (message bytea, additional bytea, key_id bigint, context bytea, nonce bytea)
TO bob
WITH GRANT OPTION;

REVOKE ALL PRIVILEGES
ON FUNCTION vault._crypto_aead_det_encrypt (message bytea, additional bytea, key_id bigint, context bytea, nonce bytea)
FROM PUBLIC;

REVOKE ALL PRIVILEGES
ON FUNCTION vault._crypto_aead_det_noncegen ()
FROM PUBLIC;

REVOKE ALL PRIVILEGES
ON FUNCTION vault.create_secret (new_secret text, new_name text, new_description text, new_key_id uuid)
FROM PUBLIC;

GRANT ALL PRIVILEGES
ON FUNCTION vault.create_secret (new_secret text, new_name text, new_description text, new_key_id uuid)
TO bob
WITH GRANT OPTION;

REVOKE ALL PRIVILEGES
ON FUNCTION vault.update_secret (secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid)
FROM PUBLIC;

GRANT ALL PRIVILEGES
ON FUNCTION vault.update_secret (secret_id uuid, new_secret text, new_name text, new_description text, new_key_id uuid)
TO bob
WITH GRANT OPTION
Loading