diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2c844337..b0ebb90e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3.1.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6b70a153..a17d5a94 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: issues: write steps: - - uses: actions/stale@v5.2.0 + - uses: actions/stale@v6.0.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 30 diff --git a/.github/workflows/sync-back-to-dev.yml b/.github/workflows/sync-back-to-dev.yml index dd66ebdc..f689ae36 100644 --- a/.github/workflows/sync-back-to-dev.yml +++ b/.github/workflows/sync-back-to-dev.yml @@ -11,7 +11,7 @@ jobs: name: Syncing branches steps: - name: Checkout - uses: actions/checkout@v3.0.2 + uses: actions/checkout@v3.1.0 - name: Opening pull request id: pull uses: tretuna/sync-branches@1.4.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7725ecc3..ff3af994 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,29 +12,33 @@ jobs: if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - - - name: Checkout repository - uses: actions/checkout@v3.0.2 - - - name: Check scripts in repository are executable - run: | - IFS=$'\n'; - for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done - unset IFS; - # If FAIL is 1 then we fail. - [[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!" - - - name: Spell-Checking - uses: codespell-project/actions-codespell@master - with: - ignore_words_file: .codespellignore - - - name: Get editorconfig-checker - uses: editorconfig-checker/action-editorconfig-checker@main # tag v1.0.0 is really out of date - - - name: Run editorconfig-checker - run: editorconfig-checker + - name: Checkout repository + uses: actions/checkout@v3.1.0 + - name: Check scripts in repository are executable + run: | + IFS=$'\n'; + for f in $(find . -name '*.sh'); do if [[ ! -x $f ]]; then echo "$f is not executable" && FAIL=1; fi ;done + unset IFS; + # If FAIL is 1 then we fail. + [[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!" + + - name: Spell-Checking + uses: codespell-project/actions-codespell@master + with: + ignore_words_file: .codespellignore + + - name: Get editorconfig-checker + uses: editorconfig-checker/action-editorconfig-checker@main # tag v1.0.0 is really out of date + + - name: Run editorconfig-checker + run: editorconfig-checker + + - name: Check python code formatting with black + uses: psf/black@stable + with: + src: "./test" + options: "--check --diff --color" distro-test: if: github.event.pull_request.draft == false @@ -43,21 +47,29 @@ jobs: strategy: fail-fast: false matrix: - distro: [debian_10, debian_11, ubuntu_20, ubuntu_22, centos_8, fedora_34] + distro: + [ + debian_10, + debian_11, + ubuntu_20, + ubuntu_22, + centos_8, + fedora_35, + fedora_36, + ] env: DISTRO: ${{matrix.distro}} steps: - - - name: Checkout repository - uses: actions/checkout@v3.0.2 - - - name: Set up Python 3.10 - uses: actions/setup-python@v4.2.0 - with: - python-version: '3.10' - - - name: Install dependencies - run: pip install -r test/requirements.txt - - - name: Test with tox - run: tox -c test/tox.${DISTRO}.ini + - name: Checkout repository + uses: actions/checkout@v3.1.0 + + - name: Set up Python 3.10 + uses: actions/setup-python@v4.2.0 + with: + python-version: "3.10" + + - name: Install dependencies + run: pip install -r test/requirements.txt + + - name: Test with tox + run: tox -c test/tox.${DISTRO}.ini diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 1fb34905..677910f6 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -29,13 +29,6 @@ bogus-priv no-resolv -server=@DNS1@ -server=@DNS2@ - -interface=@INT@ - -cache-size=@CACHE_SIZE@ - log-queries log-facility=/var/log/pihole/pihole.log diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index fddb3936..d69a56d3 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -14,7 +14,9 @@ LC_NUMERIC=C # Retrieve stats from FTL engine pihole-FTL() { local ftl_port LINE - ftl_port=$(cat /run/pihole-FTL.port 2> /dev/null) + # shellcheck disable=SC1091 + . /opt/pihole/utils.sh + ftl_port=$(getFTLAPIPort) if [[ -n "$ftl_port" ]]; then # Open connection to FTL exec 3<>"/dev/tcp/127.0.0.1/$ftl_port" @@ -503,11 +505,11 @@ chronoFunc() { fi printFunc " Pi-hole: " "$ph_status" "$ph_info" - printFunc " Ads Today: " "$ads_percentage_today%" "$ads_info" + printFunc " Blocked: " "$ads_percentage_today%" "$ads_info" printFunc "Local Qrys: " "$queries_cached_percentage%" "$dns_info" - printFunc " Blocked: " "$recent_blocked" - printFunc "Top Advert: " "$top_ad" + printFunc "Last Block: " "$recent_blocked" + printFunc " Top Block: " "$top_ad" # Provide more stats on screens with more lines if [[ "$scr_lines" -eq 17 ]]; then diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 7c4a1f77..8c4c6db3 100755 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -42,6 +42,11 @@ warning1() { esac } +updateCheckFunc() { + /opt/pihole/updatecheck.sh + /opt/pihole/updatecheck.sh x remote +} + checkout() { local corebranches local webbranches @@ -164,6 +169,8 @@ checkout() { exit 1 fi checkout_pull_branch "${webInterfaceDir}" "${2}" + # Force an update of the updatechecker + updateCheckFunc elif [[ "${1}" == "ftl" ]] ; then local path local oldbranch @@ -178,6 +185,8 @@ checkout() { FTLinstall "${binary}" restart_service pihole-FTL enable_service pihole-FTL + # Force an update of the updatechecker + updateCheckFunc else echo " ${CROSS} Requested branch \"${2}\" is not available" ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') ) diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index ab16d7ff..dbf56709 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -126,7 +126,6 @@ PIHOLE_COMMAND="${BIN_DIRECTORY}/pihole" PIHOLE_COLTABLE_FILE="${BIN_DIRECTORY}/COL_TABLE" FTL_PID="${RUN_DIRECTORY}/pihole-FTL.pid" -FTL_PORT="${RUN_DIRECTORY}/pihole-FTL.port" PIHOLE_LOG="${LOG_DIRECTORY}/pihole.log" PIHOLE_LOG_GZIPS="${LOG_DIRECTORY}/pihole.log.[0-9].*" @@ -155,7 +154,6 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}" "${PIHOLE_COMMAND}" "${PIHOLE_COLTABLE_FILE}" "${FTL_PID}" -"${FTL_PORT}" "${PIHOLE_LOG}" "${PIHOLE_LOG_GZIPS}" "${PIHOLE_DEBUG_LOG}" @@ -678,15 +676,20 @@ ping_gateway() { local protocol="${1}" ping_ipv4_or_ipv6 "${protocol}" # Check if we are using IPv4 or IPv6 - # Find the default gateway using IPv4 or IPv6 + # Find the default gateways using IPv4 or IPv6 local gateway - gateway="$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3)" - # If the gateway variable has a value (meaning a gateway was found), - if [[ -n "${gateway}" ]]; then - log_write "${INFO} Default IPv${protocol} gateway: ${gateway}" + log_write "${INFO} Default IPv${protocol} gateway(s):" + + while IFS= read -r gateway; do + log_write " ${gateway}" + done < <(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3) + + gateway=$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3 | head -n 1) + # If there was at least one gateway + if [ -n "${gateway}" ]; then # Let the user know we will ping the gateway for a response - log_write " * Pinging ${gateway}..." + log_write " * Pinging first gateway ${gateway}..." # Try to quietly ping the gateway 3 times, with a timeout of 3 seconds, using numeric output only, # on the pihole interface, and tail the last three lines of the output # If pinging the gateway is not successful, diff --git a/advanced/Scripts/updatecheck.sh b/advanced/Scripts/updatecheck.sh index b1e111ae..a9d7523e 100755 --- a/advanced/Scripts/updatecheck.sh +++ b/advanced/Scripts/updatecheck.sh @@ -38,6 +38,14 @@ VERSION_FILE="/etc/pihole/versions" touch "${VERSION_FILE}" chmod 644 "${VERSION_FILE}" +# if /pihole.docker.tag file exists, we will use it's value later in this script +DOCKER_TAG=$(cat /pihole.docker.tag 2>/dev/null) +regex='^([0-9]+\.){1,2}(\*|[0-9]+)(-.*)?$|(^nightly$)|(^dev.*$)' +if [[ ! "${DOCKER_TAG}" =~ $regex ]]; then + # DOCKER_TAG does not match the pattern (see https://regex101.com/r/RsENuz/1), so unset it. + unset DOCKER_TAG +fi + if [[ "$2" == "remote" ]]; then if [[ "$3" == "reboot" ]]; then @@ -55,7 +63,7 @@ if [[ "$2" == "remote" ]]; then GITHUB_FTL_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null | jq --raw-output .tag_name)" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_VERSION" "${GITHUB_FTL_VERSION}" - if [[ "${PIHOLE_DOCKER_TAG}" ]]; then + if [[ "${DOCKER_TAG}" ]]; then GITHUB_DOCKER_VERSION="$(curl -s 'https://api.github.com/repos/pi-hole/docker-pi-hole/releases/latest' 2> /dev/null | jq --raw-output .tag_name)" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_DOCKER_VERSION" "${GITHUB_DOCKER_VERSION}" fi @@ -84,9 +92,8 @@ else FTL_VERSION="$(pihole-FTL version)" addOrEditKeyValPair "${VERSION_FILE}" "FTL_VERSION" "${FTL_VERSION}" - # PIHOLE_DOCKER_TAG is set as env variable only on docker installations - if [[ "${PIHOLE_DOCKER_TAG}" ]]; then - addOrEditKeyValPair "${VERSION_FILE}" "DOCKER_VERSION" "${PIHOLE_DOCKER_TAG}" + if [[ "${DOCKER_TAG}" ]]; then + addOrEditKeyValPair "${VERSION_FILE}" "DOCKER_VERSION" "${DOCKER_TAG}" fi fi diff --git a/advanced/Scripts/utils.sh b/advanced/Scripts/utils.sh index a9e05692..1174fa62 100755 --- a/advanced/Scripts/utils.sh +++ b/advanced/Scripts/utils.sh @@ -32,8 +32,8 @@ addOrEditKeyValPair() { local value="${3}" if grep -q "^${key}=" "${file}"; then - # Key already exists in file, modify the value - sed -i "/^${key}=/c\\${key}=${value}" "${file}" + # Key already exists in file, modify the value + sed -i "/^${key}=/c\\${key}=${value}" "${file}" else # Key does not already exist, add it and it's value echo "${key}=${value}" >> "${file}" @@ -52,8 +52,8 @@ addKey(){ local key="${2}" if ! grep -q "^${key}" "${file}"; then - # Key does not exist, add it. - echo "${key}" >> "${file}" + # Key does not exist, add it. + echo "${key}" >> "${file}" fi } @@ -70,47 +70,27 @@ removeKey() { sed -i "/^${key}/d" "${file}" } -####################### -# returns path of FTL's port file -####################### -getFTLAPIPortFile() { - local FTLCONFFILE="/etc/pihole/pihole-FTL.conf" - local DEFAULT_PORT_FILE="/run/pihole-FTL.port" - local FTL_APIPORT_FILE - - if [ -s "${FTLCONFFILE}" ]; then - # if PORTFILE is not set in pihole-FTL.conf, use the default path - FTL_APIPORT_FILE="$({ grep '^PORTFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PORT_FILE}"; } | cut -d'=' -f2-)" - else - # if there is no pihole-FTL.conf, use the default path - FTL_APIPORT_FILE="${DEFAULT_PORT_FILE}" - fi - - echo "${FTL_APIPORT_FILE}" -} - -####################### -# returns FTL's current telnet API port based on the content of the pihole-FTL.port file -# -# Takes one argument: path to pihole-FTL.port -# Example getFTLAPIPort "/run/pihole-FTL.port" ####################### +# returns FTL's current telnet API port based on the setting in /etc/pihole-FTL.conf +######################## getFTLAPIPort(){ - local PORTFILE="${1}" + local FTLCONFFILE="/etc/pihole/pihole-FTL.conf" local DEFAULT_FTL_PORT=4711 local ftl_api_port - if [ -s "$PORTFILE" ]; then - # -s: FILE exists and has a size greater than zero - ftl_api_port=$(cat "${PORTFILE}") - # Exploit prevention: unset the variable if there is malicious content - # Verify that the value read from the file is numeric - expr "$ftl_api_port" : "[^[:digit:]]" > /dev/null && unset ftl_api_port + if [ -s "$FTLCONFFILE" ]; then + # if FTLPORT is not set in pihole-FTL.conf, use the default port + ftl_api_port="$({ grep '^FTLPORT=' "${FTLCONFFILE}" || echo "${DEFAULT_FTL_PORT}"; } | cut -d'=' -f2-)" + # Exploit prevention: set the port to the default port if there is malicious (non-numeric) + # content set in pihole-FTL.conf + expr "${ftl_api_port}" : "[^[:digit:]]" > /dev/null && ftl_api_port="${DEFAULT_FTL_PORT}" + else + # if there is no pihole-FTL.conf, use the default port + ftl_api_port="${DEFAULT_FTL_PORT}" fi - # echo the port found in the portfile or default to the default port - echo "${ftl_api_port:=$DEFAULT_FTL_PORT}" + echo "${ftl_api_port}" } ####################### diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 3ee48aef..e02e03f9 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -393,13 +393,8 @@ ProcessDHCPSettings() { if [[ "${DHCP_LEASETIME}" == "0" ]]; then leasetime="infinite" elif [[ "${DHCP_LEASETIME}" == "" ]]; then - leasetime="24" - addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "${leasetime}" - elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then - #Installation is affected by known bug, introduced in a previous version. - #This will automatically clean up setupVars.conf and remove the unnecessary "h" - leasetime="24" - addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "${leasetime}" + leasetime="24h" + addOrEditKeyValPair "${setupVars}" "DHCP_LEASETIME" "24" else leasetime="${DHCP_LEASETIME}h" fi diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service index 46e5c1f2..bc1b1d20 100644 --- a/advanced/Templates/pihole-FTL.service +++ b/advanced/Templates/pihole-FTL.service @@ -9,7 +9,7 @@ # Description: Enable service provided by pihole-FTL daemon ### END INIT INFO -#source utils.sh for getFTLPIDFile(), getFTLPID (), getFTLAPIPortFile() +#source utils.sh for getFTLPIDFile(), getFTLPID () PI_HOLE_SCRIPT_DIR="/opt/pihole" utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" . "${utilsfile}" @@ -31,7 +31,6 @@ start() { # Touch files to ensure they exist (create if non-existing, preserve if existing) mkdir -pm 0755 /run/pihole /var/log/pihole [ ! -f "${FTL_PID_FILE}" ] && install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PID_FILE}" - [ ! -f "${FTL_PORT_FILE}" ] && install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PORT_FILE}" [ ! -f /var/log/pihole/FTL.log ] && install -m 644 -o pihole -g pihole /dev/null /var/log/pihole/FTL.log [ ! -f /var/log/pihole/pihole.log ] && install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/pihole.log [ ! -f /etc/pihole/dhcp.leases ] && install -m 644 -o pihole -g pihole /dev/null /etc/pihole/dhcp.leases @@ -91,7 +90,7 @@ stop() { echo "Not running" fi # Cleanup - rm -f /run/pihole/FTL.sock /dev/shm/FTL-* "${FTL_PID_FILE}" "${FTL_PORT_FILE}" + rm -f /run/pihole/FTL.sock /dev/shm/FTL-* "${FTL_PID_FILE}" echo } @@ -111,7 +110,6 @@ status() { # Get file paths FTL_PID_FILE="$(getFTLPIDFile)" -FTL_PORT_FILE="$(getFTLAPIPortFile)" # Get FTL's current PID FTL_PID="$(getFTLPID ${FTL_PID_FILE})" diff --git a/advanced/Templates/pihole.cron b/advanced/Templates/pihole.cron index 46640fbb..336a66fe 100644 --- a/advanced/Templates/pihole.cron +++ b/advanced/Templates/pihole.cron @@ -28,9 +28,6 @@ @reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate -# Pi-hole: Grab local version and branch every 10 minutes -*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local - # Pi-hole: Grab remote version every 24 hours 59 17 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote @reboot root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker remote reboot diff --git a/advanced/index.php b/advanced/index.php index 9a2b19e6..f3f2ce1c 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -29,7 +29,8 @@ if (!empty($_SERVER["FQDN"])) { if ($serverName === "pi.hole" || (!empty($_SERVER["VIRTUAL_HOST"]) && $serverName === $_SERVER["VIRTUAL_HOST"])) { // Redirect to Web Interface - exit(header("Location: /admin")); + header("Location: /admin"); + exit(); } elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) { // When directly browsing via IP or authorized hostname // Render splash/landing page based off presence of $landPage file @@ -75,6 +76,6 @@ EOT; exit($splashPage); } -exit(header("HTTP/1.1 404 Not Found")); - +header("HTTP/1.1 404 Not Found"); +exit(); ?> diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index a51f5d9a..d4c1ce77 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -83,6 +83,7 @@ PI_HOLE_INSTALL_DIR="/opt/pihole" PI_HOLE_CONFIG_DIR="/etc/pihole" PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_404_DIR="${webroot}/pihole" +FTL_CONFIG_FILE="${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" if [ -z "$useUpdateVars" ]; then useUpdateVars=false fi @@ -999,10 +1000,10 @@ If you want to specify a port other than 53, separate it with a hash.\ # and continue the loop. DNSSettingsCorrect=False else - dialog --no-shadow --keep-tite \ + dialog --no-shadow --no-collapse --keep-tite \ --backtitle "Specify Upstream DNS Provider(s)" \ --title "Upstream DNS Provider(s)" \ - --yesno "Are these settings correct?\\n\\tDNS Server 1:\\t${PIHOLE_DNS_1}\\n\\tDNS Server 2:\\t${PIHOLE_DNS_2}" \ + --yesno "Are these settings correct?\\n"$'\t'"DNS Server 1:"$'\t'"${PIHOLE_DNS_1}\\n"$'\t'"DNS Server 2:"$'\t'"${PIHOLE_DNS_2}" \ "${r}" "${c}" && result=0 || result=$? case ${result} in @@ -1264,35 +1265,30 @@ version_check_dnsmasq() { # Copy the new Pi-hole DNS config file into the dnsmasq.d directory install -D -m 644 -T "${dnsmasq_pihole_01_source}" "${dnsmasq_pihole_01_target}" printf "%b %b Installed %s\n" "${OVER}" "${TICK}" "${dnsmasq_pihole_01_target}" - # Replace our placeholder values with the GLOBAL DNS variables that we populated earlier - # First, swap in the interface to listen on, - sed -i "s/@INT@/$PIHOLE_INTERFACE/" "${dnsmasq_pihole_01_target}" + # Add settings with the GLOBAL DNS variables that we populated earlier + # First, set the interface to listen on + addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "interface" "$PIHOLE_INTERFACE" if [[ "${PIHOLE_DNS_1}" != "" ]]; then - # then swap in the primary DNS server. - sed -i "s/@DNS1@/$PIHOLE_DNS_1/" "${dnsmasq_pihole_01_target}" - else - # Otherwise, remove the line which sets DNS1. - sed -i '/^server=@DNS1@/d' "${dnsmasq_pihole_01_target}" + # then add in the primary DNS server. + addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "server" "$PIHOLE_DNS_1" fi # Ditto if DNS2 is not empty if [[ "${PIHOLE_DNS_2}" != "" ]]; then - sed -i "s/@DNS2@/$PIHOLE_DNS_2/" "${dnsmasq_pihole_01_target}" - else - sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}" + addKey "${dnsmasq_pihole_01_target}" "server=$PIHOLE_DNS_2" fi # Set the cache size - sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}" + addOrEditKeyValPair "${dnsmasq_pihole_01_target}" "cache-size" "$CACHE_SIZE" sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}" # If the user does not want to enable logging, if [[ "${QUERY_LOGGING}" == false ]] ; then - # disable it by commenting out the directive in the DNS config file - sed -i 's/^log-queries/#log-queries/' "${dnsmasq_pihole_01_target}" + # remove itfrom the DNS config file + removeKey "${dnsmasq_pihole_01_target}" "log-queries" else - # Otherwise, enable it by uncommenting the directive in the DNS config file - sed -i 's/^#log-queries/log-queries/' "${dnsmasq_pihole_01_target}" + # Otherwise, enable it by adding the directive to the DNS config file + addKey "${dnsmasq_pihole_01_target}" "log-queries" fi printf " %b Installing %s..." "${INFO}" "${dnsmasq_rfc6761_06_source}" @@ -1365,9 +1361,9 @@ installConfigs() { chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf" # Install template file if it does not exist - if [[ ! -r "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then + if [[ ! -r "${FTL_CONFIG_FILE}" ]]; then install -d -m 0755 ${PI_HOLE_CONFIG_DIR} - if ! install -T -o pihole -m 664 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.conf" "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" &>/dev/null; then + if ! install -T -o pihole -m 664 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.conf" "${FTL_CONFIG_FILE}" &>/dev/null; then printf " %b Error: Unable to initialize configuration file %s/pihole-FTL.conf\\n" "${COL_LIGHT_RED}" "${PI_HOLE_CONFIG_DIR}" return 1 fi @@ -1784,30 +1780,24 @@ create_pihole_user() { # This function saves any changes to the setup variables into the setupvars.conf file for future runs finalExports() { - # If the setup variable file exists, - if [[ -e "${setupVars}" ]]; then - # update the variables in the file - sed -i.update.bak '/PIHOLE_INTERFACE/d;/PIHOLE_DNS_1\b/d;/PIHOLE_DNS_2\b/d;/QUERY_LOGGING/d;/INSTALL_WEB_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;/CACHE_SIZE/d;/DNS_FQDN_REQUIRED/d;/DNS_BOGUS_PRIV/d;/DNSMASQ_LISTENING/d;' "${setupVars}" - fi - # echo the information to the user - { - echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" - echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" - echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" - echo "QUERY_LOGGING=${QUERY_LOGGING}" - echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" - echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" - echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" - echo "CACHE_SIZE=${CACHE_SIZE}" - echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}" - echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}" - echo "DNSMASQ_LISTENING=${DNSMASQ_LISTENING:-local}" - }>> "${setupVars}" + # set or update the variables in the file + + addOrEditKeyValPair "${setupVars}" "PIHOLE_INTERFACE" "${PIHOLE_INTERFACE}" + addOrEditKeyValPair "${setupVars}" "PIHOLE_DNS_1" "${PIHOLE_DNS_1}" + addOrEditKeyValPair "${setupVars}" "PIHOLE_DNS_2" "${PIHOLE_DNS_2}" + addOrEditKeyValPair "${setupVars}" "QUERY_LOGGING" "${QUERY_LOGGING}" + addOrEditKeyValPair "${setupVars}" "INSTALL_WEB_SERVER" "${INSTALL_WEB_SERVER}" + addOrEditKeyValPair "${setupVars}" "INSTALL_WEB_INTERFACE" "${INSTALL_WEB_INTERFACE}" + addOrEditKeyValPair "${setupVars}" "LIGHTTPD_ENABLED" "${LIGHTTPD_ENABLED}" + addOrEditKeyValPair "${setupVars}" "CACHE_SIZE" "${CACHE_SIZE}" + addOrEditKeyValPair "${setupVars}" "DNS_FQDN_REQUIRED" "${DNS_FQDN_REQUIRED:-true}" + addOrEditKeyValPair "${setupVars}" "DNS_BOGUS_PRIV" "${DNS_BOGUS_PRIV:-true}" + addOrEditKeyValPair "${setupVars}" "DNSMASQ_LISTENING" "${DNSMASQ_LISTENING:-local}" + chmod 644 "${setupVars}" # Set the privacy level - sed -i '/PRIVACYLEVEL/d' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" - echo "PRIVACYLEVEL=${PRIVACY_LEVEL}" >> "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" + addOrEditKeyValPair "${FTL_CONFIG_FILE}" "PRIVACYLEVEL" "${PRIVACY_LEVEL}" # Bring in the current settings and the functions to manipulate them source "${setupVars}" @@ -1895,6 +1885,16 @@ installPihole() { printf " %b Failure in dependent script copy function.\\n" "${CROSS}" exit 1 fi + + # /opt/pihole/utils.sh should be installed by installScripts now, so we can use it + if [ -f "${PI_HOLE_INSTALL_DIR}/utils.sh" ]; then + # shellcheck disable=SC1091 + source "${PI_HOLE_INSTALL_DIR}/utils.sh" + else + printf " %b Failure: /opt/pihole/utils.sh does not exist .\\n" "${CROSS}" + exit 1 + fi + # Install config files if ! installConfigs; then printf " %b Failure in dependent config copy function.\\n" "${CROSS}" @@ -2022,9 +2022,8 @@ update_dialogs() { \\n($strAdd)"\ "${r}" "${c}" 2 \ "${opt1a}" "${opt1b}" \ - "${opt2a}" "${opt2b}" || true) + "${opt2a}" "${opt2b}") || result=$? - result=$? case ${result} in "${DIALOG_CANCEL}" | "${DIALOG_ESC}") printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" @@ -2569,8 +2568,8 @@ main() { source "${setupVars}" # Get the privacy level if it exists (default is 0) - if [[ -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then - PRIVACY_LEVEL=$(sed -ne 's/PRIVACYLEVEL=\(.*\)/\1/p' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf") + if [[ -f "${FTL_CONFIG_FILE}" ]]; then + PRIVACY_LEVEL=$(sed -ne 's/PRIVACYLEVEL=\(.*\)/\1/p' "${FTL_CONFIG_FILE}") # If no setting was found, default to 0 PRIVACY_LEVEL="${PRIVACY_LEVEL:-0}" diff --git a/gravity.sh b/gravity.sh index 6f96b845..7c831b22 100755 --- a/gravity.sh +++ b/gravity.sh @@ -40,6 +40,7 @@ gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql" gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql" domainsExtension="domains" +curl_connect_timeout=10 # Source setupVars from install script setupVars="${piholeDir}/setupVars.conf" @@ -641,7 +642,7 @@ gravity_DownloadBlocklistFromUrl() { fi # shellcheck disable=SC2086 - httpCode=$(curl -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null) + httpCode=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null) case $url in # Did we "download" a local file? diff --git a/pihole b/pihole index 1047d152..aad83451 100755 --- a/pihole +++ b/pihole @@ -303,14 +303,13 @@ analyze_ports() { statusFunc() { # Determine if there is pihole-FTL service is listening - local pid port ftl_api_port ftl_pid_file ftl_apiport_file + local pid port ftl_api_port ftl_pid_file ftl_pid_file="$(getFTLPIDFile)" pid="$(getFTLPID ${ftl_pid_file})" - ftl_apiport_file="${getFTLAPIPortFile}" - ftl_api_port="$(getFTLAPIPort ${ftl_apiport_file})" + ftl_api_port="$(getFTLAPIPort)" if [[ "$pid" -eq "-1" ]]; then case "${1}" in "web") echo "-1";; diff --git a/test/_centos_8.Dockerfile b/test/_centos_8.Dockerfile index 2a894587..a07a67e9 100644 --- a/test/_centos_8.Dockerfile +++ b/test/_centos_8.Dockerfile @@ -1,5 +1,5 @@ FROM quay.io/centos/centos:stream8 -RUN yum install -y git +RUN yum install -y git initscripts ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/_fedora_34.Dockerfile b/test/_fedora_35.Dockerfile similarity index 91% rename from test/_fedora_34.Dockerfile rename to test/_fedora_35.Dockerfile index 9c90ce7d..83c17650 100644 --- a/test/_fedora_34.Dockerfile +++ b/test/_fedora_35.Dockerfile @@ -1,5 +1,5 @@ -FROM fedora:34 -RUN dnf install -y git +FROM fedora:35 +RUN dnf install -y git initscripts ENV GITDIR /etc/.pihole ENV SCRIPTDIR /opt/pihole diff --git a/test/_fedora_36.Dockerfile b/test/_fedora_36.Dockerfile new file mode 100644 index 00000000..847767e7 --- /dev/null +++ b/test/_fedora_36.Dockerfile @@ -0,0 +1,18 @@ +FROM fedora:36 +RUN dnf install -y git initscripts + +ENV GITDIR /etc/.pihole +ENV SCRIPTDIR /opt/pihole + +RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole +ADD . $GITDIR +RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/ +ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR + +RUN true && \ + chmod +x $SCRIPTDIR/* + +ENV SKIP_INSTALL true +ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net + +#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \ diff --git a/test/conftest.py b/test/conftest.py index fb7e1eea..e395ec27 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -6,12 +6,12 @@ from textwrap import dedent SETUPVARS = { - 'PIHOLE_INTERFACE': 'eth99', - 'PIHOLE_DNS_1': '4.2.2.1', - 'PIHOLE_DNS_2': '4.2.2.2' + "PIHOLE_INTERFACE": "eth99", + "PIHOLE_DNS_1": "4.2.2.1", + "PIHOLE_DNS_2": "4.2.2.2", } -IMAGE = 'pytest_pihole:test_container' +IMAGE = "pytest_pihole:test_container" tick_box = "[\x1b[1;32m\u2713\x1b[0m]" cross_box = "[\x1b[1;31m\u2717\x1b[0m]" @@ -38,132 +38,187 @@ testinfra.backend.docker.DockerBackend.run = run_bash @pytest.fixture def host(): # run a container - docker_id = subprocess.check_output( - ['docker', 'run', '-t', '-d', '--cap-add=ALL', IMAGE]).decode().strip() + docker_id = ( + subprocess.check_output(["docker", "run", "-t", "-d", "--cap-add=ALL", IMAGE]) + .decode() + .strip() + ) # return a testinfra connection to the container docker_host = testinfra.get_host("docker://" + docker_id) yield docker_host # at the end of the test suite, destroy the container - subprocess.check_call(['docker', 'rm', '-f', docker_id]) + subprocess.check_call(["docker", "rm", "-f", docker_id]) # Helper functions def mock_command(script, args, container): - ''' + """ Allows for setup of commands we don't really want to have to run for real in unit tests - ''' - full_script_path = '/usr/local/bin/{}'.format(script) - mock_script = dedent(r'''\ + """ + full_script_path = "/usr/local/bin/{}".format(script) + mock_script = dedent( + r"""\ #!/bin/bash -e echo "\$0 \$@" >> /var/log/{script} - case "\$1" in'''.format(script=script)) + case "\$1" in""".format( + script=script + ) + ) for k, v in args.items(): - case = dedent(''' + case = dedent( + """ {arg}) echo {res} exit {retcode} - ;;'''.format(arg=k, res=v[0], retcode=v[1])) + ;;""".format( + arg=k, res=v[0], retcode=v[1] + ) + ) mock_script += case - mock_script += dedent(''' - esac''') - container.run(''' + mock_script += dedent( + """ + esac""" + ) + container.run( + """ cat < {script}\n{content}\nEOF chmod +x {script} - rm -f /var/log/{scriptlog}'''.format(script=full_script_path, - content=mock_script, - scriptlog=script)) + rm -f /var/log/{scriptlog}""".format( + script=full_script_path, content=mock_script, scriptlog=script + ) + ) def mock_command_passthrough(script, args, container): - ''' + """ Per other mock_command* functions, allows intercepting of commands we don't want to run for real in unit tests, however also allows only specific arguments to be mocked. Anything not defined will be passed through to the actual command. Example use-case: mocking `git pull` but still allowing `git clone` to work as intended - ''' - orig_script_path = container.check_output('command -v {}'.format(script)) - full_script_path = '/usr/local/bin/{}'.format(script) - mock_script = dedent(r'''\ + """ + orig_script_path = container.check_output("command -v {}".format(script)) + full_script_path = "/usr/local/bin/{}".format(script) + mock_script = dedent( + r"""\ #!/bin/bash -e echo "\$0 \$@" >> /var/log/{script} - case "\$1" in'''.format(script=script)) + case "\$1" in""".format( + script=script + ) + ) for k, v in args.items(): - case = dedent(''' + case = dedent( + """ {arg}) echo {res} exit {retcode} - ;;'''.format(arg=k, res=v[0], retcode=v[1])) + ;;""".format( + arg=k, res=v[0], retcode=v[1] + ) + ) mock_script += case - mock_script += dedent(r''' + mock_script += dedent( + r""" *) {orig_script_path} "\$@" - ;;'''.format(orig_script_path=orig_script_path)) - mock_script += dedent(''' - esac''') - container.run(''' + ;;""".format( + orig_script_path=orig_script_path + ) + ) + mock_script += dedent( + """ + esac""" + ) + container.run( + """ cat < {script}\n{content}\nEOF chmod +x {script} - rm -f /var/log/{scriptlog}'''.format(script=full_script_path, - content=mock_script, - scriptlog=script)) + rm -f /var/log/{scriptlog}""".format( + script=full_script_path, content=mock_script, scriptlog=script + ) + ) def mock_command_run(script, args, container): - ''' + """ Allows for setup of commands we don't really want to have to run for real in unit tests - ''' - full_script_path = '/usr/local/bin/{}'.format(script) - mock_script = dedent(r'''\ + """ + full_script_path = "/usr/local/bin/{}".format(script) + mock_script = dedent( + r"""\ #!/bin/bash -e echo "\$0 \$@" >> /var/log/{script} - case "\$1 \$2" in'''.format(script=script)) + case "\$1 \$2" in""".format( + script=script + ) + ) for k, v in args.items(): - case = dedent(''' + case = dedent( + """ \"{arg}\") echo {res} exit {retcode} - ;;'''.format(arg=k, res=v[0], retcode=v[1])) + ;;""".format( + arg=k, res=v[0], retcode=v[1] + ) + ) mock_script += case - mock_script += dedent(''' - esac''') - container.run(''' + mock_script += dedent( + """ + esac""" + ) + container.run( + """ cat < {script}\n{content}\nEOF chmod +x {script} - rm -f /var/log/{scriptlog}'''.format(script=full_script_path, - content=mock_script, - scriptlog=script)) + rm -f /var/log/{scriptlog}""".format( + script=full_script_path, content=mock_script, scriptlog=script + ) + ) def mock_command_2(script, args, container): - ''' + """ Allows for setup of commands we don't really want to have to run for real in unit tests - ''' - full_script_path = '/usr/local/bin/{}'.format(script) - mock_script = dedent(r'''\ + """ + full_script_path = "/usr/local/bin/{}".format(script) + mock_script = dedent( + r"""\ #!/bin/bash -e echo "\$0 \$@" >> /var/log/{script} - case "\$1 \$2" in'''.format(script=script)) + case "\$1 \$2" in""".format( + script=script + ) + ) for k, v in args.items(): - case = dedent(''' + case = dedent( + """ \"{arg}\") echo \"{res}\" exit {retcode} - ;;'''.format(arg=k, res=v[0], retcode=v[1])) + ;;""".format( + arg=k, res=v[0], retcode=v[1] + ) + ) mock_script += case - mock_script += dedent(''' - esac''') - container.run(''' + mock_script += dedent( + """ + esac""" + ) + container.run( + """ cat < {script}\n{content}\nEOF chmod +x {script} - rm -f /var/log/{scriptlog}'''.format(script=full_script_path, - content=mock_script, - scriptlog=script)) + rm -f /var/log/{scriptlog}""".format( + script=full_script_path, content=mock_script, scriptlog=script + ) + ) def run_script(Pihole, script): diff --git a/test/requirements.txt b/test/requirements.txt index d65ee6a5..0eb22a1b 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,6 +1,5 @@ docker-compose pytest pytest-xdist -pytest-cov pytest-testinfra tox diff --git a/test/setup.py b/test/setup.py index 9f3b4b48..cdde20d3 100644 --- a/test/setup.py +++ b/test/setup.py @@ -2,6 +2,6 @@ from setuptools import setup setup( py_modules=[], - setup_requires=['pytest-runner'], - tests_require=['pytest'], + setup_requires=["pytest-runner"], + tests_require=["pytest"], ) diff --git a/test/test_any_automated_install.py b/test/test_any_automated_install.py index 70300243..0fd80f7e 100644 --- a/test/test_any_automated_install.py +++ b/test/test_any_automated_install.py @@ -10,39 +10,42 @@ from .conftest import ( mock_command_run, mock_command_2, mock_command_passthrough, - run_script + run_script, ) def test_supported_package_manager(host): - ''' + """ confirm installer exits when no supported package manager found - ''' + """ # break supported package managers - host.run('rm -rf /usr/bin/apt-get') - host.run('rm -rf /usr/bin/rpm') - package_manager_detect = host.run(''' + host.run("rm -rf /usr/bin/apt-get") + host.run("rm -rf /usr/bin/rpm") + package_manager_detect = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect - ''') - expected_stdout = cross_box + ' No supported package manager found' + """ + ) + expected_stdout = cross_box + " No supported package manager found" assert expected_stdout in package_manager_detect.stdout # assert package_manager_detect.rc == 1 def test_setupVars_are_sourced_to_global_scope(host): - ''' + """ currently update_dialogs sources setupVars with a dot, then various other functions use the variables. This confirms the sourced variables are in scope between functions - ''' - setup_var_file = 'cat < /etc/pihole/setupVars.conf\n' + """ + setup_var_file = "cat < /etc/pihole/setupVars.conf\n" for k, v in SETUPVARS.items(): setup_var_file += "{}={}\n".format(k, v) setup_var_file += "EOF\n" host.run(setup_var_file) - script = dedent('''\ + script = dedent( + """\ set -e printSetupVars() { # Currently debug test function only @@ -56,7 +59,8 @@ def test_setupVars_are_sourced_to_global_scope(host): } update_dialogs printSetupVars - ''') + """ + ) output = run_script(host, script).stdout @@ -65,27 +69,32 @@ def test_setupVars_are_sourced_to_global_scope(host): def test_setupVars_saved_to_file(host): - ''' + """ confirm saved settings are written to a file for future updates to re-use - ''' + """ # dedent works better with this and padding matching script below - set_setup_vars = '\n' + set_setup_vars = "\n" for k, v in SETUPVARS.items(): set_setup_vars += " {}={}\n".format(k, v) host.run(set_setup_vars) - script = dedent('''\ + script = dedent( + """\ set -e echo start TERM=xterm source /opt/pihole/basic-install.sh + source /opt/pihole/utils.sh {} mkdir -p /etc/dnsmasq.d version_check_dnsmasq echo "" > /etc/pihole/pihole-FTL.conf finalExports cat /etc/pihole/setupVars.conf - '''.format(set_setup_vars)) + """.format( + set_setup_vars + ) + ) output = run_script(host, script).stdout @@ -94,48 +103,52 @@ def test_setupVars_saved_to_file(host): def test_selinux_not_detected(host): - ''' + """ confirms installer continues when SELinux configuration file does not exist - ''' - check_selinux = host.run(''' + """ + check_selinux = host.run( + """ rm -f /etc/selinux/config source /opt/pihole/basic-install.sh checkSelinux - ''') - expected_stdout = info_box + ' SELinux not detected' + """ + ) + expected_stdout = info_box + " SELinux not detected" assert expected_stdout in check_selinux.stdout assert check_selinux.rc == 0 def test_installPiholeWeb_fresh_install_no_errors(host): - ''' + """ confirms all web page assets from Core repo are installed on a fresh build - ''' - installWeb = host.run(''' + """ + installWeb = host.run( + """ umask 0027 source /opt/pihole/basic-install.sh installPiholeWeb - ''') - expected_stdout = info_box + ' Installing 404 page...' + """ + ) + expected_stdout = info_box + " Installing 404 page..." assert expected_stdout in installWeb.stdout - expected_stdout = tick_box + (' Creating directory for 404 page, ' - 'and copying files') + expected_stdout = tick_box + ( + " Creating directory for 404 page, " "and copying files" + ) assert expected_stdout in installWeb.stdout - expected_stdout = info_box + ' Backing up index.lighttpd.html' + expected_stdout = info_box + " Backing up index.lighttpd.html" assert expected_stdout in installWeb.stdout - expected_stdout = ('No default index.lighttpd.html file found... ' - 'not backing up') + expected_stdout = "No default index.lighttpd.html file found... " "not backing up" assert expected_stdout in installWeb.stdout - expected_stdout = tick_box + ' Installing sudoer file' + expected_stdout = tick_box + " Installing sudoer file" assert expected_stdout in installWeb.stdout - web_directory = host.run('ls -r /var/www/html/pihole').stdout - assert 'index.php' in web_directory + web_directory = host.run("ls -r /var/www/html/pihole").stdout + assert "index.php" in web_directory def get_directories_recursive(host, directory): if directory is None: return directory - ls = host.run('ls -d {}'.format(directory + '/*/')) + ls = host.run("ls -d {}".format(directory + "/*/")) directories = list(filter(bool, ls.stdout.splitlines())) dirs = directories for dirval in directories: @@ -148,61 +161,41 @@ def get_directories_recursive(host, directory): def test_installPihole_fresh_install_readableFiles(host): - ''' + """ confirms all necessary files are readable by pihole user - ''' + """ # dialog returns Cancel for user prompt - mock_command('dialog', {'*': ('', '0')}, host) + mock_command("dialog", {"*": ("", "0")}, host) # mock git pull - mock_command_passthrough('git', {'pull': ('', '0')}, host) + mock_command_passthrough("git", {"pull": ("", "0")}, host) # mock systemctl to not start lighttpd and FTL mock_command_2( - 'systemctl', + "systemctl", { - 'enable lighttpd': ( - '', - '0' - ), - 'restart lighttpd': ( - '', - '0' - ), - 'start lighttpd': ( - '', - '0' - ), - 'enable pihole-FTL': ( - '', - '0' - ), - 'restart pihole-FTL': ( - '', - '0' - ), - 'start pihole-FTL': ( - '', - '0' - ), - '*': ( - 'echo "systemctl call with $@"', - '0' - ), + "enable lighttpd": ("", "0"), + "restart lighttpd": ("", "0"), + "start lighttpd": ("", "0"), + "enable pihole-FTL": ("", "0"), + "restart pihole-FTL": ("", "0"), + "start pihole-FTL": ("", "0"), + "*": ('echo "systemctl call with $@"', "0"), }, - host + host, ) # try to install man - host.run('command -v apt-get > /dev/null && apt-get install -qq man') - host.run('command -v dnf > /dev/null && dnf install -y man') - host.run('command -v yum > /dev/null && yum install -y man') + host.run("command -v apt-get > /dev/null && apt-get install -qq man") + host.run("command -v dnf > /dev/null && dnf install -y man") + host.run("command -v yum > /dev/null && yum install -y man") # create configuration file - setup_var_file = 'cat < /etc/pihole/setupVars.conf\n' + setup_var_file = "cat < /etc/pihole/setupVars.conf\n" for k, v in SETUPVARS.items(): setup_var_file += "{}={}\n".format(k, v) setup_var_file += "INSTALL_WEB_SERVER=true\n" setup_var_file += "INSTALL_WEB_INTERFACE=true\n" setup_var_file += "EOF\n" host.run(setup_var_file) - install = host.run(''' + install = host.run( + """ export TERM=xterm export DEBIAN_FRONTEND=noninteractive umask 0027 @@ -212,183 +205,164 @@ def test_installPihole_fresh_install_readableFiles(host): runUnattended=true useUpdateVars=true main - ''') + """ + ) assert 0 == install.rc maninstalled = True - if (info_box + ' man not installed') in install.stdout: + if (info_box + " man not installed") in install.stdout: maninstalled = False - piholeuser = 'pihole' + piholeuser = "pihole" exit_status_success = 0 test_cmd = 'su --shell /bin/bash --command "test -{0} {1}" -p {2}' # check files in /etc/pihole for read, write and execute permission - check_etc = test_cmd.format('r', '/etc/pihole', piholeuser) + check_etc = test_cmd.format("r", "/etc/pihole", piholeuser) actual_rc = host.run(check_etc).rc assert exit_status_success == actual_rc - check_etc = test_cmd.format('x', '/etc/pihole', piholeuser) + check_etc = test_cmd.format("x", "/etc/pihole", piholeuser) actual_rc = host.run(check_etc).rc assert exit_status_success == actual_rc # readable and writable dhcp.leases - check_leases = test_cmd.format('r', '/etc/pihole/dhcp.leases', piholeuser) + check_leases = test_cmd.format("r", "/etc/pihole/dhcp.leases", piholeuser) actual_rc = host.run(check_leases).rc assert exit_status_success == actual_rc - check_leases = test_cmd.format('w', '/etc/pihole/dhcp.leases', piholeuser) + check_leases = test_cmd.format("w", "/etc/pihole/dhcp.leases", piholeuser) actual_rc = host.run(check_leases).rc # readable dns-servers.conf assert exit_status_success == actual_rc - check_servers = test_cmd.format( - 'r', '/etc/pihole/dns-servers.conf', piholeuser) + check_servers = test_cmd.format("r", "/etc/pihole/dns-servers.conf", piholeuser) actual_rc = host.run(check_servers).rc assert exit_status_success == actual_rc # readable install.log - check_install = test_cmd.format( - 'r', '/etc/pihole/install.log', piholeuser) + check_install = test_cmd.format("r", "/etc/pihole/install.log", piholeuser) actual_rc = host.run(check_install).rc assert exit_status_success == actual_rc # readable versions - check_localversion = test_cmd.format( - 'r', '/etc/pihole/versions', piholeuser) + check_localversion = test_cmd.format("r", "/etc/pihole/versions", piholeuser) actual_rc = host.run(check_localversion).rc assert exit_status_success == actual_rc # readable logrotate - check_logrotate = test_cmd.format( - 'r', '/etc/pihole/logrotate', piholeuser) + check_logrotate = test_cmd.format("r", "/etc/pihole/logrotate", piholeuser) actual_rc = host.run(check_logrotate).rc assert exit_status_success == actual_rc # readable macvendor.db - check_macvendor = test_cmd.format( - 'r', '/etc/pihole/macvendor.db', piholeuser) + check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser) actual_rc = host.run(check_macvendor).rc assert exit_status_success == actual_rc # readable and writeable pihole-FTL.conf - check_FTLconf = test_cmd.format( - 'r', '/etc/pihole/pihole-FTL.conf', piholeuser) + check_FTLconf = test_cmd.format("r", "/etc/pihole/pihole-FTL.conf", piholeuser) actual_rc = host.run(check_FTLconf).rc assert exit_status_success == actual_rc - check_FTLconf = test_cmd.format( - 'w', '/etc/pihole/pihole-FTL.conf', piholeuser) + check_FTLconf = test_cmd.format("w", "/etc/pihole/pihole-FTL.conf", piholeuser) actual_rc = host.run(check_FTLconf).rc assert exit_status_success == actual_rc # readable setupVars.conf - check_setup = test_cmd.format( - 'r', '/etc/pihole/setupVars.conf', piholeuser) + check_setup = test_cmd.format("r", "/etc/pihole/setupVars.conf", piholeuser) actual_rc = host.run(check_setup).rc assert exit_status_success == actual_rc # check dnsmasq files # readable /etc/dnsmasq.conf - check_dnsmasqconf = test_cmd.format( - 'r', '/etc/dnsmasq.conf', piholeuser) + check_dnsmasqconf = test_cmd.format("r", "/etc/dnsmasq.conf", piholeuser) actual_rc = host.run(check_dnsmasqconf).rc assert exit_status_success == actual_rc # readable /etc/dnsmasq.d/01-pihole.conf - check_dnsmasqconf = test_cmd.format( - 'r', '/etc/dnsmasq.d', piholeuser) + check_dnsmasqconf = test_cmd.format("r", "/etc/dnsmasq.d", piholeuser) actual_rc = host.run(check_dnsmasqconf).rc assert exit_status_success == actual_rc - check_dnsmasqconf = test_cmd.format( - 'x', '/etc/dnsmasq.d', piholeuser) + check_dnsmasqconf = test_cmd.format("x", "/etc/dnsmasq.d", piholeuser) actual_rc = host.run(check_dnsmasqconf).rc assert exit_status_success == actual_rc check_dnsmasqconf = test_cmd.format( - 'r', '/etc/dnsmasq.d/01-pihole.conf', piholeuser) + "r", "/etc/dnsmasq.d/01-pihole.conf", piholeuser + ) actual_rc = host.run(check_dnsmasqconf).rc assert exit_status_success == actual_rc # check readable and executable /etc/init.d/pihole-FTL - check_init = test_cmd.format( - 'x', '/etc/init.d/pihole-FTL', piholeuser) + check_init = test_cmd.format("x", "/etc/init.d/pihole-FTL", piholeuser) actual_rc = host.run(check_init).rc assert exit_status_success == actual_rc - check_init = test_cmd.format( - 'r', '/etc/init.d/pihole-FTL', piholeuser) + check_init = test_cmd.format("r", "/etc/init.d/pihole-FTL", piholeuser) actual_rc = host.run(check_init).rc assert exit_status_success == actual_rc # check readable /etc/lighttpd/lighttpd.conf - check_lighttpd = test_cmd.format( - 'r', '/etc/lighttpd/lighttpd.conf', piholeuser) + check_lighttpd = test_cmd.format("r", "/etc/lighttpd/lighttpd.conf", piholeuser) actual_rc = host.run(check_lighttpd).rc assert exit_status_success == actual_rc # check readable and executable manpages if maninstalled is True: - check_man = test_cmd.format( - 'x', '/usr/local/share/man', piholeuser) + check_man = test_cmd.format("x", "/usr/local/share/man", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc - check_man = test_cmd.format( - 'r', '/usr/local/share/man', piholeuser) + check_man = test_cmd.format("r", "/usr/local/share/man", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc - check_man = test_cmd.format( - 'x', '/usr/local/share/man/man8', piholeuser) + check_man = test_cmd.format("x", "/usr/local/share/man/man8", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc - check_man = test_cmd.format( - 'r', '/usr/local/share/man/man8', piholeuser) + check_man = test_cmd.format("r", "/usr/local/share/man/man8", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc - check_man = test_cmd.format( - 'x', '/usr/local/share/man/man5', piholeuser) + check_man = test_cmd.format("x", "/usr/local/share/man/man5", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc - check_man = test_cmd.format( - 'r', '/usr/local/share/man/man5', piholeuser) + check_man = test_cmd.format("r", "/usr/local/share/man/man5", piholeuser) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc check_man = test_cmd.format( - 'r', '/usr/local/share/man/man8/pihole.8', piholeuser) + "r", "/usr/local/share/man/man8/pihole.8", piholeuser + ) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc check_man = test_cmd.format( - 'r', '/usr/local/share/man/man8/pihole-FTL.8', piholeuser) + "r", "/usr/local/share/man/man8/pihole-FTL.8", piholeuser + ) actual_rc = host.run(check_man).rc assert exit_status_success == actual_rc # check not readable sudoers file - check_sudo = test_cmd.format( - 'r', '/etc/sudoers.d/pihole', piholeuser) + check_sudo = test_cmd.format("r", "/etc/sudoers.d/pihole", piholeuser) actual_rc = host.run(check_sudo).rc assert exit_status_success != actual_rc # check not readable cron file - check_sudo = test_cmd.format( - 'x', '/etc/cron.d/', piholeuser) + check_sudo = test_cmd.format("x", "/etc/cron.d/", piholeuser) actual_rc = host.run(check_sudo).rc assert exit_status_success == actual_rc - check_sudo = test_cmd.format( - 'r', '/etc/cron.d/', piholeuser) + check_sudo = test_cmd.format("r", "/etc/cron.d/", piholeuser) actual_rc = host.run(check_sudo).rc assert exit_status_success == actual_rc - check_sudo = test_cmd.format( - 'r', '/etc/cron.d/pihole', piholeuser) + check_sudo = test_cmd.format("r", "/etc/cron.d/pihole", piholeuser) actual_rc = host.run(check_sudo).rc assert exit_status_success == actual_rc - directories = get_directories_recursive(host, '/etc/.pihole/') + directories = get_directories_recursive(host, "/etc/.pihole/") for directory in directories: - check_pihole = test_cmd.format('r', directory, piholeuser) + check_pihole = test_cmd.format("r", directory, piholeuser) actual_rc = host.run(check_pihole).rc - check_pihole = test_cmd.format('x', directory, piholeuser) + check_pihole = test_cmd.format("x", directory, piholeuser) actual_rc = host.run(check_pihole).rc findfiles = 'find "{}" -maxdepth 1 -type f -exec echo {{}} \\;;' filelist = host.run(findfiles.format(directory)) files = list(filter(bool, filelist.stdout.splitlines())) for file in files: - check_pihole = test_cmd.format('r', file, piholeuser) + check_pihole = test_cmd.format("r", file, piholeuser) actual_rc = host.run(check_pihole).rc @pytest.mark.parametrize("test_webpage", [True]) def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): - ''' + """ confirms all web page assets from Core repo are readable by $LIGHTTPD_USER on a fresh build - ''' + """ piholeWebpage = [ "127.0.0.1", # "pi.hole" ] # dialog returns Cancel for user prompt - mock_command('dialog', {'*': ('', '0')}, host) + mock_command("dialog", {"*": ("", "0")}, host) # mock git pull - mock_command_passthrough('git', {'pull': ('', '0')}, host) + mock_command_passthrough("git", {"pull": ("", "0")}, host) # mock systemctl to start lighttpd and FTL - ligthttpdcommand = dedent(r'''\"\" + ligthttpdcommand = dedent( + r'''\"\" echo 'starting lighttpd with {}' if [ command -v "apt-get" >/dev/null 2>&1 ]; then LIGHTTPD_USER="www-data" @@ -418,63 +392,45 @@ def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): /usr/sbin/lighttpd -tt -f '{config}' /usr/sbin/lighttpd -f '{config}' echo \"\"'''.format( - '{}', - usergroup='${{LIGHTTPD_USER}}:${{LIGHTTPD_GROUP}}', - chmodarg='{{}}', - config='/etc/lighttpd/lighttpd.conf', - run='/var/run/lighttpd', - cache='/var/cache/lighttpd', - uploads='/var/cache/lighttpd/uploads', - compress='/var/cache/lighttpd/compress' + "{}", + usergroup="${{LIGHTTPD_USER}}:${{LIGHTTPD_GROUP}}", + chmodarg="{{}}", + config="/etc/lighttpd/lighttpd.conf", + run="/var/run/lighttpd", + cache="/var/cache/lighttpd", + uploads="/var/cache/lighttpd/uploads", + compress="/var/cache/lighttpd/compress", ) ) - FTLcommand = dedent('''\"\" + FTLcommand = dedent( + '''\"\" set -x /etc/init.d/pihole-FTL restart - echo \"\"''') + echo \"\"''' + ) mock_command_run( - 'systemctl', + "systemctl", { - 'enable lighttpd': ( - '', - '0' - ), - 'restart lighttpd': ( - ligthttpdcommand.format('restart'), - '0' - ), - 'start lighttpd': ( - ligthttpdcommand.format('start'), - '0' - ), - 'enable pihole-FTL': ( - '', - '0' - ), - 'restart pihole-FTL': ( - FTLcommand, - '0' - ), - 'start pihole-FTL': ( - FTLcommand, - '0' - ), - '*': ( - 'echo "systemctl call with $@"', - '0' - ), + "enable lighttpd": ("", "0"), + "restart lighttpd": (ligthttpdcommand.format("restart"), "0"), + "start lighttpd": (ligthttpdcommand.format("start"), "0"), + "enable pihole-FTL": ("", "0"), + "restart pihole-FTL": (FTLcommand, "0"), + "start pihole-FTL": (FTLcommand, "0"), + "*": ('echo "systemctl call with $@"', "0"), }, - host + host, ) # create configuration file - setup_var_file = 'cat < /etc/pihole/setupVars.conf\n' + setup_var_file = "cat < /etc/pihole/setupVars.conf\n" for k, v in SETUPVARS.items(): setup_var_file += "{}={}\n".format(k, v) setup_var_file += "INSTALL_WEB_SERVER=true\n" setup_var_file += "INSTALL_WEB_INTERFACE=true\n" setup_var_file += "EOF\n" host.run(setup_var_file) - installWeb = host.run(''' + installWeb = host.run( + """ export TERM=xterm export DEBIAN_FRONTEND=noninteractive umask 0027 @@ -488,33 +444,32 @@ def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): echo "webroot=${webroot}" echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" - ''') + """ + ) assert 0 == installWeb.rc - piholeuser = 'pihole' - webuser = '' - user = re.findall( - r"^\s*LIGHTTPD_USER=.*$", installWeb.stdout, re.MULTILINE) + piholeuser = "pihole" + webuser = "" + user = re.findall(r"^\s*LIGHTTPD_USER=.*$", installWeb.stdout, re.MULTILINE) for match in user: - webuser = match.replace('LIGHTTPD_USER=', '').strip() - webroot = '' - user = re.findall( - r"^\s*webroot=.*$", installWeb.stdout, re.MULTILINE) + webuser = match.replace("LIGHTTPD_USER=", "").strip() + webroot = "" + user = re.findall(r"^\s*webroot=.*$", installWeb.stdout, re.MULTILINE) for match in user: - webroot = match.replace('webroot=', '').strip() + webroot = match.replace("webroot=", "").strip() if not webroot.strip(): - webroot = '/var/www/html' + webroot = "/var/www/html" installWebInterface = True interface = re.findall( - r"^\s*INSTALL_WEB_INTERFACE=.*$", installWeb.stdout, re.MULTILINE) + r"^\s*INSTALL_WEB_INTERFACE=.*$", installWeb.stdout, re.MULTILINE + ) for match in interface: - testvalue = match.replace('INSTALL_WEB_INTERFACE=', '').strip().lower() + testvalue = match.replace("INSTALL_WEB_INTERFACE=", "").strip().lower() if not testvalue.strip(): installWebInterface = testvalue == "true" installWebServer = True - server = re.findall( - r"^\s*INSTALL_WEB_SERVER=.*$", installWeb.stdout, re.MULTILINE) + server = re.findall(r"^\s*INSTALL_WEB_SERVER=.*$", installWeb.stdout, re.MULTILINE) for match in server: - testvalue = match.replace('INSTALL_WEB_SERVER=', '').strip().lower() + testvalue = match.replace("INSTALL_WEB_SERVER=", "").strip().lower() if not testvalue.strip(): installWebServer = testvalue == "true" # if webserver install was not requested @@ -525,87 +480,88 @@ def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): test_cmd = 'su --shell /bin/bash --command "test -{0} {1}" -p {2}' # check files that need a running FTL to be created # readable and writeable pihole-FTL.db - check_FTLconf = test_cmd.format( - 'r', '/etc/pihole/pihole-FTL.db', piholeuser) + check_FTLconf = test_cmd.format("r", "/etc/pihole/pihole-FTL.db", piholeuser) actual_rc = host.run(check_FTLconf).rc assert exit_status_success == actual_rc - check_FTLconf = test_cmd.format( - 'w', '/etc/pihole/pihole-FTL.db', piholeuser) + check_FTLconf = test_cmd.format("w", "/etc/pihole/pihole-FTL.db", piholeuser) actual_rc = host.run(check_FTLconf).rc assert exit_status_success == actual_rc # check directories above $webroot for read and execute permission - check_var = test_cmd.format('r', '/var', webuser) + check_var = test_cmd.format("r", "/var", webuser) actual_rc = host.run(check_var).rc assert exit_status_success == actual_rc - check_var = test_cmd.format('x', '/var', webuser) + check_var = test_cmd.format("x", "/var", webuser) actual_rc = host.run(check_var).rc assert exit_status_success == actual_rc - check_www = test_cmd.format('r', '/var/www', webuser) + check_www = test_cmd.format("r", "/var/www", webuser) actual_rc = host.run(check_www).rc assert exit_status_success == actual_rc - check_www = test_cmd.format('x', '/var/www', webuser) + check_www = test_cmd.format("x", "/var/www", webuser) actual_rc = host.run(check_www).rc assert exit_status_success == actual_rc - check_html = test_cmd.format('r', '/var/www/html', webuser) + check_html = test_cmd.format("r", "/var/www/html", webuser) actual_rc = host.run(check_html).rc assert exit_status_success == actual_rc - check_html = test_cmd.format('x', '/var/www/html', webuser) + check_html = test_cmd.format("x", "/var/www/html", webuser) actual_rc = host.run(check_html).rc assert exit_status_success == actual_rc # check directories below $webroot for read and execute permission - check_admin = test_cmd.format('r', webroot + '/admin', webuser) + check_admin = test_cmd.format("r", webroot + "/admin", webuser) actual_rc = host.run(check_admin).rc assert exit_status_success == actual_rc - check_admin = test_cmd.format('x', webroot + '/admin', webuser) + check_admin = test_cmd.format("x", webroot + "/admin", webuser) actual_rc = host.run(check_admin).rc assert exit_status_success == actual_rc - directories = get_directories_recursive(host, webroot + '/admin/*/') + directories = get_directories_recursive(host, webroot + "/admin/*/") for directory in directories: - check_pihole = test_cmd.format('r', directory, webuser) + check_pihole = test_cmd.format("r", directory, webuser) actual_rc = host.run(check_pihole).rc - check_pihole = test_cmd.format('x', directory, webuser) + check_pihole = test_cmd.format("x", directory, webuser) actual_rc = host.run(check_pihole).rc findfiles = 'find "{}" -maxdepth 1 -type f -exec echo {{}} \\;;' filelist = host.run(findfiles.format(directory)) files = list(filter(bool, filelist.stdout.splitlines())) for file in files: - check_pihole = test_cmd.format('r', file, webuser) + check_pihole = test_cmd.format("r", file, webuser) actual_rc = host.run(check_pihole).rc # check web interface files # change nameserver to pi-hole # setting nameserver in /etc/resolv.conf to pi-hole does # not work here because of the way docker uses this file - ns = host.run( - r"sed -i 's/nameserver.*/nameserver 127.0.0.1/' /etc/resolv.conf") + ns = host.run(r"sed -i 's/nameserver.*/nameserver 127.0.0.1/' /etc/resolv.conf") pihole_is_ns = ns.rc == 0 def is_ip(address): m = re.match(r"(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})", address) return bool(m) + if installWebInterface is True: - check_pihole = test_cmd.format('r', webroot + '/pihole', webuser) + check_pihole = test_cmd.format("r", webroot + "/pihole", webuser) actual_rc = host.run(check_pihole).rc assert exit_status_success == actual_rc - check_pihole = test_cmd.format('x', webroot + '/pihole', webuser) + check_pihole = test_cmd.format("x", webroot + "/pihole", webuser) actual_rc = host.run(check_pihole).rc assert exit_status_success == actual_rc # check most important files in $webroot for read permission - check_index = test_cmd.format( - 'r', webroot + '/pihole/index.php', webuser) + check_index = test_cmd.format("r", webroot + "/pihole/index.php", webuser) actual_rc = host.run(check_index).rc assert exit_status_success == actual_rc if test_webpage is True: # check webpage for unreadable files noPHPfopen = re.compile( - (r"PHP Error(%d+):\s+fopen([^)]+):\s+" + - r"failed to open stream: " + - r"Permission denied in"), - re.I) + ( + r"PHP Error(%d+):\s+fopen([^)]+):\s+" + + r"failed to open stream: " + + r"Permission denied in" + ), + re.I, + ) # using cURL option --dns-servers is not possible status = ( - 'curl -s --head "{}" | ' + - 'head -n 1 | ' + - 'grep "HTTP/1.[01] [23].." > /dev/null') + 'curl -s --head "{}" | ' + + "head -n 1 | " + + 'grep "HTTP/1.[01] [23].." > /dev/null' + ) digcommand = r"dig A +short {} @127.0.0.1 | head -n 1" pagecontent = 'curl --verbose -L "{}"' for page in piholeWebpage: @@ -625,258 +581,285 @@ def test_installPihole_fresh_install_readableBlockpage(host, test_webpage): def test_update_package_cache_success_no_errors(host): - ''' + """ confirms package cache was updated without any errors - ''' - updateCache = host.run(''' + """ + updateCache = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect update_package_cache - ''') - expected_stdout = tick_box + ' Update local cache of available packages' + """ + ) + expected_stdout = tick_box + " Update local cache of available packages" assert expected_stdout in updateCache.stdout - assert 'error' not in updateCache.stdout.lower() + assert "error" not in updateCache.stdout.lower() def test_update_package_cache_failure_no_errors(host): - ''' + """ confirms package cache was not updated - ''' - mock_command('apt-get', {'update': ('', '1')}, host) - updateCache = host.run(''' + """ + mock_command("apt-get", {"update": ("", "1")}, host) + updateCache = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect update_package_cache - ''') - expected_stdout = cross_box + ' Update local cache of available packages' + """ + ) + expected_stdout = cross_box + " Update local cache of available packages" assert expected_stdout in updateCache.stdout - assert 'Error: Unable to update package cache.' in updateCache.stdout + assert "Error: Unable to update package cache." in updateCache.stdout def test_FTL_detect_aarch64_no_errors(host): - ''' + """ confirms only aarch64 package is downloaded for FTL engine - ''' + """ # mock uname to return aarch64 platform - mock_command('uname', {'-m': ('aarch64', '0')}, host) + mock_command("uname", {"-m": ("aarch64", "0")}, host) # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) + mock_command("which", {"sh": ("/bin/sh", "0")}, host) # mock ldd to respond with aarch64 shared library - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux-aarch64.so.1', '0')}, host) - detectPlatform = host.run(''' + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux-aarch64.so.1", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Detected AArch64 (64 Bit ARM) processor' + expected_stdout = tick_box + " Detected AArch64 (64 Bit ARM) processor" assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv4t_no_errors(host): - ''' + """ confirms only armv4t package is downloaded for FTL engine - ''' + """ # mock uname to return armv4t platform - mock_command('uname', {'-m': ('armv4t', '0')}, host) + mock_command("uname", {"-m": ("armv4t", "0")}, host) # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) + mock_command("which", {"sh": ("/bin/sh", "0")}, host) # mock ldd to respond with armv4t shared library - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux.so.3', '0')}, host) - detectPlatform = host.run(''' + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux.so.3", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + (' Detected ARMv4 processor') + expected_stdout = tick_box + (" Detected ARMv4 processor") assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv5te_no_errors(host): - ''' + """ confirms only armv5te package is downloaded for FTL engine - ''' + """ # mock uname to return armv5te platform - mock_command('uname', {'-m': ('armv5te', '0')}, host) + mock_command("uname", {"-m": ("armv5te", "0")}, host) # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) + mock_command("which", {"sh": ("/bin/sh", "0")}, host) # mock ldd to respond with ld-linux shared library - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux.so.3', '0')}, host) - detectPlatform = host.run(''' + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux.so.3", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + (' Detected ARMv5 (or newer) processor') + expected_stdout = tick_box + (" Detected ARMv5 (or newer) processor") assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv6l_no_errors(host): - ''' + """ confirms only armv6l package is downloaded for FTL engine - ''' + """ # mock uname to return armv6l platform - mock_command('uname', {'-m': ('armv6l', '0')}, host) + mock_command("uname", {"-m": ("armv6l", "0")}, host) # mock ldd to respond with ld-linux-armhf shared library # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux-armhf.so.3', '0')}, host) - detectPlatform = host.run(''' + mock_command("which", {"sh": ("/bin/sh", "0")}, host) + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux-armhf.so.3", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + (' Detected ARMv6 processor ' - '(with hard-float support)') + expected_stdout = tick_box + ( + " Detected ARMv6 processor " "(with hard-float support)" + ) assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv7l_no_errors(host): - ''' + """ confirms only armv7l package is downloaded for FTL engine - ''' + """ # mock uname to return armv7l platform - mock_command('uname', {'-m': ('armv7l', '0')}, host) + mock_command("uname", {"-m": ("armv7l", "0")}, host) # mock ldd to respond with ld-linux-armhf shared library # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux-armhf.so.3', '0')}, host) - detectPlatform = host.run(''' + mock_command("which", {"sh": ("/bin/sh", "0")}, host) + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux-armhf.so.3", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + (' Detected ARMv7 processor ' - '(with hard-float support)') + expected_stdout = tick_box + ( + " Detected ARMv7 processor " "(with hard-float support)" + ) assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv8a_no_errors(host): - ''' + """ confirms only armv8a package is downloaded for FTL engine - ''' + """ # mock uname to return armv8a platform - mock_command('uname', {'-m': ('armv8a', '0')}, host) + mock_command("uname", {"-m": ("armv8a", "0")}, host) # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) + mock_command("which", {"sh": ("/bin/sh", "0")}, host) # mock ldd to respond with ld-linux-armhf shared library - mock_command('ldd', {'/bin/sh': ('/lib/ld-linux-armhf.so.3', '0')}, host) - detectPlatform = host.run(''' + mock_command("ldd", {"/bin/sh": ("/lib/ld-linux-armhf.so.3", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Detected ARMv8 (or newer) processor' + expected_stdout = tick_box + " Detected ARMv8 (or newer) processor" assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_x86_64_no_errors(host): - ''' + """ confirms only x86_64 package is downloaded for FTL engine - ''' + """ # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) - detectPlatform = host.run(''' + mock_command("which", {"sh": ("/bin/sh", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = info_box + ' FTL Checks...' + """ + ) + expected_stdout = info_box + " FTL Checks..." assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Detected x86_64 processor' + expected_stdout = tick_box + " Detected x86_64 processor" assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Downloading and Installing FTL' + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in detectPlatform.stdout def test_FTL_detect_unknown_no_errors(host): - ''' confirms only generic package is downloaded for FTL engine ''' + """confirms only generic package is downloaded for FTL engine""" # mock uname to return generic platform - mock_command('uname', {'-m': ('mips', '0')}, host) + mock_command("uname", {"-m": ("mips", "0")}, host) # mock `which sh` to return `/bin/sh` - mock_command('which', {'sh': ('/bin/sh', '0')}, host) - detectPlatform = host.run(''' + mock_command("which", {"sh": ("/bin/sh", "0")}, host) + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - expected_stdout = 'Not able to detect processor (unknown: mips)' + """ + ) + expected_stdout = "Not able to detect processor (unknown: mips)" assert expected_stdout in detectPlatform.stdout def test_FTL_download_aarch64_no_errors(host): - ''' + """ confirms only aarch64 package is downloaded for FTL engine - ''' + """ # mock dialog answers and ensure installer dependencies - mock_command('dialog', {'*': ('', '0')}, host) - host.run(''' + mock_command("dialog", {"*": ("", "0")}, host) + host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${INSTALLER_DEPS[@]} - ''') - download_binary = host.run(''' + """ + ) + download_binary = host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user FTLinstall "pihole-FTL-aarch64-linux-gnu" - ''') - expected_stdout = tick_box + ' Downloading and Installing FTL' + """ + ) + expected_stdout = tick_box + " Downloading and Installing FTL" assert expected_stdout in download_binary.stdout - assert 'error' not in download_binary.stdout.lower() + assert "error" not in download_binary.stdout.lower() def test_FTL_binary_installed_and_responsive_no_errors(host): - ''' + """ confirms FTL binary is copied and functional in installed location - ''' - installed_binary = host.run(''' + """ + host.run( + """ source /opt/pihole/basic-install.sh create_pihole_user funcOutput=$(get_binary_name) @@ -884,176 +867,189 @@ def test_FTL_binary_installed_and_responsive_no_errors(host): binary="pihole-FTL${funcOutput##*pihole-FTL}" theRest="${funcOutput%pihole-FTL*}" FTLdetect "${binary}" "${theRest}" - ''') - version_check = host.run(''' + """ + ) + version_check = host.run( + """ VERSION=$(pihole-FTL version) echo ${VERSION:0:1} - ''') - expected_stdout = 'v' + """ + ) + expected_stdout = "v" assert expected_stdout in version_check.stdout def test_IPv6_only_link_local(host): - ''' + """ confirms IPv6 blocking is disabled for Link-local address - ''' + """ # mock ip -6 address to return Link-local address mock_command_2( - 'ip', - { - '-6 address': ( - 'inet6 fe80::d210:52fa:fe00:7ad7/64 scope link', - '0' - ) - }, - host + "ip", + {"-6 address": ("inet6 fe80::d210:52fa:fe00:7ad7/64 scope link", "0")}, + host, ) - detectPlatform = host.run(''' + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh find_IPv6_information - ''') - expected_stdout = ('Unable to find IPv6 ULA/GUA address') + """ + ) + expected_stdout = "Unable to find IPv6 ULA/GUA address" assert expected_stdout in detectPlatform.stdout def test_IPv6_only_ULA(host): - ''' + """ confirms IPv6 blocking is enabled for ULA addresses - ''' + """ # mock ip -6 address to return ULA address mock_command_2( - 'ip', + "ip", { - '-6 address': ( - 'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', - '0' + "-6 address": ( + "inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global", + "0", ) }, - host + host, ) - detectPlatform = host.run(''' + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh find_IPv6_information - ''') - expected_stdout = 'Found IPv6 ULA address' + """ + ) + expected_stdout = "Found IPv6 ULA address" assert expected_stdout in detectPlatform.stdout def test_IPv6_only_GUA(host): - ''' + """ confirms IPv6 blocking is enabled for GUA addresses - ''' + """ # mock ip -6 address to return GUA address mock_command_2( - 'ip', + "ip", { - '-6 address': ( - 'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', - '0' + "-6 address": ( + "inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global", + "0", ) }, - host + host, ) - detectPlatform = host.run(''' + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh find_IPv6_information - ''') - expected_stdout = 'Found IPv6 GUA address' + """ + ) + expected_stdout = "Found IPv6 GUA address" assert expected_stdout in detectPlatform.stdout def test_IPv6_GUA_ULA_test(host): - ''' + """ confirms IPv6 blocking is enabled for GUA and ULA addresses - ''' + """ # mock ip -6 address to return GUA and ULA addresses mock_command_2( - 'ip', + "ip", { - '-6 address': ( - 'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\n' - 'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', - '0' + "-6 address": ( + "inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\n" + "inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global", + "0", ) }, - host + host, ) - detectPlatform = host.run(''' + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh find_IPv6_information - ''') - expected_stdout = 'Found IPv6 ULA address' + """ + ) + expected_stdout = "Found IPv6 ULA address" assert expected_stdout in detectPlatform.stdout def test_IPv6_ULA_GUA_test(host): - ''' + """ confirms IPv6 blocking is enabled for GUA and ULA addresses - ''' + """ # mock ip -6 address to return ULA and GUA addresses mock_command_2( - 'ip', + "ip", { - '-6 address': ( - 'inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\n' - 'inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', - '0' + "-6 address": ( + "inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\n" + "inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global", + "0", ) }, - host + host, ) - detectPlatform = host.run(''' + detectPlatform = host.run( + """ source /opt/pihole/basic-install.sh find_IPv6_information - ''') - expected_stdout = 'Found IPv6 ULA address' + """ + ) + expected_stdout = "Found IPv6 ULA address" assert expected_stdout in detectPlatform.stdout def test_validate_ip(host): - ''' + """ Tests valid_ip for various IP addresses - ''' + """ def test_address(addr, success=True): - output = host.run(''' + output = host.run( + """ source /opt/pihole/basic-install.sh valid_ip "{addr}" - '''.format(addr=addr)) + """.format( + addr=addr + ) + ) assert output.rc == 0 if success else 1 - test_address('192.168.1.1') - test_address('127.0.0.1') - test_address('255.255.255.255') - test_address('255.255.255.256', False) - test_address('255.255.256.255', False) - test_address('255.256.255.255', False) - test_address('256.255.255.255', False) - test_address('1092.168.1.1', False) - test_address('not an IP', False) - test_address('8.8.8.8#', False) - test_address('8.8.8.8#0') - test_address('8.8.8.8#1') - test_address('8.8.8.8#42') - test_address('8.8.8.8#888') - test_address('8.8.8.8#1337') - test_address('8.8.8.8#65535') - test_address('8.8.8.8#65536', False) - test_address('8.8.8.8#-1', False) - test_address('00.0.0.0', False) - test_address('010.0.0.0', False) - test_address('001.0.0.0', False) - test_address('0.0.0.0#00', False) - test_address('0.0.0.0#01', False) - test_address('0.0.0.0#001', False) - test_address('0.0.0.0#0001', False) - test_address('0.0.0.0#00001', False) + test_address("192.168.1.1") + test_address("127.0.0.1") + test_address("255.255.255.255") + test_address("255.255.255.256", False) + test_address("255.255.256.255", False) + test_address("255.256.255.255", False) + test_address("256.255.255.255", False) + test_address("1092.168.1.1", False) + test_address("not an IP", False) + test_address("8.8.8.8#", False) + test_address("8.8.8.8#0") + test_address("8.8.8.8#1") + test_address("8.8.8.8#42") + test_address("8.8.8.8#888") + test_address("8.8.8.8#1337") + test_address("8.8.8.8#65535") + test_address("8.8.8.8#65536", False) + test_address("8.8.8.8#-1", False) + test_address("00.0.0.0", False) + test_address("010.0.0.0", False) + test_address("001.0.0.0", False) + test_address("0.0.0.0#00", False) + test_address("0.0.0.0#01", False) + test_address("0.0.0.0#001", False) + test_address("0.0.0.0#0001", False) + test_address("0.0.0.0#00001", False) def test_os_check_fails(host): - ''' Confirms install fails on unsupported OS ''' - host.run(''' + """Confirms install fails on unsupported OS""" + host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${OS_CHECK_DEPS[@]} @@ -1062,65 +1058,78 @@ def test_os_check_fails(host): ID=UnsupportedOS VERSION_ID="2" EOT - ''') - detectOS = host.run('''t + """ + ) + detectOS = host.run( + """t source /opt/pihole/basic-install.sh os_check - ''') - expected_stdout = 'Unsupported OS detected: UnsupportedOS' + """ + ) + expected_stdout = "Unsupported OS detected: UnsupportedOS" assert expected_stdout in detectOS.stdout def test_os_check_passes(host): - ''' Confirms OS meets the requirements ''' - host.run(''' + """Confirms OS meets the requirements""" + host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${OS_CHECK_DEPS[@]} install_dependent_packages ${INSTALLER_DEPS[@]} - ''') - detectOS = host.run(''' + """ + ) + detectOS = host.run( + """ source /opt/pihole/basic-install.sh os_check - ''') - expected_stdout = 'Supported OS detected' + """ + ) + expected_stdout = "Supported OS detected" assert expected_stdout in detectOS.stdout def test_package_manager_has_installer_deps(host): - ''' Confirms OS is able to install the required packages for the installer''' - mock_command('dialog', {'*': ('', '0')}, host) - output = host.run(''' + """Confirms OS is able to install the required packages for the installer""" + mock_command("dialog", {"*": ("", "0")}, host) + output = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${INSTALLER_DEPS[@]} - ''') + """ + ) - assert 'No package' not in output.stdout + assert "No package" not in output.stdout assert output.rc == 0 def test_package_manager_has_pihole_deps(host): - ''' Confirms OS is able to install the required packages for Pi-hole ''' - mock_command('dialog', {'*': ('', '0')}, host) - output = host.run(''' + """Confirms OS is able to install the required packages for Pi-hole""" + mock_command("dialog", {"*": ("", "0")}, host) + output = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${PIHOLE_DEPS[@]} - ''') + """ + ) - assert 'No package' not in output.stdout + assert "No package" not in output.stdout assert output.rc == 0 def test_package_manager_has_web_deps(host): - ''' Confirms OS is able to install the required packages for web ''' - mock_command('dialog', {'*': ('', '0')}, host) - output = host.run(''' + """Confirms OS is able to install the required packages for web""" + mock_command("dialog", {"*": ("", "0")}, host) + output = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect install_dependent_packages ${PIHOLE_WEB_DEPS[@]} - ''') + """ + ) - assert 'No package' not in output.stdout + assert "No package" not in output.stdout assert output.rc == 0 diff --git a/test/test_any_utils.py b/test/test_any_utils.py index 5126f263..5b4075d9 100644 --- a/test/test_any_utils.py +++ b/test/test_any_utils.py @@ -1,22 +1,27 @@ def test_key_val_replacement_works(host): - ''' Confirms addOrEditKeyValPair either adds or replaces a key value pair in a given file ''' - host.run(''' + """Confirms addOrEditKeyValPair either adds or replaces a key value pair in a given file""" + host.run( + """ source /opt/pihole/utils.sh addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1" addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2" addOrEditKeyValPair "./testoutput" "KEY_ONE" "value3" addOrEditKeyValPair "./testoutput" "KEY_FOUR" "value4" - ''') - output = host.run(''' + """ + ) + output = host.run( + """ cat ./testoutput - ''') - expected_stdout = 'KEY_ONE=value3\nKEY_TWO=value2\nKEY_FOUR=value4\n' + """ + ) + expected_stdout = "KEY_ONE=value3\nKEY_TWO=value2\nKEY_FOUR=value4\n" assert expected_stdout == output.stdout def test_key_addition_works(host): - ''' Confirms addKey adds a key (no value) to a file without duplicating it ''' - host.run(''' + """Confirms addKey adds a key (no value) to a file without duplicating it""" + host.run( + """ source /opt/pihole/utils.sh addKey "./testoutput" "KEY_ONE" addKey "./testoutput" "KEY_ONE" @@ -24,17 +29,21 @@ def test_key_addition_works(host): addKey "./testoutput" "KEY_TWO" addKey "./testoutput" "KEY_THREE" addKey "./testoutput" "KEY_THREE" - ''') - output = host.run(''' + """ + ) + output = host.run( + """ cat ./testoutput - ''') - expected_stdout = 'KEY_ONE\nKEY_TWO\nKEY_THREE\n' + """ + ) + expected_stdout = "KEY_ONE\nKEY_TWO\nKEY_THREE\n" assert expected_stdout == output.stdout def test_key_removal_works(host): - ''' Confirms removeKey removes a key or key/value pair ''' - host.run(''' + """Confirms removeKey removes a key or key/value pair""" + host.run( + """ source /opt/pihole/utils.sh addOrEditKeyValPair "./testoutput" "KEY_ONE" "value1" addOrEditKeyValPair "./testoutput" "KEY_TWO" "value2" @@ -42,81 +51,102 @@ def test_key_removal_works(host): addKey "./testoutput" "KEY_FOUR" removeKey "./testoutput" "KEY_TWO" removeKey "./testoutput" "KEY_FOUR" - ''') - output = host.run(''' + """ + ) + output = host.run( + """ cat ./testoutput - ''') - expected_stdout = 'KEY_ONE=value1\nKEY_THREE=value3\n' + """ + ) + expected_stdout = "KEY_ONE=value1\nKEY_THREE=value3\n" assert expected_stdout == output.stdout -def test_getFTLAPIPortFile_default(host): - ''' Confirms getFTLAPIPortFile returns the default API port file path ''' - output = host.run(''' +def test_getFTLAPIPort_default(host): + """Confirms getFTLAPIPort returns the default API port""" + output = host.run( + """ source /opt/pihole/utils.sh - getFTLAPIPortFile - ''') - expected_stdout = '/run/pihole-FTL.port\n' + getFTLAPIPort + """ + ) + expected_stdout = "4711\n" assert expected_stdout == output.stdout -def test_getFTLAPIPort_default(host): - ''' Confirms getFTLAPIPort returns the default API port ''' - output = host.run(''' +def test_getFTLAPIPort_custom(host): + """Confirms getFTLAPIPort returns a custom API port""" + host.run( + """ + echo "FTLPORT=1234" > /etc/pihole/pihole-FTL.conf + """ + ) + output = host.run( + """ source /opt/pihole/utils.sh - getFTLAPIPort "/run/pihole-FTL.port" - ''') - expected_stdout = '4711\n' + getFTLAPIPort + """ + ) + expected_stdout = "1234\n" assert expected_stdout == output.stdout -def test_getFTLAPIPortFile_and_getFTLAPIPort_custom(host): - ''' Confirms getFTLAPIPort returns a custom API port in a custom PORTFILE location ''' - host.run(''' - tmpfile=$(mktemp) - echo "PORTFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf - echo "1234" > ${tmpfile} - ''') - output = host.run(''' +def test_getFTLAPIPort_malicious(host): + """Confirms getFTLAPIPort returns 4711 if the setting in pihole-FTL.conf contains non-digits""" + host.run( + """ + echo "FTLPORT=*$ssdfsd" > /etc/pihole/pihole-FTL.conf + """ + ) + output = host.run( + """ source /opt/pihole/utils.sh - FTL_API_PORT_FILE=$(getFTLAPIPortFile) - getFTLAPIPort "${FTL_API_PORT_FILE}" - ''') - expected_stdout = '1234\n' + getFTLAPIPort + """ + ) + expected_stdout = "4711\n" assert expected_stdout == output.stdout def test_getFTLPIDFile_default(host): - ''' Confirms getFTLPIDFile returns the default PID file path ''' - output = host.run(''' + """Confirms getFTLPIDFile returns the default PID file path""" + output = host.run( + """ source /opt/pihole/utils.sh getFTLPIDFile - ''') - expected_stdout = '/run/pihole-FTL.pid\n' + """ + ) + expected_stdout = "/run/pihole-FTL.pid\n" assert expected_stdout == output.stdout def test_getFTLPID_default(host): - ''' Confirms getFTLPID returns the default value if FTL is not running ''' - output = host.run(''' + """Confirms getFTLPID returns the default value if FTL is not running""" + output = host.run( + """ source /opt/pihole/utils.sh getFTLPID - ''') - expected_stdout = '-1\n' + """ + ) + expected_stdout = "-1\n" assert expected_stdout == output.stdout def test_getFTLPIDFile_and_getFTLPID_custom(host): - ''' Confirms getFTLPIDFile returns a custom PID file path ''' - host.run(''' + """Confirms getFTLPIDFile returns a custom PID file path""" + host.run( + """ tmpfile=$(mktemp) echo "PIDFILE=${tmpfile}" > /etc/pihole/pihole-FTL.conf echo "1234" > ${tmpfile} - ''') - output = host.run(''' + """ + ) + output = host.run( + """ source /opt/pihole/utils.sh FTL_PID_FILE=$(getFTLPIDFile) getFTLPID "${FTL_PID_FILE}" - ''') - expected_stdout = '1234\n' + """ + ) + expected_stdout = "1234\n" assert expected_stdout == output.stdout diff --git a/test/test_centos_common_support.py b/test/test_centos_common_support.py index 3497267a..871fee29 100644 --- a/test/test_centos_common_support.py +++ b/test/test_centos_common_support.py @@ -8,17 +8,20 @@ from .conftest import ( def test_enable_epel_repository_centos(host): - ''' + """ confirms the EPEL package repository is enabled when installed on CentOS - ''' - package_manager_detect = host.run(''' + """ + package_manager_detect = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect - ''') - expected_stdout = info_box + (' Enabling EPEL package repository ' - '(https://fedoraproject.org/wiki/EPEL)') + """ + ) + expected_stdout = info_box + ( + " Enabling EPEL package repository " "(https://fedoraproject.org/wiki/EPEL)" + ) assert expected_stdout in package_manager_detect.stdout - expected_stdout = tick_box + ' Installed' + expected_stdout = tick_box + " Installed" assert expected_stdout in package_manager_detect.stdout - epel_package = host.package('epel-release') + epel_package = host.package("epel-release") assert epel_package.is_installed diff --git a/test/test_centos_fedora_common_support.py b/test/test_centos_fedora_common_support.py index df806771..7e0bae4e 100644 --- a/test/test_centos_fedora_common_support.py +++ b/test/test_centos_fedora_common_support.py @@ -6,60 +6,70 @@ from .conftest import ( def mock_selinux_config(state, host): - ''' + """ Creates a mock SELinux config file with expected content - ''' + """ # validate state string - valid_states = ['enforcing', 'permissive', 'disabled'] + valid_states = ["enforcing", "permissive", "disabled"] assert state in valid_states # getenforce returns the running state of SELinux - mock_command('getenforce', {'*': (state.capitalize(), '0')}, host) + mock_command("getenforce", {"*": (state.capitalize(), "0")}, host) # create mock configuration with desired content - host.run(''' + host.run( + """ mkdir /etc/selinux echo "SELINUX={state}" > /etc/selinux/config - '''.format(state=state.lower())) + """.format( + state=state.lower() + ) + ) def test_selinux_enforcing_exit(host): - ''' + """ confirms installer prompts to exit when SELinux is Enforcing by default - ''' + """ mock_selinux_config("enforcing", host) - check_selinux = host.run(''' + check_selinux = host.run( + """ source /opt/pihole/basic-install.sh checkSelinux - ''') - expected_stdout = cross_box + ' Current SELinux: enforcing' + """ + ) + expected_stdout = cross_box + " Current SELinux: enforcing" assert expected_stdout in check_selinux.stdout - expected_stdout = 'SELinux Enforcing detected, exiting installer' + expected_stdout = "SELinux Enforcing detected, exiting installer" assert expected_stdout in check_selinux.stdout assert check_selinux.rc == 1 def test_selinux_permissive(host): - ''' + """ confirms installer continues when SELinux is Permissive - ''' + """ mock_selinux_config("permissive", host) - check_selinux = host.run(''' + check_selinux = host.run( + """ source /opt/pihole/basic-install.sh checkSelinux - ''') - expected_stdout = tick_box + ' Current SELinux: permissive' + """ + ) + expected_stdout = tick_box + " Current SELinux: permissive" assert expected_stdout in check_selinux.stdout assert check_selinux.rc == 0 def test_selinux_disabled(host): - ''' + """ confirms installer continues when SELinux is Disabled - ''' + """ mock_selinux_config("disabled", host) - check_selinux = host.run(''' + check_selinux = host.run( + """ source /opt/pihole/basic-install.sh checkSelinux - ''') - expected_stdout = tick_box + ' Current SELinux: disabled' + """ + ) + expected_stdout = tick_box + " Current SELinux: disabled" assert expected_stdout in check_selinux.stdout assert check_selinux.rc == 0 diff --git a/test/test_fedora_support.py b/test/test_fedora_support.py index 57a6c5c4..e7d31a5d 100644 --- a/test/test_fedora_support.py +++ b/test/test_fedora_support.py @@ -1,13 +1,15 @@ def test_epel_and_remi_not_installed_fedora(host): - ''' + """ confirms installer does not attempt to install EPEL/REMI repositories on Fedora - ''' - package_manager_detect = host.run(''' + """ + package_manager_detect = host.run( + """ source /opt/pihole/basic-install.sh package_manager_detect - ''') - assert package_manager_detect.stdout == '' + """ + ) + assert package_manager_detect.stdout == "" - epel_package = host.package('epel-release') + epel_package = host.package("epel-release") assert not epel_package.is_installed diff --git a/test/tox.centos_8.ini b/test/tox.centos_8.ini index 71b147c7..dc160d2a 100644 --- a/test/tox.centos_8.ini +++ b/test/tox.centos_8.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt commands = docker build -f _centos_8.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_centos_common_support.py diff --git a/test/tox.debian_10.ini b/test/tox.debian_10.ini index d21620dc..ef9fa7a0 100644 --- a/test/tox.debian_10.ini +++ b/test/tox.debian_10.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt commands = docker build -f _debian_10.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py diff --git a/test/tox.debian_11.ini b/test/tox.debian_11.ini index f2546922..6d25a0c8 100644 --- a/test/tox.debian_11.ini +++ b/test/tox.debian_11.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py diff --git a/test/tox.fedora_34.ini b/test/tox.fedora_35.ini similarity index 71% rename from test/tox.fedora_34.ini rename to test/tox.fedora_35.ini index f4115a66..5e90426d 100644 --- a/test/tox.fedora_34.ini +++ b/test/tox.fedora_35.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt -commands = docker build -f _fedora_34.Dockerfile -t pytest_pihole:test_container ../ +commands = docker build -f _fedora_35.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_fedora_support.py diff --git a/test/tox.fedora_36.ini b/test/tox.fedora_36.ini new file mode 100644 index 00000000..1d250f82 --- /dev/null +++ b/test/tox.fedora_36.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py3 + +[testenv] +allowlist_externals = docker +deps = -rrequirements.txt +commands = docker build -f _fedora_36.Dockerfile -t pytest_pihole:test_container ../ + pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py ./test_centos_fedora_common_support.py ./test_fedora_support.py diff --git a/test/tox.ubuntu_20.ini b/test/tox.ubuntu_20.ini index 80fb7485..4ae79a0c 100644 --- a/test/tox.ubuntu_20.ini +++ b/test/tox.ubuntu_20.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt commands = docker build -f _ubuntu_20.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py diff --git a/test/tox.ubuntu_22.ini b/test/tox.ubuntu_22.ini index 41007a9c..3ddf7eca 100644 --- a/test/tox.ubuntu_22.ini +++ b/test/tox.ubuntu_22.ini @@ -2,7 +2,7 @@ envlist = py3 [testenv] -whitelist_externals = docker +allowlist_externals = docker deps = -rrequirements.txt commands = docker build -f _ubuntu_22.Dockerfile -t pytest_pihole:test_container ../ pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py