url check

Check URL function refactored and improved.
This commit is contained in:
Cristhian Martínez Ochoa 2022-12-30 16:03:50 -07:00
parent 10bb7b7977
commit 2c4cda5392
5 changed files with 111 additions and 70 deletions

View file

@ -698,6 +698,7 @@ is_domain() {
is_url_path() {
# Should start with / and after that all should be valid characters.
# https://stackoverflow.com/questions/4669692/valid-characters-for-directory-part-of-a-url-for-short-links
if [[ -n $1 && $1 =~ ^\/([\]A-Za-z0-9_\/\.:\!\*\'\[\(\)\;@\&\=\+\$\,\?#\~\%\-]+)?$ ]]; then
echo "true"
else
@ -707,70 +708,114 @@ is_url_path() {
is_url() {
# Output: http,https,ip,unix,true,false Example: $(is_url $domain)
# Global variables when -split is set: url_scheme, url_host, url_path, url_port Example: is_url $domain -split
# Output: http,https,true,http+ip,https+ip,ip,http+unix,https+unix,unix,false - Example: $(is_url $domain)
# Global variables when -split is set: url_type, url_scheme, url_host, url_path, url_port - Example: is_url $domain -split
# Examples:
# example.com -> true
# http://example.com -> http
# https://example.com -> https
# 1.1.1.1 -> ip
# http://1.1.1.1 -> http+ip
# https://1.1.1.1 -> https+ip
# unix:/tmp/backend.socket:/uri/ -> unix
# http://unix:/tmp/backend.socket:/uri/ -> http+unix
# https://unix:/tmp/backend.socket:/uri/ -> https+unix
# Unix sockets are mainly used in upstream and proxy_pass
# http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
# http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
# Important, because they can have a previous value when runs multiple times.
unset url_scheme
unset url_host
unset url_path
unset url_port
unset url_type
# Here we are assuming URL with scheme
local scheme=$(echo "${1,,}" | cut -d':' -f 1 -s)
local host=$(echo "${1,,}" | cut -d':' -f 2 -s | cut -d'/' -f 3 -s)
local path=$(echo "${1,,}" | cut -d':' -f 2- -s | cut -d'/' -f 4- -s)
local port=$(echo "${1,,}" | cut -d':' -f 3 -s | cut -d'/' -f 1 )
local host=$(echo "${1,,}" | cut -d'/' -f 3 -s)
[[ $(echo $host | cut -d':' -f 2 -s) =~ ^[0-9]+$ ]] && local host=$(echo $host | cut -d':' -f 1) # We need this 2dn step to prevent http://example.com:/tmp (empty port)
local path=$(echo "${1,,}" | cut -d'/' -f 4- -s)
local port=$(echo "${1,,}" | cut -d'/' -f 3 -s | cut -d':' -f 2 -s)
local out="false"
# In case of no-scheme format
# In case of URL with no-scheme
local hosted=$(echo "${1,,}" | cut -d'/' -f 1)
# In case of no-scheme have port included
[[ $(echo $hosted | cut -d':' -f 2 -s) =~ ^[0-9]+$ ]] && hosted=$(echo $hosted | cut -d':' -f 1)
if [[ $1 =~ ^((http|https)+\:\/\/)?unix:(.+):(\/.*)?$ ]]; then
out="unix"
if [[ $1 =~ ^((http|https)+\:\/\/)?unix:\/[^\:]+(:\/.*)?$ ]]; then
unset port
if [[ $scheme == "unix" ]]; then
local host="$(echo "${1,,}" | cut -d':' -f 1-2 -s):"
local host="$(echo "${1,,}" | cut -d':' -f 1-2 -s)"
local path=$(echo "${1,,}" | cut -d':' -f 3- -s)
local out="unix"
unset scheme
else
local host="$(echo "${1,,}" | cut -d':' -f 2-3 -s):"
local host="$(echo "${1,,}" | cut -d':' -f 2-3 -s)"
local host=${host:2}
local path=$(echo "${1,,}" | cut -d':' -f 4- -s)
local out="${scheme}+unix"
fi
local path=$(echo "${1,,}" | rev | cut -d':' -f 1 -s | rev | cut -d '/' -f 2- -s)
local port=""
elif [[ $scheme =~ ^(http|https)$ ]]; then
if [[ $(is_domain $host) != "true" && $(is_ip $host) != "true" ]]; then
out="false"
local out="false"
elif [[ -n $path && $(is_url_path /$path) != "true" ]]; then
out="false"
local out="false"
elif [[ -n $port ]] && ! [[ $port =~ ^[0-9]+$ && $port -ge 0 && $port -le 65535 ]]; then
out="false"
local out="false"
elif [[ $(is_ip $host) == "true" ]]; then
local out="${scheme}+ip"
else
out=$scheme
local out="${scheme}"
fi
elif [[ $(is_domain $hosted) == "true" || $(is_ip $hosted) == "true" ]]; then
scheme=""
host=$hosted
path=$(echo "${1,,}" | cut -d'/' -f 2- -s)
port=$(echo "${1,,}" | cut -d'/' -f 1 | cut -d':' -f 2 -s)
local scheme=""
local host=$hosted
local path=$(echo "${1,,}" | cut -d'/' -f 2- -s)
local port=$(echo "${1,,}" | cut -d'/' -f 1 | cut -d':' -f 2 -s)
if [[ -n $path && $(is_url_path /$path) != "true" ]]; then
out="false"
local out="false"
elif [[ -n $port ]] && ! [[ $port =~ ^[0-9]+$ && $port -ge 0 && $port -le 65535 ]]; then
out="false"
local out="false"
elif [[ $(is_ip $hosted) == "true" ]]; then
out="ip"
local out="ip"
else
out="true"
local out="true"
fi
else
out="false"
local out="false"
fi
# If path doesn't begin with /, then add it!
# If there is no path, but the url ends with /, then / is a valid path, so show it as path!
[[ ( -n $path && $(echo "$path" | cut -c-1) != "/" ) || ( -z $path && $(echo "${1}" | rev | cut -c-1) == "/" ) ]] && local path="/$path"
[[ -n $path && $out =~ ^(unix|http\+unix|https\+unix)$ ]] && local path=":${path}"
# Final validation, just because we need to be really sure! (double check!!)
if [[ -n $scheme && -n $host && -n $port ]]; then
local url_final="${scheme}://${host}:${port}${path}"
elif [[ -z $scheme && -n $host && -n $port ]]; then
local url_final="${host}:${port}${path}"
elif [[ -n $scheme && -n $host && -z $port ]]; then
local url_final="${scheme}://${host}${path}"
elif [[ -z $scheme && -n $host && -z $port ]]; then
local url_final="${host}${path}"
fi
[[ $url_final != ${1,,} ]] && out="false" # SHORT CIRCUIT!!!
# Final output!
if [[ $2 == "-split" && $out != "false" ]]; then
url_scheme=$scheme
url_host=$host
[[ -n $path || $(echo "${1}" | rev | cut -c-1) == "/" ]] && url_path="/$path" # Check if / is alone, it's also a valid path!
url_port=$port
[[ -n $scheme ]] && url_scheme=$scheme
[[ -n $path ]] && url_path=$path
[[ -n $port ]] && url_port=$port
url_host=$host # We are very sure these two vars are never empty! ;)
url_type=$out # empty/unset when false
elif [[ $2 != "-split" ]]; then
echo $out
fi

View file

@ -73,7 +73,7 @@ http_header_xfo() {
fi
if [[ $header_data == "ALLOW-FROM" ]]; then
if [[ -z $(conf_read header-xfo-url) ]] || ! [[ $(is_url $(conf_read header-xfo-url)) =~ ^(http|https|ip|true)$ ]]; then
if [[ -z $(conf_read header-xfo-url) ]] || ! [[ $(is_url $(conf_read header-xfo-url)) =~ ^(http|https|true|http\+ip|https\+ip|ip)$ ]]; then
echo "${red}[ERROR] Invalid URL for X-Frame-Options header!${end}"
return 1
fi

View file

@ -166,7 +166,7 @@ wpinstall() {
url_path=""
done="0"
[[ $type == [2345] ]] && exit 1 || continue 1
elif [[ $(is_url $dbhost) =~ ^(http|https|ip|true)$ ]]; then
elif [[ $url_type =~ ^(http|https|true|http\+ip|https\+ip|ip)$ ]]; then
local dburl=$url_host
local dbport=$url_port
else
@ -1238,10 +1238,12 @@ parked_domain() {
forward_domain() {
[[ -z $forward || $forward == "true" ]] && read -p "${gre}Destination domain: ${end}" forward
if ! [[ $(is_url $forward) =~ ^(http|https|true)$ ]]; then
[[ -n $forward ]] && is_url $forward -split
if ! [[ $url_type =~ ^(http|https|true)$ ]]; then
echo "${red}[ERROR] Invalid destination domain!${end}"
exit 1
elif [[ $(is_url $forward) == "true" ]]; then
elif [[ $url_type == "true" ]]; then
forward="http://${forward}"
fi
@ -1276,7 +1278,17 @@ reverse_proxy() {
read -p "${blu}URL address: ${end}" proxydata
fi
if ! [[ -n $proxydata && $(is_url $proxydata) =~ ^(http|https|ip|unix|true)$ ]]; then
if [[ -n $proxydata ]]; then
is_url $proxydata -split
[[ -z $url_scheme ]] && local proxydata="http://${proxydata}"
# Force / at the end of the url for subfolders
[[ $dedicated_reverse_proxy == "simple" && -n $url_path && $url_path != "/" && $(echo "${proxydata}" | rev | cut -c-1) != "/" ]] && local proxydata="${proxydata}/"
# Fix slash at the end when is alone, no real URI path!
[[ $dedicated_reverse_proxy != "simple" && $url_path == "/" ]] && local proxydata=${proxydata:0:-1}
is_url $proxydata -split # Reset in case data has changed!
fi
if ! [[ $url_type =~ ^(http|https|true|http\+ip|https\+ip|ip|http\+unix|https\+unix|unix)$ ]]; then
echo "${red}[ERROR] Please, enter a valid host and port!${end}"
exit 1
elif [[ -f /etc/nginx/sites-available/$domain && -z $subfolder && (($(is_empty_root_site $domain) == "full" && $(is_proxy $domain) != "false") || $(is_empty_root_site $domain) == "false") ]]; then
@ -1291,30 +1303,9 @@ reverse_proxy() {
elif [[ $(is_empty_root_site $domain) == "full" && $(is_dedicated_proxy_domain $domain) == "false" ]]; then
echo "${red}[ERROR] This domain is currently set as Empty (blank), to create a site you need to add your own NGINX configuration. ${dim}(/var/www/example.com/*-nginx.conf)!${end}"
exit 1
fi
is_url $proxydata -split
# URL Scheme correction... Just in case of empty scheme, http is default!
if [[ -z $url_scheme || ${url_scheme,,} == "unix" ]]; then
local proxydata="http://${proxydata}"
url_scheme="http"
fi
# After create-site this global variable is empty, because running multiple times
local local_url_scheme=$url_scheme
local local_url_host=$url_host
local local_url_port=$url_port
local local_url_path=$url_path
# Fix slash at the end when is alone, no real URI path!
if [[ $dedicated_reverse_proxy != "simple" && $url_path == "/" ]]; then
local proxydata=${proxydata:0:-1}
unset url_path
fi
elif [[ $dedicated_reverse_proxy != "simple" && -n $url_path ]]; then
# URI part (subfolders) are not allowed because the proxy configuration we are using includes the static files location, this location block is using regex which is not allowed!
# Dedicated reverse proxy are also affected (don't have static file location block), but subfolders are not allowed in upstream servers.
if [[ $dedicated_reverse_proxy != "simple" && -n $url_path ]]; then
echo "${red}[ERROR] Subfolder in your endpoint or upstream server is not supported!${dim} (URI: $url_path)${end}"
exit 1
fi
@ -1340,12 +1331,12 @@ reverse_proxy() {
sudo sed -i "/^location \/ {/c location $subfolder/ {" /etc/nginx/apps.d/$domain$subname-proxy.conf
fi
is_url $proxydata -split # Run again! (after create-site these global variables are empty, because running multiple times)
if [[ $dedicated_reverse_proxy == "simple" ]]; then
# Force / at the end of the url for subfolders
[[ -n $local_url_path && $local_url_path != "/" && $(echo "${proxydata}" | rev | cut -c-1) != "/" ]] && proxydata="${proxydata}/"
sudo sed -i "s#<upstream_name>#${proxydata}#g" /etc/nginx/apps.d/$domain$subname-proxy.conf
else
[[ -n $local_url_scheme ]] && sudo sed -i "s#proxy_pass <upstream_name>#proxy_pass ${local_url_scheme,,}://<upstream_name>#g" /etc/nginx/apps.d/$domain$subname-proxy.conf
[[ -n $url_scheme ]] && sudo sed -i "s#proxy_pass <upstream_name>#proxy_pass ${url_scheme,,}://<upstream_name>#g" /etc/nginx/apps.d/$domain$subname-proxy.conf
sudo sed -i "s#<upstream_name>#$(echo "$domain$subfolder" | sed "s/[^0-9A-Za-z]/_/g")#g" /etc/nginx/apps.d/$domain$subname-proxy.conf
# Create the upstream
@ -1356,9 +1347,9 @@ reverse_proxy() {
fi
# Upstream server needs port, if empty, default is 80, so we need to fix it in case of https.
[[ -z $local_url_port && ${local_url_scheme,,} == "https" ]] && local fixport=":443"
[[ -z $url_port && $url_type =~ ^(https|https\+ip)$ ]] && local fixport=":443"
local upsvr="$(echo "${proxydata}${fixport}" | sed "s#${local_url_scheme}://##g")"
local upsvr="$(echo "${proxydata}${fixport}" | sed "s#${url_scheme}://##g")"
echo "upstream $(echo "$domain$subfolder" | sed "s/[^0-9A-Za-z]/_/g") {
zone upstreams;
@ -1378,7 +1369,7 @@ reverse_proxy() {
# Set Host header for external sources, except when IP or Unix socket is used!
# Simple dedicated is also excluded because proxy_pass seems to not need it.
if [[ -n $upsvr && -n $proxydata && $local_url_host != "localhost" && $(is_url $local_url_host) != "ip" && $(is_url $local_url_host) != "unix" ]]; then
if [[ -n $upsvr && $url_host != "localhost" && $url_type =~ ^(http|https|true)$ ]]; then
sudo sed -i "/#proxy_set_header Host /c\ proxy_set_header Host '${upsvr}';" /etc/nginx/apps.d/$domain$subname-proxy.conf
fi

View file

@ -158,15 +158,17 @@ smtp_setup() {
elif [[ ! -f /etc/nginx/sites-available/$mainhost ]]; then
echo "${red}[ERROR] Main Host site not found in your server!${end}"
exit 1
elif [[ $(is_url $host) != "true" ]]; then
if [[ $(is_url $host) =~ ^(http|https)$ ]]; then
echo "${red}[ERROR] Invalid SMTP host! (HTTP/HTTPS can not be used in conjunction with SMTP protocol)${end}"
fi
is_url $host -split
if [[ $url_type != "true" ]]; then
if [[ $url_type =~ ^(http|https)$ ]]; then
echo "${red}[ERROR] Invalid SMTP host! (HTTP/HTTPS scheme can not be used in conjunction with SMTP protocol)${end}"
else
echo "${red}[ERROR] Invalid SMTP host!${end}"
fi
exit 1
else
is_url $host -split
if [[ -n $url_port ]]; then
echo "${red}[ERROR] Invalid SMTP host! (Custom port is not supported)${end}"
exit 1

View file

@ -278,6 +278,8 @@
# HTTP Header: X-Frame-Options
# Values: deny | sameorigin | allow-from | off
# Default: sameorigin
# Note: This header is no longer recommended and support will be removed very soon from Webinoly.
# Note: The Content-Security-Policy HTTP header has a frame-ancestors directive which obsoletes this header for supporting browsers.
# Note: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
###########################
#header-xfo:sameorigin
@ -297,6 +299,7 @@
# Values: boolean (true/false)
# Default: false
# Note: This header is no longer recommended and support will be removed very soon from Webinoly.
# Note: These protections are largely unnecessary in modern browsers when sites implement a strong Content-Security-Policy.
# Note: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
# Note: When true is set: (X-XSS-Protection: 1; mode=block)
###########################