Skip to content

Commit 30ed2c9

Browse files
CopilotPDowney
andauthored
Fix vhost-install.sh: remove normalization, align validation with RAND_CHAR variables, harden SQL credential handling
Agent-Logs-Url: https://github.com/EngineScript/EngineScript/sessions/1f3d1ed7-c2f9-48d6-9024-bde0000164bb Co-authored-by: PDowney <[email protected]>
1 parent 903951f commit 30ed2c9

2 files changed

Lines changed: 32 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ All notable changes to EngineScript will be documented in this file.
44

55
Changes are organized by date, with the most recent changes listed first.
66

7+
## 2026-04-12
8+
9+
### 🔒 VHOST INSTALL SECURITY & VALIDATION FIXES
10+
11+
- Removed `database_name` and `database_user` lowercase normalizations from `scripts/functions/vhost/vhost-install.sh`; the random character sources (`RAND_CHAR4` and `RAND_CHAR16`) are `a-zA-Z0-9` and must not be altered, as normalization would corrupt generated identifiers.
12+
- Updated `validate_db_identifier` regex from `^[a-z][a-z0-9_]*$` to `^[A-Za-z][A-Za-z0-9_]*$` to correctly accept mixed-case identifiers produced by `RAND_CHAR4`.
13+
- Updated the pre-write `database_user` validation regex from `^[A-Za-z0-9_]+$` (already fixed from earlier lowercase-only pattern) to correctly reflect the `RAND_CHAR16` charset (`a-zA-Z0-9`).
14+
- Updated the post-source `DB` validation regex from `^[a-z][a-z0-9_]*$` to `^[A-Za-z][A-Za-z0-9_]*$` to match the mixed-case database name.
15+
- Updated the pre-write `database_password` validation regex to `^[A-Za-z0-9_]+$`, precisely matching the `RAND_CHAR32` charset (`a-zA-Z0-9_`), replacing the prior broader pattern that excluded `_` and would have incorrectly rejected valid generated passwords.
16+
- Consolidated password validation to also reject single quotes and backslashes at the pre-write stage, eliminating a TOCTOU gap where a password could pass the first check but fail a later one.
17+
- Added `escape_sql_string_literal()` helper function to safely escape MariaDB single-quoted string literals, guarding against SQL injection if password validation is ever bypassed.
18+
- Used `printf -v` to prepare the `CREATE DATABASE` SQL statement separately before passing it to `mariadb -e`, reducing direct interpolation risk.
19+
- Used `escape_sql_string_literal` on `PSWD` before interpolating into the `CREATE USER` SQL statement.
20+
721
## 2026-04-11
822

923
### 🔧 VHOST IMPORT BUG FIXES & IMPROVEMENTS

scripts/functions/vhost/vhost-install.sh

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ source /usr/local/bin/enginescript/scripts/functions/shared/enginescript-shared-
2424
# Prompt timeout settings (seconds)
2525
WORDPRESS_PROMPT_TIMEOUT=300
2626

27+
# Escape arbitrary text for safe inclusion in MariaDB single-quoted string literals.
28+
escape_sql_string_literal() {
29+
local input="$1"
30+
input="${input//\\/\\\\}"
31+
input="${input//\'/\'\'}"
32+
printf '%s' "$input"
33+
}
34+
2735
# Shared multi-part public suffixes for domain parsing logic.
2836
# Keep this aligned with supported multi-part entries in VALID_TLDS.
2937
MULTIPART_PUBLIC_SUFFIXES=(
@@ -37,7 +45,7 @@ MULTIPART_PUBLIC_SUFFIXES=(
3745
validate_db_identifier() {
3846
local db_identifier="$1"
3947
local domain_context="$2"
40-
if [[ -z "${db_identifier}" || ! "${db_identifier}" =~ ^[a-z][a-z0-9_]*$ ]]; then
48+
if [[ -z "${db_identifier}" || ! "${db_identifier}" =~ ^[A-Za-z][A-Za-z0-9_]*$ ]]; then
4149
echo "Error: Invalid database name '${db_identifier}' for domain '${domain_context}'." >&2
4250
exit 1
4351
fi
@@ -226,15 +234,12 @@ if [[ "${INSTALL_WORDPRESS}" == "1" ]]; then
226234
domain_without_tld="${domain_without_tld:0:max_domain_without_tld_len}"
227235
fi
228236
database_name="${domain_without_tld}${db_name_suffix}"
229-
# Normalize to lowercase for MySQL/MariaDB portability across platforms
230-
database_name="${database_name,,}"
231237
# Validate DB identifier before writing credentials file or interpolating into SQL
232238
validate_db_identifier "${database_name}" "${DOMAIN}"
233239
database_user="${RAND_CHAR16}"
234240
database_password="${RAND_CHAR32}"
235241

236242
# Domain Database Credentials
237-
# No need to normalize database_user to lowercase as this is a Linux-only project that does not require windows compatibility.
238243
credentials_dir="/home/EngineScript/mysql-credentials"
239244
credentials_file="${credentials_dir}/${DOMAIN}.txt"
240245
# Ensure parent directory exists and is restricted before writing sensitive data
@@ -244,7 +249,7 @@ if [[ "${INSTALL_WORDPRESS}" == "1" ]]; then
244249
exit 1
245250
fi
246251

247-
if [[ -z "${database_password}" || ! "${database_password}" =~ ^[A-Za-z0-9@%+=:,./-]+$ ]]; then
252+
if [[ -z "${database_password}" || ! "${database_password}" =~ ^[A-Za-z0-9_]+$ || "${database_password}" == *"'"* || "${database_password}" == *"\\"* ]]; then
248253
echo "Error: Invalid generated database password for domain '${DOMAIN}'." >&2
249254
exit 1
250255
fi
@@ -261,7 +266,7 @@ if [[ "${INSTALL_WORDPRESS}" == "1" ]]; then
261266
source "${credentials_file}"
262267

263268
# Validate DB identifier before interpolating into SQL
264-
if [[ -z "${DB}" || ! "${DB}" =~ ^[a-z][a-z0-9_]*$ ]]; then
269+
if [[ -z "${DB}" || ! "${DB}" =~ ^[A-Za-z][A-Za-z0-9_]*$ ]]; then
265270
echo "Error: Invalid database name '${DB}' for domain '${DOMAIN}'." >&2
266271
exit 1
267272
fi
@@ -276,12 +281,17 @@ if [[ "${INSTALL_WORDPRESS}" == "1" ]]; then
276281

277282
echo "Randomly generated MySQL database credentials for ${DOMAIN}."
278283

279-
if ! sudo mariadb -e "CREATE DATABASE \`${DB}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;"; then
284+
local create_db_sql
285+
printf -v create_db_sql 'CREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_uca1400_ai_ci;' "${DB}"
286+
if ! sudo mariadb -e "${create_db_sql}"; then
280287
echo "Error: Failed to create database '${DB}' for domain '${DOMAIN}'." >&2
281288
exit 1
282289
fi
283290

284-
if ! sudo mariadb -e "CREATE USER '${USR}'@'localhost' IDENTIFIED BY '${PSWD}';"; then
291+
local SQL_ESCAPED_PSWD
292+
SQL_ESCAPED_PSWD="$(escape_sql_string_literal "${PSWD}")"
293+
294+
if ! sudo mariadb -e "CREATE USER '${USR}'@'localhost' IDENTIFIED BY '${SQL_ESCAPED_PSWD}';"; then
285295
echo "Error: Failed to create MariaDB user '${USR}' for domain '${DOMAIN}'." >&2
286296
exit 1
287297
fi

0 commit comments

Comments
 (0)