Saturday, May 09, 2026 AM03:21:24 HKT
This commit is contained in:
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2022 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
set -e
|
||||
|
||||
NGINX_VERSION="$1"
|
||||
N_CPUS=$(nproc)
|
||||
|
||||
if [ -z "$NGINX_VERSION" ]; then
|
||||
echo "usage: $0 <nginx version>"
|
||||
echo "example: $0 1.12.2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "############################"
|
||||
echo " NGINX VERSION: $NGINX_VERSION"
|
||||
echo "############################"
|
||||
|
||||
NEW_BUILD=true
|
||||
if [ -d "nginx-tmp" ] && [ "$NGINX_VERSION" == $(cat nginx-tmp/nginx.version) ]; then
|
||||
NEW_BUILD=false
|
||||
fi
|
||||
|
||||
if $NEW_BUILD ; then
|
||||
rm -rf nginx-source nginx-tmp nginx.tar.gz 2>&1 > /dev/null
|
||||
wget --no-clobber -O nginx.tar.gz "https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz"
|
||||
mkdir -p nginx-source nginx-tmp/naxsi_ut/root
|
||||
echo "$NGINX_VERSION" > nginx-tmp/nginx.version
|
||||
tar -C nginx-source -xzf nginx.tar.gz --strip-components=1
|
||||
rm nginx.tar.gz
|
||||
fi
|
||||
|
||||
export NAXSI_SRC_PATH=$(realpath naxsi_src/)
|
||||
export NAXSI_TMP_PATH=$(realpath nginx-tmp/)
|
||||
export NGINX_TMP_PATH=$(realpath nginx-source/)
|
||||
|
||||
if $NEW_BUILD ; then
|
||||
cd "$NGINX_TMP_PATH"
|
||||
./configure --with-cc-opt='-g -O2 -Wextra -Wall -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \
|
||||
--with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' \
|
||||
--with-select_module \
|
||||
--conf-path="$NAXSI_TMP_PATH/naxsi_ut/nginx.conf" \
|
||||
--http-client-body-temp-path="$NAXSI_TMP_PATH/naxsi_ut/body/" \
|
||||
--http-fastcgi-temp-path="$NAXSI_TMP_PATH/naxsi_ut/fastcgi/" \
|
||||
--http-proxy-temp-path="$NAXSI_TMP_PATH/naxsi_ut/proxy/" \
|
||||
--lock-path="$NAXSI_TMP_PATH/nginx.lock" \
|
||||
--pid-path="$NAXSI_TMP_PATH/naxsi_ut/nginx.pid" \
|
||||
--modules-path="$NAXSI_TMP_PATH/naxsi_ut/modules/" \
|
||||
--without-mail_pop3_module \
|
||||
--without-mail_smtp_module \
|
||||
--without-mail_imap_module \
|
||||
--with-http_realip_module \
|
||||
--with-http_v2_module \
|
||||
--without-http_uwsgi_module \
|
||||
--without-http_scgi_module \
|
||||
--prefix="$NAXSI_TMP_PATH/" \
|
||||
--add-dynamic-module="$NAXSI_SRC_PATH" \
|
||||
--error-log-path="$NAXSI_TMP_PATH/naxsi_ut/error.log" \
|
||||
--conf-path="$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
cd ..
|
||||
fi
|
||||
|
||||
make -C "$NGINX_TMP_PATH" -j$N_CPUS install
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2022 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
set -e
|
||||
|
||||
N_DEBUGS=$(cat naxsi_src/naxsi.h | grep "#define _debug" | grep 1 | wc -l)
|
||||
if [ $N_DEBUGS -gt 0 ]; then
|
||||
cat naxsi_src/naxsi.h | grep "#define _debug" | grep 1
|
||||
exit 1
|
||||
fi
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2023 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
set -e
|
||||
|
||||
check_file_for() {
|
||||
local FILENAME="$1"
|
||||
local SEARCH_FOR="$2"
|
||||
local EXPECTED="$3"
|
||||
local FOUND=$(cat "$FILENAME" | grep "$SEARCH_FOR")
|
||||
if [ -z "$FOUND" ]; then
|
||||
echo "'$FILENAME' does not contain '$SEARCH_FOR'."
|
||||
exit 1
|
||||
fi
|
||||
local HAS_EXPECTED=$(echo "$FOUND" | grep "$EXPECTED")
|
||||
if [ -z "$HAS_EXPECTED" ]; then
|
||||
echo "'$FILENAME' differs with what is expected to have."
|
||||
echo "Expected: $EXPECTED"
|
||||
echo "Got: $FOUND"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# check distro release version.
|
||||
NAXSI_VERSION=$(grep -R "NAXSI_VERSION" naxsi_src/ | cut -d '"' -f2)
|
||||
|
||||
if [ -z "$NAXSI_VERSION" ]; then
|
||||
echo "Cannot find NAXSI_VERSION in naxsi_src/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_file_for "distros/arch/PKGBUILD" "pkgver=" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.18.0-r13/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.20.1-r3/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.20.2-r1/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.22.1-r0/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.18.0-r15/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.20.2-r0/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.22.0-r1/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
check_file_for "distros/alpine/1.24.0-r3/APKBUILD" "_add_module \"http-naxsi" "$NAXSI_VERSION"
|
||||
|
||||
# check libinjection hash
|
||||
SUBMODULE_HASH=$(git -C naxsi_src/libinjection rev-parse HEAD)
|
||||
for DISTRO_YAML_HASH in $(grep "git -C naxsi_src/libinjection checkout" .github/workflows/distros.yaml | xargs -L1 echo | cut -d ' ' -f5); do
|
||||
if [ "$DISTRO_YAML_HASH" != "$SUBMODULE_HASH" ]; then
|
||||
echo "The libinjection hash in .github/workflows/distros.yaml does not match the submodule one."
|
||||
echo "Expected: $SUBMODULE_HASH"
|
||||
echo "Got: $DISTRO_YAML_HASH"
|
||||
echo ".github/workflows/distros.yaml:"
|
||||
grep -n "$DISTRO_YAML_HASH" .github/workflows/distros.yaml
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2022 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
RULE_FOLDER="$1"
|
||||
|
||||
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
TMP_DIR="$SCRIPT_DIR/.tmp"
|
||||
RET_VAL=0
|
||||
|
||||
if [ -z "$RULE_FOLDER" ]; then
|
||||
echo "usage: $0 <rule folder>"
|
||||
echo "example: $0 naxsi_rules/blocking"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -d "$TMP_DIR" ]]; then
|
||||
rm -rf "$TMP_DIR"
|
||||
fi
|
||||
|
||||
mkdir "$TMP_DIR" || exit 1
|
||||
for FILE in $(ls "$RULE_FOLDER/"*.rules); do
|
||||
FILENAME=$(basename $FILE)
|
||||
echo "Linter: $FILE"
|
||||
python "$SCRIPT_DIR/naxsi-lint.py" -r "$FILE" -o "$TMP_DIR/$FILENAME" || exit 1
|
||||
DIFF=$(diff -u "$FILE" "$TMP_DIR/$FILENAME")
|
||||
if [[ ! -z "$DIFF" ]]; then
|
||||
diff -u --color=auto "$FILE" "$TMP_DIR/$FILENAME"
|
||||
RET_VAL=1
|
||||
fi
|
||||
done
|
||||
|
||||
rm -rf "$TMP_DIR"
|
||||
exit $RET_VAL
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2022 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
RUN_TEST="$1"
|
||||
|
||||
export NAXSI_CFG_PATH=$(realpath naxsi_rules/)
|
||||
export NAXSI_TMP_PATH=$(realpath nginx-tmp/)
|
||||
export NAXSI_TST_PATH=$(realpath unit-tests/)
|
||||
|
||||
if [ -z "$NAXSI_CFG_PATH" ] || [ -z "$NAXSI_TMP_PATH" ] || [ -z "$NAXSI_TST_PATH" ] ; then
|
||||
echo "did you run first ci-build.sh ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "############################"
|
||||
echo " running tests"
|
||||
echo "############################"
|
||||
|
||||
cp -v "$NAXSI_TST_PATH/nginx-ci.conf" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
openssl req -batch -x509 -nodes -days 365 -newkey rsa:2048 -keyout "$NAXSI_TMP_PATH/nginx.key" -out "$NAXSI_TMP_PATH/nginx.crt"
|
||||
|
||||
export PATH="$NGINX_TMP_PATH/objs/:$PATH"
|
||||
export TEST_NGINX_SERVROOT="$NAXSI_TMP_PATH/naxsi_ut/root"
|
||||
export TEST_NGINX_BINARY="$NAXSI_TMP_PATH/sbin/nginx"
|
||||
export TEST_NGINX_NAXSI_MODULE_SO="$NAXSI_TMP_PATH/naxsi_ut/modules/ngx_http_naxsi_module.so"
|
||||
export TEST_NGINX_NAXSI_RULES="$NAXSI_CFG_PATH/naxsi_core.rules"
|
||||
export TEST_NGINX_NAXSI_BLOCKING_RULES="$NAXSI_CFG_PATH/blocking"
|
||||
export TEST_NGINX_NAXSI_WHITELISTS_RULES="$NAXSI_CFG_PATH/whitelists"
|
||||
|
||||
cd "$NAXSI_TMP_PATH"
|
||||
|
||||
if [ -z "$RUN_TEST" ]; then
|
||||
prove -r "$NAXSI_TST_PATH/tests/"*.t
|
||||
else
|
||||
prove --verbose -r "$NAXSI_TST_PATH/tests/$RUN_TEST"
|
||||
fi
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2022 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
RUN_TEST="$1"
|
||||
|
||||
if command -v python3 &> /dev/null ; then
|
||||
PYTHON=python3
|
||||
elif command -v python &> /dev/null ; then
|
||||
PYTHON=python
|
||||
else
|
||||
echo "Cannot find python.."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export NAXSI_CFG_PATH=$(realpath naxsi_rules/)
|
||||
export NAXSI_TMP_PATH=$(realpath nginx-tmp/)
|
||||
export NAXSI_TST_PATH=$(realpath unit-tests/)
|
||||
|
||||
if [ -z "$NAXSI_CFG_PATH" ] || [ -z "$NAXSI_TMP_PATH" ] || [ -z "$NAXSI_TST_PATH" ] ; then
|
||||
echo "did you run first ci-build.sh ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "############################"
|
||||
echo " running tests"
|
||||
echo "############################"
|
||||
|
||||
cp -v "$NAXSI_TST_PATH/nginx-ci.conf" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
openssl req -batch -x509 -nodes -days 365 -newkey rsa:2048 -keyout "$NAXSI_TMP_PATH/nginx.key" -out "$NAXSI_TMP_PATH/nginx.crt"
|
||||
|
||||
$PYTHON .scripts/naxsi-gen-tests.py
|
||||
$PYTHON -m unittest discover ./unit-tests/python/ $RUN_TEST -v
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2023 wargio <deroad@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
set -e
|
||||
|
||||
WAFEFFICACY_VERSION="v1.0"
|
||||
NUCLEI_VERSION="2.9.6"
|
||||
|
||||
case "$1" in
|
||||
"core" | "complete")
|
||||
;;
|
||||
*)
|
||||
if [ -z "$1" ]; then
|
||||
echo "usage: $0 [mode]"
|
||||
else
|
||||
echo "error: invalid mode option '$1'."
|
||||
fi
|
||||
echo "supported modes:"
|
||||
echo " - core test efficacy only against naxsi_core.rules"
|
||||
echo " - complete test efficacy against all rules"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
export NAXSI_CFG_PATH=$(realpath naxsi_rules)
|
||||
export NAXSI_TMP_PATH=$(realpath nginx-tmp)
|
||||
export NAXSI_DST_PATH=$(realpath distros/nginx)
|
||||
export NAXSI_TST_PATH=$(realpath unit-tests/)
|
||||
export NAXSI_VERSION=$(grep -R "NAXSI_VERSION" naxsi_src/ | cut -d '"' -f2)
|
||||
export NAXSI_MODE="$1"
|
||||
|
||||
check_for() {
|
||||
if [ "$1" == "f" ] && [ ! -f "$2" ]; then
|
||||
echo "file '$1' does not exist!"
|
||||
exit 1
|
||||
elif [ "$1" == "d" ] && [ ! -d "$2" ]; then
|
||||
echo "directory '$1' does not exist!"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -z "$NAXSI_CFG_PATH" ] || [ -z "$NAXSI_TMP_PATH" ] || [ -z "$NAXSI_DST_PATH" ] ; then
|
||||
echo "did you run first ci-build.sh ?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$NAXSI_TMP_PATH/wafefficacy" ]; then
|
||||
mkdir -p "$NAXSI_TMP_PATH/wafefficacy-tmp"
|
||||
OLDPATH="$PWD"
|
||||
cd "$NAXSI_TMP_PATH/wafefficacy-tmp"
|
||||
# install wafefficacy
|
||||
wget -O wafefficacy.zip "https://github.com/wargio/wafefficacy/releases/download/$WAFEFFICACY_VERSION/wafefficacy-linux-amd64.zip"
|
||||
unzip wafefficacy.zip
|
||||
mv -v wafefficacy-release "$NAXSI_TMP_PATH/wafefficacy"
|
||||
cd "$OLDPATH"
|
||||
rm -rf "$NAXSI_TMP_PATH/wafefficacy-tmp"
|
||||
fi
|
||||
|
||||
if [ ! -f "$NAXSI_TMP_PATH/sbin/nuclei" ]; then
|
||||
mkdir -p "$NAXSI_TMP_PATH/nuclei-tmp"
|
||||
OLDPATH="$PWD"
|
||||
cd "$NAXSI_TMP_PATH/nuclei-tmp"
|
||||
# install nuclei
|
||||
wget -O nuclei.zip "https://github.com/projectdiscovery/nuclei/releases/download/v$NUCLEI_VERSION/nuclei_""$NUCLEI_VERSION""_linux_amd64.zip"
|
||||
unzip nuclei.zip || sleep 0
|
||||
mv -v nuclei "$NAXSI_TMP_PATH/sbin/"
|
||||
cd "$OLDPATH"
|
||||
rm -rf "$NAXSI_TMP_PATH/nuclei-tmp"
|
||||
fi
|
||||
|
||||
echo "############################"
|
||||
echo " running wafefficacy"
|
||||
echo "############################"
|
||||
|
||||
if [ ! -d "$NAXSI_TMP_PATH/naxsi_ut/logs" ]; then
|
||||
mkdir -p "$NAXSI_TMP_PATH/naxsi_ut/logs"
|
||||
fi
|
||||
|
||||
WAFEFFICACY_NGINX_CONF=$(realpath "$NAXSI_TST_PATH/nginx-ci-wafefficacy.conf")
|
||||
RULES_NGINX_NAXSI_BLOCKING=$(realpath "$NAXSI_CFG_PATH/blocking")
|
||||
RULES_NGINX_NAXSI_CORE=$(realpath "$NAXSI_CFG_PATH/naxsi_core.rules")
|
||||
RULES_NGINX_NAXSI_WHITELISTS=$(realpath "$NAXSI_CFG_PATH/whitelists")
|
||||
TEST_NGINX_NAXSI_MODULE_SO=$(realpath "$NAXSI_TMP_PATH/naxsi_ut/modules/ngx_http_naxsi_module.so")
|
||||
|
||||
check_for "f" "$WAFEFFICACY_NGINX_CONF"
|
||||
check_for "f" "$TEST_NGINX_NAXSI_MODULE_SO"
|
||||
check_for "f" "$RULES_NGINX_NAXSI_CORE"
|
||||
check_for "d" "$RULES_NGINX_NAXSI_WHITELISTS"
|
||||
check_for "d" "$RULES_NGINX_NAXSI_BLOCKING"
|
||||
|
||||
cp -v "$WAFEFFICACY_NGINX_CONF" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
sed -i "s#@RULES_NGINX_NAXSI_CORE@#include $RULES_NGINX_NAXSI_CORE;#g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
sed -i "s#@TEST_NGINX_NAXSI_MODULE_SO@#$TEST_NGINX_NAXSI_MODULE_SO#g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
|
||||
case "$1" in
|
||||
"core")
|
||||
sed -i "s#@RULES_NGINX_NAXSI_BLOCKING@##g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
sed -i "s#@RULES_NGINX_NAXSI_WHITELISTS@##g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
;;
|
||||
"complete")
|
||||
sed -i "s#@RULES_NGINX_NAXSI_BLOCKING@#include $RULES_NGINX_NAXSI_BLOCKING/*;#g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
sed -i "s#@RULES_NGINX_NAXSI_WHITELISTS@#include $RULES_NGINX_NAXSI_WHITELISTS/*;#g" "$NAXSI_TMP_PATH/naxsi_ut/nginx.conf"
|
||||
;;
|
||||
*)
|
||||
echo "error: invalid mode option '$1'."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
cd "$NAXSI_TMP_PATH/wafefficacy"
|
||||
|
||||
"$NAXSI_TMP_PATH/sbin/nginx" -p "$NAXSI_TMP_PATH/naxsi_ut/" &
|
||||
PATH="$PATH:$NAXSI_TMP_PATH/sbin" ./run.sh -w "naxsi-$NAXSI_VERSION" -t localhost:4242
|
||||
|
||||
jobs -p | xargs kill
|
||||
@@ -0,0 +1,30 @@
|
||||
@echo off
|
||||
rem SPDX-FileCopyrightText: 2022 Alex <alex@staticlibs.net>
|
||||
rem SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
@echo on
|
||||
|
||||
set BAD_SLASH_SCRIPT_DIR=%~dp0
|
||||
set SCRIPT_DIR=%BAD_SLASH_SCRIPT_DIR:\=/%
|
||||
set NAXSI_DIR=%SCRIPT_DIR%..
|
||||
set NW_DIR=%NAXSI_DIR%/../nginx-windows
|
||||
|
||||
set NGINX_VERSION=%1
|
||||
|
||||
pushd .. || exit /b 1
|
||||
git clone --branch 1.22.1-1 https://github.com/noproxy-http/nginx-windows.git || exit /b 1
|
||||
popd || exit /b 1
|
||||
|
||||
pushd "%NW_DIR%" || exit /b 1
|
||||
git submodule update --init || exit /b 1
|
||||
popd || exit /b 1
|
||||
|
||||
pushd "%NW_DIR%/sources/nginx" || exit /b 1
|
||||
git checkout release-%NGINX_VERSION% || exit /b 1
|
||||
popd || exit /b 1
|
||||
|
||||
robocopy %NAXSI_DIR%/naxsi_src %NW_DIR%/modules/naxsi_src /e /nfl /ndl /njh /njs /nc /ns /np
|
||||
|
||||
pushd "%NW_DIR%" || exit /b 1
|
||||
scripts\build.bat "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat" nossl
|
||||
popd || exit /b 1
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
@echo off
|
||||
rem SPDX-FileCopyrightText: 2022 Alex <alex@staticlibs.net>
|
||||
rem SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
@echo on
|
||||
|
||||
set BAD_SLASH_SCRIPT_DIR=%~dp0
|
||||
set SCRIPT_DIR=%BAD_SLASH_SCRIPT_DIR:\=/%
|
||||
set NAXSI_DIR=%SCRIPT_DIR%..
|
||||
|
||||
pushd "%NAXSI_DIR%" || exit /b 1
|
||||
python .scripts/naxsi-windows-test-dist.py %NAXSI_DIR%/../nginx-windows/build/dist || exit /b 1
|
||||
python .scripts/naxsi-gen-tests.py || exit /b 1
|
||||
python -m unittest discover unit-tests/python/ -v || exit /b 1
|
||||
popd || exit /b 1
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
# -* coding: utf-8
|
||||
# SPDX-FileCopyrightText: 2022 Alex <alex@staticlibs.net>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
import os, sys
|
||||
from collections import namedtuple
|
||||
from os import path, replace
|
||||
import re
|
||||
|
||||
NginxTest = namedtuple("NginxTest", "filename name user_files main_config http_config config more_headers request raw_request curl curl_protocol curl_options error_code, error_log no_error_log response_body")
|
||||
|
||||
unique_fun_names = set()
|
||||
|
||||
def is_test_file(dirpath, filename):
|
||||
if not filename.endswith(".t"):
|
||||
return False
|
||||
filepath = path.join(dirpath, filename)
|
||||
return path.isfile(filepath)
|
||||
|
||||
def read_list_of_test_files(test_file_or_dir):
|
||||
if path.isfile(test_file_or_dir):
|
||||
return [test_file_or_dir]
|
||||
elif path.isdir(test_file_or_dir):
|
||||
lst = os.listdir(test_file_or_dir)
|
||||
filtered = list(filter(lambda f: is_test_file(test_file_or_dir, f), lst))
|
||||
filtered.sort()
|
||||
return list(map(lambda f: path.join(test_file_or_dir, f), filtered))
|
||||
else:
|
||||
print("ERROR: tests not found in directory: [{}]".format(test_file_or_dir))
|
||||
sys.exit(1)
|
||||
|
||||
def collect_section(lines, idx, dest):
|
||||
i = idx + 1
|
||||
while not lines[i].startswith("--- "):
|
||||
dest.append(lines[i])
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def trim_test_lines(lines):
|
||||
last_nonempty_idx = len(lines)
|
||||
for i in reversed(range(len(lines))):
|
||||
if len(lines[i]) == 0:
|
||||
last_nonempty_idx = i
|
||||
else:
|
||||
break
|
||||
return lines[:last_nonempty_idx]
|
||||
|
||||
def parse_test(lines, test_file, line_num):
|
||||
lines = trim_test_lines(lines)
|
||||
if len(lines) == 0:
|
||||
return None
|
||||
filename = path.basename(test_file)
|
||||
name = lines[0][4:].strip()
|
||||
user_files = []
|
||||
main_config = []
|
||||
http_config = []
|
||||
config = []
|
||||
more_headers = []
|
||||
request = []
|
||||
raw_request = []
|
||||
curl = False
|
||||
curl_protocol = "http"
|
||||
curl_options = ""
|
||||
error_code = 0
|
||||
error_log = []
|
||||
no_error_log = []
|
||||
response_body = []
|
||||
idx = 1
|
||||
if lines[idx].lstrip().startswith("--- user_files"):
|
||||
idx = collect_section(lines, idx, user_files)
|
||||
if lines[idx].lstrip().startswith("--- main_config"):
|
||||
idx = collect_section(lines, idx, main_config)
|
||||
if lines[idx].lstrip().startswith("--- main_config"):
|
||||
idx = collect_section(lines, idx, main_config)
|
||||
if lines[idx].lstrip().startswith("--- http_config"):
|
||||
idx = collect_section(lines, idx, http_config)
|
||||
if lines[idx].lstrip().startswith("--- user_files"):
|
||||
idx = collect_section(lines, idx, user_files)
|
||||
if lines[idx].lstrip().startswith("--- config"):
|
||||
idx = collect_section(lines, idx, config)
|
||||
if lines[idx].lstrip().startswith("--- more_headers"):
|
||||
idx = collect_section(lines, idx, more_headers)
|
||||
if lines[idx].lstrip().startswith("--- request"):
|
||||
idx = collect_section(lines, idx, request)
|
||||
if lines[idx].lstrip().startswith("--- raw_request"):
|
||||
idx = collect_section(lines, idx, raw_request)
|
||||
if lines[idx].lstrip().startswith("--- curl"):
|
||||
curl = True
|
||||
idx = idx + 1
|
||||
if lines[idx].lstrip().startswith("--- curl_protocol:"):
|
||||
curl_protocol = lines[idx].strip()[len("--- curl_protocol:"):]
|
||||
idx = idx + 1
|
||||
if lines[idx].lstrip().startswith("--- curl_options:"):
|
||||
curl_options = lines[idx].strip()[len("--- curl_options:"):]
|
||||
idx = idx + 1
|
||||
if not lines[idx].lstrip().startswith("--- error_code: "):
|
||||
print("ERROR: Cannot parse test defintion, file: [{}], line: [{}]".format(test_file, line_num + idx - 2))
|
||||
sys.exit(1)
|
||||
prefix_len = len("--- error_code:")
|
||||
error_code = int(lines[idx].lstrip()[prefix_len:])
|
||||
if idx < len(lines) - 2 and lines[idx + 1].lstrip().startswith("--- error_log"):
|
||||
idx += 2
|
||||
while idx < len(lines) and (not lines[idx].startswith("--- ") or lines[idx].startswith("=== ")):
|
||||
error_log.append(lines[idx])
|
||||
idx += 1
|
||||
idx -= 1
|
||||
if idx < len(lines) - 2 and lines[idx + 1].lstrip().startswith("--- no_error_log"):
|
||||
idx += 2
|
||||
while idx < len(lines) and (not lines[idx].startswith("--- ") or lines[idx].startswith("=== ")):
|
||||
no_error_log.append(lines[idx])
|
||||
idx += 1
|
||||
idx -= 1
|
||||
if idx < len(lines) - 1 and lines[idx + 1].lstrip().startswith("--- response_body"):
|
||||
idx+=1
|
||||
response_body.append(lines[idx][18:].strip())
|
||||
return NginxTest(filename, name, user_files, main_config, http_config, config, more_headers, request, raw_request, curl, curl_protocol, curl_options, error_code, error_log, no_error_log, response_body)
|
||||
|
||||
def read_list_of_tests(test_file):
|
||||
tests = []
|
||||
with open(test_file, encoding="utf-8") as file:
|
||||
data_reached = False
|
||||
test_lines = []
|
||||
line_num = 0
|
||||
for line in file:
|
||||
line_num += 1
|
||||
stripped = line.rstrip()
|
||||
if stripped.startswith("#"):
|
||||
continue
|
||||
if not data_reached:
|
||||
if "__DATA__" == stripped:
|
||||
data_reached = True
|
||||
continue
|
||||
if stripped.startswith("==="):
|
||||
if len(test_lines) > 0:
|
||||
test = parse_test(test_lines, test_file, line_num - len(test_lines))
|
||||
if test is not None:
|
||||
tests.append(test)
|
||||
test_lines = []
|
||||
test_lines.append(stripped)
|
||||
if len(test_lines) > 0:
|
||||
test = parse_test(test_lines, test_file, line_num - len(test_lines))
|
||||
if test is not None:
|
||||
tests.append(test)
|
||||
return tests
|
||||
|
||||
def format_array_eval_line(line):
|
||||
line = line[2:-2].strip()
|
||||
elems = line.split(",")
|
||||
for i in range(len(elems)):
|
||||
elems[i] = elems[i].strip()
|
||||
xidx = elems[i].find("\"x")
|
||||
if xidx > 0:
|
||||
base = elems[i][1:xidx]
|
||||
count = int(elems[i][xidx+2:])
|
||||
elems[i] = ""
|
||||
for j in range(count):
|
||||
elems[i] += base
|
||||
else:
|
||||
elems[i] = elems[i][1:-1]
|
||||
return "".join(elems)
|
||||
|
||||
def format_test_lines(test):
|
||||
for i in range(len(test.http_config)):
|
||||
test.http_config[i] = test.http_config[i].replace("\\", "\\\\")
|
||||
test.http_config[i] = " {}".format(test.http_config[i])
|
||||
for i in range(len(test.config)):
|
||||
test.config[i] = " {}".format(test.config[i])
|
||||
request_lines_to_pop = []
|
||||
for i in range(len(test.request)):
|
||||
line = test.request[i]
|
||||
if line.lstrip().startswith("use "):
|
||||
request_lines_to_pop.append(i)
|
||||
if line.startswith("[[") and line.endswith("]]"):
|
||||
test.request[i] = format_array_eval_line(line)
|
||||
for idx in request_lines_to_pop:
|
||||
test.request.pop(idx)
|
||||
if len(test.request) > 0 and test.request[0].startswith("\""):
|
||||
test.request[0] = test.request[0][1:]
|
||||
test.request[-1] = test.request[-1][:-1]
|
||||
for i in range(len(test.request)):
|
||||
line = test.request[i]
|
||||
test.request[i] = line.replace("\\\"", "\"").replace("\x00", "\\x00")
|
||||
if len(test.raw_request) > 0 and test.raw_request[0].startswith("\""):
|
||||
test.raw_request[0] = test.raw_request[0][1:]
|
||||
test.raw_request[-1] = test.raw_request[-1][:-1]
|
||||
for i in range(len(test.raw_request)):
|
||||
line = test.raw_request[i]
|
||||
test.raw_request[i] = line.replace("\\\"", "\"")
|
||||
if len(test.error_log) > 0:
|
||||
el = test.error_log
|
||||
for i in range(len(el)):
|
||||
if el[i].startswith("["):
|
||||
el[i] = el[i][1:].lstrip()
|
||||
if el[i].startswith("qr@"):
|
||||
el[i] = el[i][3:].lstrip()
|
||||
if el[i].endswith("]"):
|
||||
el[i] = el[i][:-1].rstrip()
|
||||
if el[i].endswith(","):
|
||||
el[i] = el[i][:-1].rstrip()
|
||||
if el[i].endswith("@"):
|
||||
el[i] = el[i][:-1].rstrip()
|
||||
el[i] = el[i].replace("server=localhost", "server=127.0.0.1")
|
||||
if len(el[-1]) == 0:
|
||||
el.pop()
|
||||
if len(test.response_body) > 0:
|
||||
for i in range(len(test.response_body)):
|
||||
test.response_body[i] = test.response_body[i].replace("localhost", "127.0.0.1")
|
||||
|
||||
def hotpatch_test(test):
|
||||
if "TEST 24: Testing MULTIPART POSTs" == test.name:
|
||||
test.more_headers[1] = test.more_headers[1].replace(
|
||||
"Content-Length: 355",
|
||||
"Content-Length: 353",
|
||||
)
|
||||
|
||||
def parse_req_method(req):
|
||||
space_idx = req[0].find(" ")
|
||||
return req[0][:space_idx]
|
||||
|
||||
def parse_req_data(req, method):
|
||||
if method not in ["POST", "PATCH"]:
|
||||
return None
|
||||
rn_idx = req[0].find("\\r\\n")
|
||||
inline = ""
|
||||
if rn_idx > 0:
|
||||
inline = req[0][rn_idx:]
|
||||
res = inline
|
||||
if len(req) > 1:
|
||||
res += "\n".join(req[1:])
|
||||
if res.startswith("\\r\\n"):
|
||||
res = res[4:]
|
||||
if res.endswith("\\r\\n\\r\\n"):
|
||||
res = res[:-8]
|
||||
return res
|
||||
|
||||
def parse_req_url(req):
|
||||
space_idx = req[0].find(" ")
|
||||
rn_idx = req[0].find("\\r\\n")
|
||||
if rn_idx > 0:
|
||||
return req[0][space_idx + 1:rn_idx]
|
||||
else:
|
||||
return req[0][space_idx + 1:]
|
||||
|
||||
def parse_headers(more_headers):
|
||||
headers = {}
|
||||
for line in more_headers:
|
||||
idx = line.find(": ")
|
||||
name = line[:idx]
|
||||
value = line[idx + 2:]
|
||||
headers[name] = value
|
||||
return headers
|
||||
|
||||
def parse_user_files(user_files):
|
||||
res = {}
|
||||
name = None
|
||||
for line in user_files:
|
||||
stripped = line.strip()
|
||||
if stripped.startswith(">>> "):
|
||||
name = stripped[4:]
|
||||
res[name] = ""
|
||||
else:
|
||||
res[name] += line
|
||||
return res
|
||||
|
||||
def gen_file_header(test_file_name_noext):
|
||||
cls_name = test_file_name_noext[2:].replace("-", "_")
|
||||
return """# -* coding: utf-8
|
||||
# SPDX-FileCopyrightText: NAXSI project
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
import unittest
|
||||
from _test_utils import nginx_runner
|
||||
|
||||
class {}(unittest.TestCase):""".format(cls_name)
|
||||
|
||||
def gen_function_header(test):
|
||||
fun_name = test.name.strip()
|
||||
fun_name = re.sub(r'[^a-zA-Z\d_]', '_', fun_name)
|
||||
if not fun_name.startswith("test_"):
|
||||
fun_name = "test_{}".format(fun_name)
|
||||
while fun_name in unique_fun_names:
|
||||
fun_name += "_"
|
||||
unique_fun_names.add(fun_name)
|
||||
docstring = test.name.replace("\\x", "\\\\x")
|
||||
return '''
|
||||
def {}(self):
|
||||
"""
|
||||
{}
|
||||
"""'''.format(fun_name, docstring)
|
||||
|
||||
def gen_runner_init(test):
|
||||
res = ''' with nginx_runner('''
|
||||
if len(test.http_config) > 0:
|
||||
http_config = "\n".join(test.http_config)
|
||||
res += '''
|
||||
http_config="""
|
||||
{}
|
||||
""",'''.format(http_config)
|
||||
if len(test.config) > 0:
|
||||
config = "\n".join(test.config)
|
||||
res += '''
|
||||
config="""
|
||||
{}
|
||||
""",'''.format(config)
|
||||
if len(test.user_files) > 0:
|
||||
res += '''
|
||||
user_files={'''
|
||||
files = parse_user_files(test.user_files)
|
||||
for name, value in files.items():
|
||||
res += '''
|
||||
"{}": "{}",'''.format(name, value)
|
||||
res += '''
|
||||
},'''
|
||||
res += '''
|
||||
) as nr:'''
|
||||
return res;
|
||||
|
||||
def gen_request(test):
|
||||
if len(test.response_body) > 0:
|
||||
res = ''' ec, resp_body = nr.request('''
|
||||
else:
|
||||
res = ''' ec = nr.request('''
|
||||
url = parse_req_url(test.request).replace("\"", "\\\"")
|
||||
res += '''
|
||||
url="{}",'''.format(url)
|
||||
method = parse_req_method(test.request)
|
||||
if "GET" != method:
|
||||
res += '''
|
||||
method="{}",'''.format(method)
|
||||
if len(test.more_headers) > 0:
|
||||
res += '''
|
||||
headers={'''
|
||||
headers = parse_headers(test.more_headers)
|
||||
for name, value in headers.items():
|
||||
res += '''
|
||||
"{}": "{}",'''.format(name, value)
|
||||
res += '''
|
||||
},'''
|
||||
data = parse_req_data(test.request, method)
|
||||
if data is not None:
|
||||
res += '''
|
||||
data="""{}""",'''.format(data)
|
||||
if test.curl:
|
||||
res += '''
|
||||
curl=True,'''
|
||||
if len(test.curl_protocol) > 0:
|
||||
res += '''
|
||||
curl_protocol="{}",'''.format(test.curl_protocol)
|
||||
if len(test.curl_options) > 0:
|
||||
res += '''
|
||||
curl_options="{}",'''.format(test.curl_options)
|
||||
if len(test.response_body) > 0:
|
||||
res += '''
|
||||
resp_body_required=True'''
|
||||
res += '''
|
||||
)'''. format(test.error_code)
|
||||
return res
|
||||
|
||||
def gen_raw_request(test):
|
||||
raw_req = "\n".join(test.raw_request)
|
||||
return ''' ec = nr.raw_request("""
|
||||
{}""")'''.format(raw_req)
|
||||
|
||||
def gen_checks(test):
|
||||
res = " self.assertEqual(ec, {})".format(test.error_code)
|
||||
if len(test.error_log) > 0:
|
||||
res += '''
|
||||
elm = nr.error_log_matches(['''
|
||||
for line in test.error_log:
|
||||
line = line.replace("\"", "\\\"")
|
||||
res += '''
|
||||
r"{}",'''.format(line)
|
||||
res += '''
|
||||
])
|
||||
self.assertTrue(elm)'''
|
||||
if len(test.no_error_log) > 0:
|
||||
res += '''
|
||||
nelm = nr.error_log_matches(['''
|
||||
for line in test.no_error_log:
|
||||
line = line.replace("\"", "\\\"")
|
||||
res += '''
|
||||
r"{}",'''.format(line)
|
||||
res += '''
|
||||
])
|
||||
self.assertFalse(nelm)'''
|
||||
if len(test.response_body) > 0:
|
||||
response_body = "\n".join(test.response_body)
|
||||
res += '''
|
||||
self.assertEqual(resp_body, """{}""".encode("utf-8"))'''.format(response_body)
|
||||
return res
|
||||
|
||||
def gen_test_function(test):
|
||||
header = gen_function_header(test)
|
||||
runner = gen_runner_init(test)
|
||||
if len(test.request) > 0:
|
||||
send_req = gen_request(test)
|
||||
else:
|
||||
send_req = gen_raw_request(test)
|
||||
checks = gen_checks(test)
|
||||
return '''
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
{}'''.format(header, runner, send_req, checks)
|
||||
|
||||
def write_python_test_file(test_file_name, test_list):
|
||||
scripts_dir = path.dirname(__file__)
|
||||
naxsi_dir = path.dirname(scripts_dir)
|
||||
dest_dir = path.join(naxsi_dir, "unit-tests", "python")
|
||||
test_file_name_noext = path.splitext(test_file_name)[0]
|
||||
test_file_name_nodash = test_file_name_noext.replace("-", "_")
|
||||
dest_file_name = "test_{}.py".format(test_file_name_nodash)
|
||||
dest_file_path = path.join(dest_dir, dest_file_name)
|
||||
if path.exists(dest_file_path):
|
||||
os.remove(dest_file_path)
|
||||
with open(dest_file_path, "w", encoding="utf-8") as file:
|
||||
file.write(gen_file_header(test_file_name_noext))
|
||||
for test in test_list:
|
||||
hotpatch_test(test)
|
||||
format_test_lines(test)
|
||||
file.write(gen_test_function(test))
|
||||
file.write("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
scripts_dir = path.dirname(__file__)
|
||||
naxsi_dir = path.dirname(scripts_dir)
|
||||
tests_dir = path.join(naxsi_dir, "unit-tests", "tests")
|
||||
test_files_list = read_list_of_test_files(tests_dir)
|
||||
for test_file_path in test_files_list[:]:
|
||||
unique_fun_names.clear()
|
||||
test_list = read_list_of_tests(test_file_path)
|
||||
test_file_name = path.basename(test_file_path)
|
||||
write_python_test_file(test_file_name, test_list[:])
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
import shlex
|
||||
|
||||
DESCRIPTION='Naxsi Rule Linter'
|
||||
EPILOG='''
|
||||
This tool lints naxsi rules
|
||||
Example:
|
||||
$ python naxsi-lint.py --rule /path/to/file.rules --output output.rules --begin-id 4200000
|
||||
'''
|
||||
|
||||
def clean_token(token):
|
||||
if not token.startswith('"'):
|
||||
return token
|
||||
return token[1:-1]
|
||||
|
||||
def find_keyword(keyword, tokens):
|
||||
for token in tokens:
|
||||
token = clean_token(token)
|
||||
if token.startswith(keyword):
|
||||
return token
|
||||
return None
|
||||
|
||||
|
||||
class Rule(object):
|
||||
def __init__(self, tokens, line_num, comments):
|
||||
super(Rule, self).__init__()
|
||||
self.line_num = line_num
|
||||
self.comments = comments
|
||||
self.main_rule = find_keyword("MainRule", tokens)
|
||||
self.basic_rule = find_keyword("BasicRule", tokens)
|
||||
self.id = find_keyword("id:", tokens)
|
||||
self.msg = find_keyword("msg:", tokens)
|
||||
self.str = find_keyword("str:", tokens)
|
||||
self.rx = find_keyword("rx:", tokens)
|
||||
self.mz = find_keyword("mz:", tokens)
|
||||
self.s = find_keyword("s:", tokens)
|
||||
self.wl = find_keyword("wl:", tokens)
|
||||
self.d = find_keyword("d:libinj_", tokens)
|
||||
self.negative = find_keyword("negative", tokens)
|
||||
|
||||
# validate first
|
||||
self.validate()
|
||||
|
||||
if self.wl != None:
|
||||
ids = list(set(self.wl[3:].split(',')))
|
||||
ids.sort()
|
||||
self.wl = 'wl:' + ','.join(ids)
|
||||
else:
|
||||
self.id = int(self.id[3:])
|
||||
if self.msg != None:
|
||||
self.msg = re.sub(r'\s+', ' ', self.msg.strip())
|
||||
self.msg = re.sub(r'"', '\'', self.msg.strip())
|
||||
|
||||
def validate(self):
|
||||
if self.main_rule == None and self.basic_rule == None:
|
||||
print("ERROR: {}: missing 'MainRule' or 'BasicRule' prefix".format(self.line_num))
|
||||
sys.exit(1)
|
||||
if self.main_rule != None and self.basic_rule != None:
|
||||
print("ERROR: {}: 'MainRule' and 'BasicRule' are set".format(self.line_num))
|
||||
sys.exit(1)
|
||||
|
||||
if self.id == None and self.wl == None:
|
||||
print("ERROR: {}: missing 'id:' or 'wl:'".format(self.line_num))
|
||||
sys.exit(1)
|
||||
elif self.id != None and self.wl != None:
|
||||
print("ERROR: {}: 'id:' and 'wl:' are both set".format(self.line_num))
|
||||
sys.exit(1)
|
||||
|
||||
if self.is_whitelist():
|
||||
if self.mz == None:
|
||||
print("ERROR: {}: missing whitelist 'mz:'".format(self.line_num))
|
||||
sys.exit(1)
|
||||
elif self.str != None:
|
||||
print("ERROR: {}: 'str:' is set for a whitelist".format(self.line_num))
|
||||
sys.exit(1)
|
||||
elif self.rx != None:
|
||||
print("ERROR: {}: 'rx:' is set for a whitelist".format(self.line_num))
|
||||
sys.exit(1)
|
||||
elif self.d != None:
|
||||
print("ERROR: {}: 'd:' is set for a whitelist".format(self.line_num))
|
||||
sys.exit(1)
|
||||
else:
|
||||
self.match()
|
||||
if self.mz == None:
|
||||
print("ERROR: {}: missing rule 'mz:'".format(self.line_num))
|
||||
sys.exit(1)
|
||||
if self.s == None:
|
||||
print("ERROR: {}: missing rule 's:'".format(self.line_num))
|
||||
sys.exit(1)
|
||||
|
||||
def prefix(self):
|
||||
return self.main_rule if self.main_rule != None else self.basic_rule
|
||||
|
||||
def match(self):
|
||||
if self.rx != None:
|
||||
return self.rx
|
||||
elif self.str != None:
|
||||
return self.str
|
||||
elif self.d != None:
|
||||
return self.d
|
||||
print("ERROR: {}: missing rule 'rx:' or 'str:' or 'd:'".format(self.line_num))
|
||||
sys.exit(1)
|
||||
|
||||
def is_whitelist(self):
|
||||
return self.wl != None
|
||||
|
||||
def message(self):
|
||||
if self.msg == None:
|
||||
return "match " + self.match().replace('"', '')
|
||||
return self.msg[4:]
|
||||
|
||||
def print(self):
|
||||
if len(self.comments) > 0:
|
||||
print('\n'.join(self.comments))
|
||||
prefix = self.prefix()
|
||||
args = []
|
||||
if self.is_whitelist():
|
||||
args = [
|
||||
self.wl,
|
||||
self.negative,
|
||||
'"{}"'.format(self.mz),
|
||||
'"{}"'.format(self.msg),
|
||||
]
|
||||
else:
|
||||
args = [
|
||||
'id:{}'.format(self.id),
|
||||
'"{}"'.format(self.s),
|
||||
self.negative,
|
||||
'"{}"'.format(self.match()),
|
||||
'"{}"'.format(self.mz),
|
||||
'"{}"'.format(self.msg),
|
||||
]
|
||||
args = list(filter(lambda x: x != None and x != '"None"', args))
|
||||
print("{} {};".format(self.prefix(), ' '.join(args)))
|
||||
|
||||
def parse_file(filename, rules, whitelists, ruleid):
|
||||
lines = []
|
||||
with open(filename, 'r') as fp:
|
||||
lines = fp.readlines()
|
||||
|
||||
line_num = 0
|
||||
comments = []
|
||||
for line in lines:
|
||||
line_num += 1
|
||||
line = line.strip()
|
||||
if line.startswith("#") or len(line) < 1:
|
||||
comments.append(line)
|
||||
continue
|
||||
elif '#' in line and not '#"' in line and not line.endswith(';'):
|
||||
comments.append('#' + line.split('#', 1)[-1])
|
||||
line = line.split('#')[0]
|
||||
|
||||
if not line.endswith(';'):
|
||||
print("ERROR: {}: missing ; at the end the line '{}'".format(line_num, line))
|
||||
sys.exit(1)
|
||||
|
||||
line = re.sub(r';$', " ;", line)
|
||||
tokens = shlex.split(line, posix=False)
|
||||
# print("{}: {}".format(line_num, tokens))
|
||||
|
||||
rule = Rule(tokens, line_num, comments)
|
||||
comments = []
|
||||
|
||||
if rule.is_whitelist():
|
||||
whitelists.append(rule)
|
||||
continue
|
||||
|
||||
if ruleid > 100:
|
||||
rule.id = ruleid
|
||||
ruleid += 1
|
||||
elif rule.id in rules:
|
||||
print("ERROR: {}: Rule {} already exists at line {}".format(line_num, rule.id, rules[rule.id].line_num))
|
||||
sys.exit(1)
|
||||
|
||||
rules[rule.id] = rule
|
||||
|
||||
def print_rules(rules_ids, rules, whitelists):
|
||||
header = len(rules_ids) < 1
|
||||
if len(whitelists) > 0:
|
||||
for whitelist in whitelists:
|
||||
if not header:
|
||||
header = True
|
||||
print('#############')
|
||||
print("# Whitelist #")
|
||||
print('#############')
|
||||
whitelist.print()
|
||||
|
||||
for idx in rules_ids:
|
||||
if header:
|
||||
header = False
|
||||
print('#########')
|
||||
print("# Rules #")
|
||||
print('#########')
|
||||
rules[idx].print()
|
||||
|
||||
def print_translate_dictionary(rules_ids, rules, whitelists):
|
||||
for idx in rules_ids:
|
||||
print('"{}","{}"'.format(rules[idx].id, rules[idx].message()))
|
||||
|
||||
output_formats = {
|
||||
'rules': print_rules,
|
||||
'logstash_translate_dictionary': print_translate_dictionary,
|
||||
}
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(usage='%(prog)s [options]', description=DESCRIPTION, epilog=EPILOG, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument('-r', '--rule', default='', help='source rule to parse')
|
||||
parser.add_argument('-o', '--output', default='', help='path to the output file')
|
||||
parser.add_argument('-f', '--format', default='rules', help='format of the output file ({})'.format(','.join(list(output_formats.keys()))))
|
||||
parser.add_argument('-b', '--begin-id', default=0, type=int, help='rebase all rules ids from this id number (id must be > 100)')
|
||||
args = parser.parse_args()
|
||||
|
||||
if len(sys.argv) == 1 or \
|
||||
len(args.rule) < 1 or \
|
||||
len(args.output) < 1 or \
|
||||
len(args.format) < 1 or \
|
||||
args.format not in output_formats or \
|
||||
(args.begin_id != 0 and args.begin_id <= 100):
|
||||
parser.print_help(sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
rules = {}
|
||||
whitelists = []
|
||||
|
||||
parse_file(args.rule, rules, whitelists, args.begin_id)
|
||||
|
||||
rules_ids = list(rules.keys())
|
||||
rules_ids.sort()
|
||||
|
||||
file_format = output_formats[args.format]
|
||||
|
||||
fd = sys.stdout
|
||||
if args.output != '-':
|
||||
sys.stdout = open(args.output, 'w')
|
||||
|
||||
file_format(rules_ids, rules, whitelists)
|
||||
|
||||
if args.output != '-':
|
||||
sys.stdout.close()
|
||||
sys.stdout = fd
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
# -* coding: utf-8
|
||||
# SPDX-FileCopyrightText: 2022 Alex <alex@staticlibs.net>
|
||||
# SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
import os, shutil, sys
|
||||
from os import path
|
||||
|
||||
if __name__ == "__main__":
|
||||
if 2 != len(sys.argv):
|
||||
print("ERROR: The path to Nginx dist must be specified as a first and only argument")
|
||||
sys.exit(1)
|
||||
src = sys.argv[1]
|
||||
if not path.isdir(src):
|
||||
print("ERROR: Nginx dist not found on the specified path: [{}]".format(src))
|
||||
sys.exit(1)
|
||||
scripts_dir = path.dirname(__file__)
|
||||
naxsi_dir = path.dirname(scripts_dir)
|
||||
nginx_dir = path.join(naxsi_dir, "nginx-tmp")
|
||||
if path.exists(nginx_dir):
|
||||
shutil.rmtree(nginx_dir)
|
||||
os.mkdir(nginx_dir)
|
||||
shutil.copytree(path.join(src, "conf"), path.join(nginx_dir, "conf"))
|
||||
os.remove(path.join(nginx_dir, "conf", "nginx.conf"))
|
||||
shutil.copytree(path.join(src, "logs"), path.join(nginx_dir, "logs"))
|
||||
shutil.copytree(path.join(src, "temp"), path.join(nginx_dir, "temp"))
|
||||
os.mkdir(path.join(nginx_dir, "html"))
|
||||
with open(path.join(nginx_dir, "html", "index.html"), "w", encoding="utf-8") as file:
|
||||
file.write("<html>Hello Nginx!<html>")
|
||||
shutil.copy(path.join(src, "nginx.exe"), nginx_dir)
|
||||
print("Test Nginx dist prepared successfully");
|
||||
Reference in New Issue
Block a user