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
40 changes: 40 additions & 0 deletions nix/plpgsql-check.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
lib,
stdenv,
fetchFromGitHub,
postgresql,
}:

stdenv.mkDerivation rec {
pname = "plpgsql-check";
version = "2.10.15";

src = fetchFromGitHub {
owner = "okbob";
repo = "plpgsql_check";
rev = "v${version}";
hash = "sha256-war+GVHMDegfDXjxdIsUkrPPDntKeyb/bGxibHVbpDo=";
};

buildInputs = [ postgresql ];

installPhase = ''
install -D -t $out *${postgresql.dlSuffix}
install -D -t $out *.sql
install -D -t $out *.control

# Needed for postgres 18+ native implementation of extension_search_path
mkdir $out/extension
install -D -t $out/extension *.sql
install -D -t $out/extension *.control
'';

meta = with lib; {
description = "Linter tool for language PL/pgSQL";
homepage = "https://github.com/okbob/plpgsql_check";
changelog = "https://github.com/okbob/plpgsql_check/releases/tag/v${version}";
platforms = postgresql.meta.platforms;
license = licenses.mit;
maintainers = [ maintainers.marsam ];
};
}
25 changes: 25 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ in
cassert ? true,
}:
let
plpgsqlcheck14 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_14;
};
plpgsqlcheck15 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_15;
};
plpgsqlcheck16 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_16;
};
plpgsqlcheck17 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_17;
};
plpgsqlcheck18 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_18;
};
plpgsqlcheck19 = pkgs.callPackage ./nix/plpgsql-check.nix {
postgresql = xpgPkgs.postgresql_19;
};

style = pkgs.writeShellScriptBin "vault-style" ''
${pkgs.clang-tools}/bin/clang-format -i src/*
${pkgs.findutils}/bin/find . -name '*.nix' -exec ${pkgs.nixfmt}/bin/nixfmt {} \+
Expand All @@ -34,16 +53,22 @@ pkgs.mkShell {
(xpgPkgs.xpg.withExtensions {
extensions = {
"14" = [
plpgsqlcheck14
];
"15" = [
plpgsqlcheck15
];
"16" = [
plpgsqlcheck16
];
"17" = [
plpgsqlcheck17
];
"18" = [
plpgsqlcheck18
];
"19" = [
plpgsqlcheck19
];
};
})
Expand Down
32 changes: 31 additions & 1 deletion sql/supabase_vault--0.3.1--0.3.2.sql
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
-- Search pg_temp last so callers can't shadow types (e.g. text, bytea) with temp objects that run as the function owner
ALTER FUNCTION vault.create_secret(text, text, text, uuid) SET search_path = pg_catalog, pg_temp;
ALTER FUNCTION vault.update_secret(uuid, text, text, text, uuid) SET search_path = pg_catalog, pg_temp;

CREATE OR REPLACE FUNCTION vault.update_secret(
secret_id uuid,
new_secret text = NULL,
new_name text = NULL,
new_description text = NULL,
-- unused
new_key_id uuid = NULL
)
RETURNS void
SECURITY DEFINER
LANGUAGE plpgsql
SET search_path = pg_catalog, pg_temp
AS $$
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
$$;
2 changes: 0 additions & 2 deletions supabase_vault.sql
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ CREATE FUNCTION vault.update_secret(secret_id 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
Expand Down
31 changes: 31 additions & 0 deletions test/expected/test.out
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,34 @@ WITH security_definer_fn_settings AS (
-------------+---------------+-----------------
(0 rows)

-- below query is sourced from from pgsql_check's git repo, link below
-- https://github.com/okbob/plpgsql_check/blob/0ac96adc13b34d071c4be8ba0ec4f73e6e7520f5/README.md?plain=1#L310
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0),
oldtable=>pg_trigger.tgoldtable,
newtable=>pg_trigger.tgnewtable) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
functionid | lineno | statement | sqlstate | message | detail | hint | level | position | query | context
-----------------------------------------------+--------+-----------+----------+-------------------------------+--------+------+---------------+----------+-------+---------
vault.create_secret(text,text,text,uuid) | | | 00000 | unused parameter "new_key_id" | | | warning extra | | |
vault.update_secret(uuid,text,text,text,uuid) | | | 00000 | unused parameter "new_key_id" | | | warning extra | | |
(2 rows)

1 change: 1 addition & 0 deletions test/init.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
CREATE ROLE bob login password 'bob';

CREATE EXTENSION plpgsql_check;
CREATE EXTENSION supabase_vault CASCADE;

GRANT USAGE ON SCHEMA vault TO bob WITH GRANT OPTION;
Expand Down
26 changes: 26 additions & 0 deletions test/sql/test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,29 @@ WITH security_definer_fn_settings AS (
WHERE array_position(s.search_path_ary, 'pg_temp') < array_position(s.search_path_ary, 'pg_catalog')
AND array_position(s.search_path_ary, 'pg_temp') IS NOT NULL
AND array_position(s.search_path_ary, 'pg_catalog') IS NOT NULL;

-- below query is sourced from from pgsql_check's git repo, link below
-- https://github.com/okbob/plpgsql_check/blob/0ac96adc13b34d071c4be8ba0ec4f73e6e7520f5/README.md?plain=1#L310
-- check all plpgsql functions (functions or trigger functions with defined triggers)
SELECT
(pcf).functionid::regprocedure, (pcf).lineno, (pcf).statement,
(pcf).sqlstate, (pcf).message, (pcf).detail, (pcf).hint, (pcf).level,
(pcf)."position", (pcf).query, (pcf).context
FROM
(
SELECT
plpgsql_check_function_tb(pg_proc.oid, COALESCE(pg_trigger.tgrelid, 0),
oldtable=>pg_trigger.tgoldtable,
newtable=>pg_trigger.tgnewtable) AS pcf
FROM pg_proc
LEFT JOIN pg_trigger
ON (pg_trigger.tgfoid = pg_proc.oid)
WHERE
prolang = (SELECT lang.oid FROM pg_language lang WHERE lang.lanname = 'plpgsql') AND
pronamespace <> (SELECT nsp.oid FROM pg_namespace nsp WHERE nsp.nspname = 'pg_catalog') AND
-- ignore unused triggers
(pg_proc.prorettype <> (SELECT typ.oid FROM pg_type typ WHERE typ.typname = 'trigger') OR
pg_trigger.tgfoid IS NOT NULL)
OFFSET 0
) ss
ORDER BY (pcf).functionid::regprocedure::text, (pcf).lineno;
Loading