From a4f6e247c993aa3ca5c5e33b566cc181254e17e8 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Thu, 12 Nov 2026 17:12:36 +0100 Subject: [PATCH 1/3] Generate database passwords with random_password setup_postgresql_db, setup_mariadb_db, setup_mysql_db and prompt_password built passwords with openssl rand | tr | head, the pattern AGENTS.md forbids. They use random_password now. The three database helpers also read APPLICATION under set -u, which only an install script has. An addon (pgAdmin) calling setup_postgresql_db died with "APPLICATION: unbound variable"; the credentials file name now falls back to APP. --- .github/workflows/verify-split-files.yml | 4 ++ core/core.func | 3 +- lib/db.func | 15 +++--- tests/db-helper-context.sh | 60 ++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 tests/db-helper-context.sh diff --git a/.github/workflows/verify-split-files.yml b/.github/workflows/verify-split-files.yml index 85a3153..c91df2a 100644 --- a/.github/workflows/verify-split-files.yml +++ b/.github/workflows/verify-split-files.yml @@ -63,6 +63,7 @@ on: - "tests/github-release-noapi.sh" - "tests/vm-dialog-esc.sh" - "tests/forge-clean-install.sh" + - "tests/db-helper-context.sh" - "tests/cs-clear.sh" - "tests/forge-appversion-scope.sh" - "tests/motd-os-version.sh" @@ -124,6 +125,7 @@ on: - "tests/github-release-noapi.sh" - "tests/vm-dialog-esc.sh" - "tests/forge-clean-install.sh" + - "tests/db-helper-context.sh" - "tests/cs-clear.sh" - "tests/forge-appversion-scope.sh" - "tests/motd-os-version.sh" @@ -262,6 +264,8 @@ jobs: run: bash tests/github-release-noapi.sh - name: Clean install keeps the target until extraction succeeds run: bash tests/forge-clean-install.sh + - name: Database helpers outside an install tests + run: bash tests/db-helper-context.sh - name: Screen clear without a terminal tests run: bash tests/cs-clear.sh - name: App version pin and .deb stamp tests diff --git a/core/core.func b/core/core.func index 05312ce..0bffd26 100644 --- a/core/core.func +++ b/core/core.func @@ -2335,8 +2335,7 @@ prompt_password() { # Generate random password if requested local generated="" if [[ "$default" == "generate" ]]; then - generated=$(openssl rand -base64 16 2>/dev/null | tr -dc 'a-zA-Z0-9' | head -c 16) - [[ -z "$generated" ]] && generated=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16) + generated=$(random_password 16) default="$generated" fi diff --git a/lib/db.func b/lib/db.func index 8168547..9700a45 100644 --- a/lib/db.func +++ b/lib/db.func @@ -421,7 +421,7 @@ setup_mariadb_db() { fi if [[ -z "${MARIADB_DB_PASS:-}" ]]; then - MARIADB_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + MARIADB_DB_PASS=$(random_password) fi msg_info "Setting up MariaDB Database" @@ -448,7 +448,8 @@ setup_mariadb_db() { $STD mariadb -u root -e "FLUSH PRIVILEGES;" - local app_name="${APPLICATION,,}" + local app_name="${APPLICATION:-${APP:-database}}" + app_name="${app_name,,}" app_name="${app_name// /}" local CREDS_FILE="${MARIADB_DB_CREDS_FILE:-${HOME}/${app_name}.creds}" { @@ -493,7 +494,7 @@ setup_mysql_db() { fi if [[ -z "${MYSQL_DB_PASS:-}" ]]; then - MYSQL_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + MYSQL_DB_PASS=$(random_password) fi msg_info "Setting up MySQL Database" @@ -503,7 +504,8 @@ setup_mysql_db() { $STD mysql -u root -e "GRANT ALL ON \`${MYSQL_DB_NAME//\`/\`\`}\`.* TO '${MYSQL_DB_USER//\'/\'\'}'@'%';" $STD mysql -u root -e "FLUSH PRIVILEGES;" - local app_name="${APPLICATION,,}" + local app_name="${APPLICATION:-${APP:-database}}" + app_name="${app_name,,}" app_name="${app_name// /}" local CREDS_FILE="${MYSQL_DB_CREDS_FILE:-${HOME}/${app_name}.creds}" { @@ -1510,7 +1512,7 @@ setup_postgresql_db() { # Generate password if not provided if [[ -z "${PG_DB_PASS:-}" ]]; then - PG_DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + PG_DB_PASS=$(random_password) fi msg_info "Setting up PostgreSQL Database" @@ -1570,7 +1572,8 @@ setup_postgresql_db() { fi # Save credentials - local app_name="${APPLICATION,,}" + local app_name="${APPLICATION:-${APP:-database}}" + app_name="${app_name,,}" app_name="${app_name// /}" local CREDS_FILE="${PG_DB_CREDS_FILE:-${HOME}/${app_name}.creds}" { diff --git a/tests/db-helper-context.sh b/tests/db-helper-context.sh new file mode 100644 index 0000000..b2a4bcd --- /dev/null +++ b/tests/db-helper-context.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -uo pipefail + +CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +eval "$(awk '/^random_password\(\) \{/,/^}/' "$CORE_DIR/core/core.func")" +eval "$(awk '/^setup_(postgresql|mariadb|mysql)_db\(\) \{/,/^}/' "$CORE_DIR/lib/db.func")" + +TEST_DIR="$(mktemp -d)" +trap 'rm -rf -- "$TEST_DIR"' EXIT +passed=0 failed=0 + +check() { + if [[ "$3" == "$2" ]]; then + echo "PASS $1" + passed=$((passed + 1)) + else + echo "FAIL $1: expected <$2>, got <$3>" >&2 + failed=$((failed + 1)) + fi +} + +# shellcheck disable=SC2034 # read by the eval'd helpers +STD="" +msg_info() { :; } +msg_ok() { :; } +msg_error() { echo "error: $*" >&2; } +sudo() { :; } +mariadb() { :; } +mysql() { :; } +systemctl() { :; } + +# An addon calls these without the install context's APPLICATION; set -u then +# ended the run with "APPLICATION: unbound variable". +run() { + local home="$TEST_DIR/$1" + mkdir -p "$home" + ( + set -u + HOME="$home" + unset APPLICATION + APP="pgAdmin" + PG_DB_NAME=appdb PG_DB_USER=appuser + MARIADB_DB_NAME=appdb MARIADB_DB_USER=appuser + MYSQL_DB_NAME=appdb MYSQL_DB_USER=appuser + "setup_$1_db" >/dev/null 2>&1 || exit 1 + case "$1" in + postgresql) pass="$PG_DB_PASS" ;; + mariadb) pass="$MARIADB_DB_PASS" ;; + mysql) pass="$MYSQL_DB_PASS" ;; + esac + printf '%s %s' "$([[ "$pass" =~ ^[A-Za-z0-9]{24}$ ]] && echo alnum24 || echo "bad:$pass")" "$(ls "$home")" + ) || echo "aborted" +} + +for db in postgresql mariadb mysql; do + check "$db: runs without APPLICATION, alphanumeric password, creds named after APP" "alnum24 pgadmin.creds" "$(run "$db")" +done + +echo "${passed} passed, ${failed} failed" +[[ "$failed" -eq 0 ]] From bb805541daa0616187a86e08b3f0e112f75d0f24 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Thu, 12 Nov 2026 18:29:02 +0100 Subject: [PATCH 2/3] Keep the database helpers' credentials files private setup_postgresql_db, setup_mariadb_db and setup_mysql_db wrote the database password to ~/.creds with the default umask, so the file was world-readable (644). It is chmod 600 now. --- lib/db.func | 3 +++ tests/db-helper-context.sh | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/db.func b/lib/db.func index 9700a45..f6d3a71 100644 --- a/lib/db.func +++ b/lib/db.func @@ -458,6 +458,7 @@ setup_mariadb_db() { echo "User: $MARIADB_DB_USER" echo "Password: $MARIADB_DB_PASS" } >>"$CREDS_FILE" + chmod 600 "$CREDS_FILE" msg_ok "Set up MariaDB Database" @@ -514,6 +515,7 @@ setup_mysql_db() { echo "User: $MYSQL_DB_USER" echo "Password: $MYSQL_DB_PASS" } >>"$CREDS_FILE" + chmod 600 "$CREDS_FILE" msg_ok "Set up MySQL Database" @@ -1582,6 +1584,7 @@ setup_postgresql_db() { echo "User: $PG_DB_USER" echo "Password: $PG_DB_PASS" } >>"$CREDS_FILE" + chmod 600 "$CREDS_FILE" msg_ok "Set up PostgreSQL Database" diff --git a/tests/db-helper-context.sh b/tests/db-helper-context.sh index b2a4bcd..47daba8 100644 --- a/tests/db-helper-context.sh +++ b/tests/db-helper-context.sh @@ -48,12 +48,13 @@ run() { mariadb) pass="$MARIADB_DB_PASS" ;; mysql) pass="$MYSQL_DB_PASS" ;; esac - printf '%s %s' "$([[ "$pass" =~ ^[A-Za-z0-9]{24}$ ]] && echo alnum24 || echo "bad:$pass")" "$(ls "$home")" + printf '%s %s %s' "$([[ "$pass" =~ ^[A-Za-z0-9]{24}$ ]] && echo alnum24 || echo "bad:$pass")" "$(ls "$home")" "$(stat -c %a "$home"/*.creds)" ) || echo "aborted" } +# The creds file holds the database password; it was left world-readable (644). for db in postgresql mariadb mysql; do - check "$db: runs without APPLICATION, alphanumeric password, creds named after APP" "alnum24 pgadmin.creds" "$(run "$db")" + check "$db: runs without APPLICATION, alphanumeric password, creds named after APP and private" "alnum24 pgadmin.creds 600" "$(umask 022; run "$db")" done echo "${passed} passed, ${failed} failed" From ddc029dd842da68c5bc9f9954965be216d4bfcdf Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Fri, 13 Nov 2026 10:45:30 +0100 Subject: [PATCH 3/3] Only chmod a database credentials file that is a regular file Scripts suppress the file with PG_DB_CREDS_FILE=/dev/null (one in ProxmoxVE and Incus, Hoppscotch and Rallly in DevScripts). The chmod 600 from the previous commit would have run on /dev/null and locked every non-root process out of it. --- lib/db.func | 6 +++--- tests/db-helper-context.sh | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/db.func b/lib/db.func index f6d3a71..58e619a 100644 --- a/lib/db.func +++ b/lib/db.func @@ -458,7 +458,7 @@ setup_mariadb_db() { echo "User: $MARIADB_DB_USER" echo "Password: $MARIADB_DB_PASS" } >>"$CREDS_FILE" - chmod 600 "$CREDS_FILE" + [[ -f "$CREDS_FILE" ]] && chmod 600 "$CREDS_FILE" msg_ok "Set up MariaDB Database" @@ -515,7 +515,7 @@ setup_mysql_db() { echo "User: $MYSQL_DB_USER" echo "Password: $MYSQL_DB_PASS" } >>"$CREDS_FILE" - chmod 600 "$CREDS_FILE" + [[ -f "$CREDS_FILE" ]] && chmod 600 "$CREDS_FILE" msg_ok "Set up MySQL Database" @@ -1584,7 +1584,7 @@ setup_postgresql_db() { echo "User: $PG_DB_USER" echo "Password: $PG_DB_PASS" } >>"$CREDS_FILE" - chmod 600 "$CREDS_FILE" + [[ -f "$CREDS_FILE" ]] && chmod 600 "$CREDS_FILE" msg_ok "Set up PostgreSQL Database" diff --git a/tests/db-helper-context.sh b/tests/db-helper-context.sh index 47daba8..ae99467 100644 --- a/tests/db-helper-context.sh +++ b/tests/db-helper-context.sh @@ -57,5 +57,18 @@ for db in postgresql mariadb mysql; do check "$db: runs without APPLICATION, alphanumeric password, creds named after APP and private" "alnum24 pgadmin.creds 600" "$(umask 022; run "$db")" done +# Scripts suppress the file with PG_DB_CREDS_FILE=/dev/null; chmod 600 on it would +# lock every non-root process out of /dev/null. +devnull_chmods() { + ( + chmod() { echo "chmod $*" >>"$TEST_DIR/chmod.log"; } + HOME="$TEST_DIR" + APP="demo" + PG_DB_NAME=appdb PG_DB_USER=appuser PG_DB_CREDS_FILE=/dev/null setup_postgresql_db >/dev/null 2>&1 + ) + grep -c '/dev/null' "$TEST_DIR/chmod.log" 2>/dev/null || echo 0 +} +check "PG_DB_CREDS_FILE=/dev/null is never chmodded" "0" "$(devnull_chmods)" + echo "${passed} passed, ${failed} failed" [[ "$failed" -eq 0 ]]