From 81a3dcadbfd9a8fd5a234555185ce9ada72f7cc2 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Mon, 16 Jun 2025 20:13:44 +0200 Subject: [PATCH 1/6] K8SPS-265 add special characters --- build/orc-entrypoint.sh | 19 ++++++++++++++++--- build/router-entrypoint.sh | 9 +++++++-- build/router_readiness_check.sh | 11 ++++++++--- build/router_startup_check.sh | 9 +++++++-- cmd/bootstrap/group_replication.go | 4 +++- e2e-tests/conf/secrets.yaml | 1 - e2e-tests/functions | 5 +++++ .../demand-backup/01-deploy-operator.yaml | 1 - .../tests/demand-backup/03-write-data.yaml | 6 +++--- .../tests/demand-backup/07-delete-data.yaml | 6 +++--- .../tests/demand-backup/10-read-data.yaml | 3 ++- .../tests/demand-backup/11-delete-data.yaml | 5 +++-- .../tests/demand-backup/13-read-data.yaml | 3 ++- .../tests/demand-backup/15-delete-data.yaml | 5 +++-- .../tests/demand-backup/17-read-data.yaml | 3 ++- .../tests/demand-backup/19-delete-data.yaml | 5 +++-- .../tests/demand-backup/21-read-data.yaml | 3 ++- .../tests/demand-backup/23-delete-data.yaml | 5 +++-- .../tests/demand-backup/25-read-data.yaml | 3 ++- .../gr-demand-backup/01-deploy-operator.yaml | 1 - .../tests/gr-demand-backup/03-write-data.yaml | 5 +++-- .../gr-demand-backup/05-delete-data.yaml | 5 +++-- .../tests/gr-demand-backup/07-read-data.yaml | 3 ++- .../gr-demand-backup/09-delete-data.yaml | 5 +++-- .../tests/gr-demand-backup/11-read-data.yaml | 3 ++- .../gr-demand-backup/13-delete-data.yaml | 5 +++-- .../tests/gr-demand-backup/15-read-data.yaml | 3 ++- .../gr-demand-backup/17-delete-data.yaml | 5 +++-- .../tests/gr-demand-backup/19-read-data.yaml | 3 ++- .../tests/gr-users/00-deploy-operator.yaml | 1 - e2e-tests/tests/gr-users/02-check-users.yaml | 3 ++- e2e-tests/tests/users/00-deploy-operator.yaml | 1 - e2e-tests/tests/users/02-check-users.yaml | 3 ++- pkg/secret/secret.go | 3 ++- 34 files changed, 103 insertions(+), 52 deletions(-) diff --git a/build/orc-entrypoint.sh b/build/orc-entrypoint.sh index 71891548e..a4c75a59f 100755 --- a/build/orc-entrypoint.sh +++ b/build/orc-entrypoint.sh @@ -41,11 +41,24 @@ if [ -f "$PATH_TO_SECRET/$TOPOLOGY_USER" ]; then TOPOLOGY_PASSWORD=$(<"${PATH_TO_SECRET}/${TOPOLOGY_USER}") fi +#set +o xtrace +#temp=$(mktemp) +#sed -r "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" +#sed -r "s|^[#]?password=.*$|password=${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" +#cat "${temp}" >"${ORC_CONF_PATH}/config/orc-topology.cnf" +#rm "${temp}" +#set -o xtrace +# +#exec "$@" + set +o xtrace temp=$(mktemp) -sed -r "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" -sed -r "s|^[#]?password=.*$|password=${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" -cat "${temp}" >"${ORC_CONF_PATH}/config/orc-topology.cnf" +ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&\]/\\&/g') +sed -r \ + -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ + -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ + "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" +cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" rm "${temp}" set -o xtrace diff --git a/build/router-entrypoint.sh b/build/router-entrypoint.sh index 7cc46e8f6..0e2266f40 100755 --- a/build/router-entrypoint.sh +++ b/build/router-entrypoint.sh @@ -6,18 +6,23 @@ ROUTER_DIR=${ROUTER_DIR:-/tmp/router} OPERATOR_USER=${OPERATOR_USER:-operator} NAMESPACE=$(?@[]^_{}~" ) var SecretUsers = []apiv1alpha1.SystemUser{ From c3366a90ba4b0ecfc4c9abbf383fd63efe95ece7 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Mon, 16 Jun 2025 20:14:58 +0200 Subject: [PATCH 2/6] delete unused --- build/orc-entrypoint.sh | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/orc-entrypoint.sh b/build/orc-entrypoint.sh index a4c75a59f..719b57ff1 100755 --- a/build/orc-entrypoint.sh +++ b/build/orc-entrypoint.sh @@ -41,16 +41,6 @@ if [ -f "$PATH_TO_SECRET/$TOPOLOGY_USER" ]; then TOPOLOGY_PASSWORD=$(<"${PATH_TO_SECRET}/${TOPOLOGY_USER}") fi -#set +o xtrace -#temp=$(mktemp) -#sed -r "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" -#sed -r "s|^[#]?password=.*$|password=${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}|" "${ORC_CONF_PATH}/orc-topology.cnf" >"${temp}" -#cat "${temp}" >"${ORC_CONF_PATH}/config/orc-topology.cnf" -#rm "${temp}" -#set -o xtrace -# -#exec "$@" - set +o xtrace temp=$(mktemp) ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&\]/\\&/g') From eb31c2dc56fcf6967c84ff1c0de6fcc217808c9e Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Tue, 17 Jun 2025 11:41:51 +0200 Subject: [PATCH 3/6] add # to specific symbols list --- build/orc-entrypoint.sh | 40 +++++++++++++++++++++++++++++++++++++++- pkg/secret/secret.go | 2 +- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/build/orc-entrypoint.sh b/build/orc-entrypoint.sh index 719b57ff1..afabd58b8 100755 --- a/build/orc-entrypoint.sh +++ b/build/orc-entrypoint.sh @@ -41,13 +41,51 @@ if [ -f "$PATH_TO_SECRET/$TOPOLOGY_USER" ]; then TOPOLOGY_PASSWORD=$(<"${PATH_TO_SECRET}/${TOPOLOGY_USER}") fi +#set +o xtrace +#temp=$(mktemp) +#ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&\]/\\&/g') +#sed -r \ +# -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ +# -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ +# "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" +#cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" +#rm "${temp}" +#set -o xtrace +# +#exec "$@" + +#set +o xtrace +#temp=$(mktemp) +# +## Properly escape the password and wrap in double quotes for .cnf +#ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/["\\]/\\&/g') +#ESCAPED_PASSWORD="\"${ESCAPED_PASSWORD}\"" +# +#sed -r \ +# -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ +# -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ +# "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" +# +#cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" +#rm "${temp}" +#set -o xtrace +# +#exec "$@" + set +o xtrace temp=$(mktemp) -ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&\]/\\&/g') + +# Escape &, ", and \ for sed substitution +ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&"\\]/\\&/g') +ESCAPED_PASSWORD="\"${ESCAPED_PASSWORD}\"" # Wrap in double quotes for .cnf + +# Substitute user and password lines sed -r \ -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" + +# Finalize config cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" rm "${temp}" set -o xtrace diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index da48db6e4..507f7a68e 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -45,7 +45,7 @@ const ( passSymbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + - "!$%&()*+,-.<=>?@[]^_{}~" + "!$%&()*+,-.<=>?@[]^_{}~#" ) var SecretUsers = []apiv1alpha1.SystemUser{ From 55033b8d3fe0ec5e7c6ca9c076c1fe8b38014834 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Tue, 17 Jun 2025 14:58:16 +0200 Subject: [PATCH 4/6] fix --- build/orc-entrypoint.sh | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/build/orc-entrypoint.sh b/build/orc-entrypoint.sh index afabd58b8..8959aef7c 100755 --- a/build/orc-entrypoint.sh +++ b/build/orc-entrypoint.sh @@ -41,52 +41,18 @@ if [ -f "$PATH_TO_SECRET/$TOPOLOGY_USER" ]; then TOPOLOGY_PASSWORD=$(<"${PATH_TO_SECRET}/${TOPOLOGY_USER}") fi -#set +o xtrace -#temp=$(mktemp) -#ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&\]/\\&/g') -#sed -r \ -# -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ -# -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ -# "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" -#cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" -#rm "${temp}" -#set -o xtrace -# -#exec "$@" - -#set +o xtrace -#temp=$(mktemp) -# -## Properly escape the password and wrap in double quotes for .cnf -#ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/["\\]/\\&/g') -#ESCAPED_PASSWORD="\"${ESCAPED_PASSWORD}\"" -# -#sed -r \ -# -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ -# -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ -# "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" -# -#cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" -#rm "${temp}" -#set -o xtrace -# -#exec "$@" - set +o xtrace temp=$(mktemp) -# Escape &, ", and \ for sed substitution ESCAPED_PASSWORD=$(printf '%s' "${TOPOLOGY_PASSWORD:-$ORC_TOPOLOGY_PASSWORD}" | sed -e 's/[&"\\]/\\&/g') ESCAPED_PASSWORD="\"${ESCAPED_PASSWORD}\"" # Wrap in double quotes for .cnf -# Substitute user and password lines sed -r \ -e "s|^[#]?user=.*$|user=${TOPOLOGY_USER}|" \ -e "s|^[#]?password=.*$|password=${ESCAPED_PASSWORD}|" \ "${ORC_CONF_PATH}/orc-topology.cnf" > "${temp}" -# Finalize config -cat "${temp}" > "${ORC_CONF_PATH}/config/orc-topology.cnf" +cat "${temp}" >"${ORC_CONF_PATH}/config/orc-topology.cnf" rm "${temp}" set -o xtrace From 242a4f7b291542db7c28e27db5efac8cb4bd7465 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Tue, 17 Jun 2025 15:53:20 +0200 Subject: [PATCH 5/6] fix PR suggestions and conflict --- build/router-entrypoint.sh | 2 +- e2e-tests/functions | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/router-entrypoint.sh b/build/router-entrypoint.sh index 0e2266f40..061b60536 100755 --- a/build/router-entrypoint.sh +++ b/build/router-entrypoint.sh @@ -22,7 +22,7 @@ mysqlrouter --force \ --conf-set-option http_auth_backend:default_auth_backend.filename="${ROUTER_DIR}/realm.txt" \ --directory "${ROUTER_DIR}" -echo ${OPERATOR_PASS_ESCAPED} | mysqlrouter_passwd set "${ROUTER_DIR}/realm.txt" ${OPERATOR_USER} +echo "${OPERATOR_PASS_ESCAPED}" | mysqlrouter_passwd set "${ROUTER_DIR}/realm.txt" "${OPERATOR_USER}" sed -i 's/logging_folder=.*/logging_folder=/g' "${ROUTER_DIR}/mysqlrouter.conf" sed -i "/\[logger\]/a destination=/dev/stdout" "${ROUTER_DIR}/mysqlrouter.conf" diff --git a/e2e-tests/functions b/e2e-tests/functions index a1e6840a1..acbf0c5de 100755 --- a/e2e-tests/functions +++ b/e2e-tests/functions @@ -1042,6 +1042,7 @@ deploy_cmctl() { get_user_pass() { local user="${1:-root}" kubectl -n "${NAMESPACE}" get secret test-secrets -o jsonpath="{.data.${user}}" | base64 --decode +} get_operator_version() { kubectl get crd -n "$NAMESPACE" perconaservermysqls.ps.percona.com -o jsonpath='{.metadata.labels.app\.kubernetes\.io/version}' From 714180ef3fc7b6c1667406cd164b81ce07640657 Mon Sep 17 00:00:00 2001 From: Natalia Marukovich Date: Wed, 2 Jul 2025 15:07:19 +0200 Subject: [PATCH 6/6] add escape characters --- build/ps-entrypoint.sh | 24 ++++++++++++++++-------- pkg/secret/secret.go | 12 ++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/build/ps-entrypoint.sh b/build/ps-entrypoint.sh index c609d8135..e12bed1bb 100755 --- a/build/ps-entrypoint.sh +++ b/build/ps-entrypoint.sh @@ -190,6 +190,14 @@ ensure_read_only() { sed -i "/\[mysqld\]/a super_read_only=ON" $CFG } +escape_special() { + { set +x; } 2>/dev/null + echo "$1" \ + | sed 's/\\/\\\\/g' \ + | sed 's/'\''/'\\\\\''/g' \ + | sed 's/"/\\\"/g' +} + MYSQL_VERSION=$(mysqld -V | awk '{print $3}' | awk -F'.' '{print $1"."$2}') if [[ "$MYSQL_VERSION" != '8.0' ]] && [[ "${MYSQL_VERSION}" != '8.4' ]]; then @@ -275,7 +283,7 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then # no, we don't care if read finds a terminating character in this heredoc # https://unix.stackexchange.com/questions/265149/why-is-set-o-errexit-breaking-this-read-heredoc-expression/265151#265151 read -r -d '' rootCreate <<-EOSQL || true - CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'root'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '$(escape_special "${MYSQL_ROOT_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT ALL ON *.* TO 'root'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; EOSQL fi @@ -299,38 +307,38 @@ if [ "$1" = 'mysqld' -a -z "$wantHelp" ]; then SET @@SESSION.SQL_LOG_BIN=0; DELETE FROM mysql.user WHERE user NOT IN ('mysql.sys', 'mysqlxsys', 'root', 'mysql.infoschema', 'mysql.session') OR host NOT IN ('localhost') ; - ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' ; + ALTER USER 'root'@'localhost' IDENTIFIED BY '$(escape_special "${MYSQL_ROOT_PASSWORD}")' ; GRANT ALL ON *.* TO 'root'@'localhost' WITH GRANT OPTION ; ${rootCreate} /*!80016 REVOKE SYSTEM_USER ON *.* FROM root */; - CREATE USER 'operator'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '${OPERATOR_ADMIN_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'operator'@'${MYSQL_ROOT_HOST}' IDENTIFIED BY '$(escape_special "${OPERATOR_ADMIN_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT ALL ON *.* TO 'operator'@'${MYSQL_ROOT_HOST}' WITH GRANT OPTION ; - CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY '${XTRABACKUP_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'xtrabackup'@'localhost' IDENTIFIED BY '$(escape_special "${XTRABACKUP_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT SYSTEM_USER, BACKUP_ADMIN, PROCESS, RELOAD, GROUP_REPLICATION_ADMIN, REPLICATION_SLAVE_ADMIN, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'xtrabackup'@'localhost'; GRANT SELECT ON performance_schema.replication_group_members TO 'xtrabackup'@'localhost'; GRANT SELECT ON performance_schema.log_status TO 'xtrabackup'@'localhost'; GRANT SELECT ON performance_schema.keyring_component_status TO 'xtrabackup'@'localhost'; - CREATE USER 'monitor'@'${MONITOR_HOST}' IDENTIFIED BY '${MONITOR_PASSWORD}' WITH MAX_USER_CONNECTIONS 100 PASSWORD EXPIRE NEVER; + CREATE USER 'monitor'@'${MONITOR_HOST}' IDENTIFIED BY '$(escape_special "${MONITOR_PASSWORD}")' WITH MAX_USER_CONNECTIONS 100 PASSWORD EXPIRE NEVER; GRANT SYSTEM_USER, SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD, BACKUP_ADMIN ON *.* TO 'monitor'@'${MONITOR_HOST}'; GRANT SELECT ON performance_schema.* TO 'monitor'@'${MONITOR_HOST}'; ${monitorConnectGrant} - CREATE USER 'replication'@'%' IDENTIFIED BY '${REPLICATION_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'replication'@'%' IDENTIFIED BY '$(escape_special "${REPLICATION_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT DELETE, INSERT, UPDATE ON mysql.* TO 'replication'@'%' WITH GRANT OPTION; GRANT SELECT ON performance_schema.threads to 'replication'@'%'; GRANT SYSTEM_USER, REPLICATION SLAVE, BACKUP_ADMIN, GROUP_REPLICATION_STREAM, CLONE_ADMIN, CONNECTION_ADMIN, CREATE USER, EXECUTE, FILE, GROUP_REPLICATION_ADMIN, PERSIST_RO_VARIABLES_ADMIN, PROCESS, RELOAD, REPLICATION CLIENT, REPLICATION_APPLIER, REPLICATION_SLAVE_ADMIN, ROLE_ADMIN, SELECT, SHUTDOWN, SYSTEM_VARIABLES_ADMIN ON *.* TO 'replication'@'%' WITH GRANT OPTION; - CREATE USER 'orchestrator'@'%' IDENTIFIED BY '${ORC_TOPOLOGY_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'orchestrator'@'%' IDENTIFIED BY '$(escape_special "${ORC_TOPOLOGY_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT SYSTEM_USER, SUPER, PROCESS, REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO 'orchestrator'@'%'; GRANT SELECT ON performance_schema.replication_group_members TO 'orchestrator'@'%'; GRANT SELECT ON mysql.slave_master_info TO 'orchestrator'@'%'; GRANT SELECT ON sys_operator.* TO 'orchestrator'@'%'; CREATE DATABASE IF NOT EXISTS sys_operator; - CREATE USER 'heartbeat'@'localhost' IDENTIFIED BY '${HEARTBEAT_PASSWORD}' PASSWORD EXPIRE NEVER; + CREATE USER 'heartbeat'@'localhost' IDENTIFIED BY '$(escape_special "${HEARTBEAT_PASSWORD}")' PASSWORD EXPIRE NEVER; GRANT SYSTEM_USER, REPLICATION CLIENT ON *.* TO 'heartbeat'@'localhost'; GRANT SELECT, CREATE, DELETE, UPDATE, INSERT ON sys_operator.heartbeat TO 'heartbeat'@'localhost'; diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index 93e03687e..6efc4889c 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -40,6 +40,18 @@ func GenerateCertsSecret(ctx context.Context, cr *apiv1alpha1.PerconaServerMySQL return secret, nil } +// Password generation constants. +// +// passSymbols defines the allowed character set for generated passwords. +// It includes uppercase letters, lowercase letters, digits, and selected special characters. +// +// Note: We intentionally exclude some characters that could break SQL, shell, YAML, +// or connection string contexts — such as single quotes ('), double quotes ("), backslashes (\), +// forward slashes (/), colons (:), pipes (|), semicolons (;), and backticks (`). +// +// These omissions reduce the risk of injection vulnerabilities or misinterpretation in tooling. +// +// The password length is constrained between passwordMinLen and passwordMaxLen for security and usability. const ( passwordMaxLen = 20 passwordMinLen = 16