From 0cbcbf3efd4ca16a502c6ecc217919e1d97261f0 Mon Sep 17 00:00:00 2001 From: ghost <49853598+JSONbored@users.noreply.github.com> Date: Wed, 1 Jul 2026 21:41:26 -0700 Subject: [PATCH 1/2] fix(selfhost): avoid postgres url argv leaks --- .../src/routes/docs.self-hosting-backup-scaling.tsx | 5 +++-- scripts/export-grafana-reporting-db.sh | 4 ++-- scripts/migrate-selfhost-sqlite-to-postgres.ts | 7 ++++--- test/unit/selfhost-grafana-reporting.test.ts | 10 ++++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx index a52017cdb0..09ea2b6a5f 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx @@ -96,8 +96,9 @@ QDRANT_URL=http://qdrant:6333`}

@pgbouncer:5432/gittensory +npm run selfhost:postgres:migrate -- --sqlite /data/gittensory.sqlite +npm run selfhost:postgres:migrate -- --sqlite /data/gittensory.sqlite --execute`} />

Restore checks

diff --git a/scripts/export-grafana-reporting-db.sh b/scripts/export-grafana-reporting-db.sh index 52cbd0c809..7d0ced17b6 100644 --- a/scripts/export-grafana-reporting-db.sh +++ b/scripts/export-grafana-reporting-db.sh @@ -41,7 +41,7 @@ pg_enabled() { } pg_scalar() { - psql "$PG_DB" -X -q -t -A -v ON_ERROR_STOP=1 -c "$1" + PGDATABASE="$PG_DB" psql -X -q -t -A -v ON_ERROR_STOP=1 -c "$1" } pg_table_exists() { @@ -63,7 +63,7 @@ pg_column_exists() { pg_copy_csv() { query="$1" out="$2" - psql "$PG_DB" -X -q -v ON_ERROR_STOP=1 -c "COPY ($query) TO STDOUT WITH CSV" >"$out" + PGDATABASE="$PG_DB" psql -X -q -v ON_ERROR_STOP=1 -c "COPY ($query) TO STDOUT WITH CSV" >"$out" } sqlite_import_csv() { diff --git a/scripts/migrate-selfhost-sqlite-to-postgres.ts b/scripts/migrate-selfhost-sqlite-to-postgres.ts index 13a400fc59..8e4653f8d2 100644 --- a/scripts/migrate-selfhost-sqlite-to-postgres.ts +++ b/scripts/migrate-selfhost-sqlite-to-postgres.ts @@ -36,14 +36,15 @@ const TABLES_ALLOWED_AFTER_SCHEMA_INIT = new Set(["global_agent_controls", "glob const POSTGRES_TEXT_NUL_REPLACEMENT = "\uFFFD"; function usage(): string { - return `Usage: npm run selfhost:postgres:migrate -- --sqlite --postgres-url [--execute] + return `Usage: DATABASE_URL= npm run selfhost:postgres:migrate -- --sqlite [--execute] Copies a self-host SQLite database into an empty Postgres backend. The default is a transactionally -rolled-back dry run. Pass --execute to commit the copy. +rolled-back dry run. Pass --execute to commit the copy. Prefer DATABASE_URL over --postgres-url so +the Postgres credential is not exposed through process command lines. Options: --sqlite SQLite source file. Defaults to DATABASE_PATH or /data/gittensory.sqlite. - --postgres-url Postgres target URL. Defaults to DATABASE_URL. + --postgres-url Postgres target URL. Defaults to DATABASE_URL; avoid this on shared hosts. --migrations-dir Migration directory. Defaults to migrations. --execute Commit the copy. Omit for a rollback dry run. --allow-non-empty Allow non-empty target tables only when overlapping primary keys are identical. diff --git a/test/unit/selfhost-grafana-reporting.test.ts b/test/unit/selfhost-grafana-reporting.test.ts index a0289c582b..96298d3695 100644 --- a/test/unit/selfhost-grafana-reporting.test.ts +++ b/test/unit/selfhost-grafana-reporting.test.ts @@ -50,6 +50,16 @@ function fakePsql(root: string): string { psql, `#!/bin/sh args="$*" +case " $args " in + *" postgres://"*|*" postgresql://"*) + echo 'psql command line leaked postgres URL' >&2 + exit 8 + ;; +esac +if [ "\${PGDATABASE:-}" != "postgres://gittensory:pw@postgres:5432/gittensory" ]; then + echo 'psql did not receive postgres URL through PGDATABASE' >&2 + exit 8 +fi case "$args" in *\\\\copy*) echo 'unexpected psql meta-command copy' >&2 From 6511de402211716943010d8801f43d8c0e3637cf Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:22:14 -0700 Subject: [PATCH 2/2] fix(docs): export DATABASE_URL in the postgres migrator doc snippet The postgres migrator now reads DATABASE_URL from the environment instead of taking --postgres-url on argv (avoiding a credential leak through process listings), and the docs were updated to match. But the snippet set DATABASE_URL as a plain (non-exported) shell assignment, so a user pasting the block verbatim would run the two following npm run selfhost:postgres:migrate commands without the variable in their environment, failing with "--postgres-url or DATABASE_URL must be a postgres:// URL". --- .../src/routes/docs.self-hosting-backup-scaling.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx index 09ea2b6a5f..ed012253fb 100644 --- a/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx +++ b/apps/gittensory-ui/src/routes/docs.self-hosting-backup-scaling.tsx @@ -96,7 +96,7 @@ QDRANT_URL=http://qdrant:6333`}

@pgbouncer:5432/gittensory + code={`export DATABASE_URL=postgres://gittensory:@pgbouncer:5432/gittensory npm run selfhost:postgres:migrate -- --sqlite /data/gittensory.sqlite npm run selfhost:postgres:migrate -- --sqlite /data/gittensory.sqlite --execute`} />