Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added scripts for testing functionality #306

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Maldet Tests

### Concept:

1. Create a framework for tests that can be run to confirm or deny the effectiveness of changes that were made.
2. Create tests for the most recent pull requests that conform to the rules laid out herein, both to confirm the effectiveness of those pull requests, and to serve as examples for the creation of future tests.
3. Over time grow a body of historical tests that will be able to be run against future versions of Linux Malware Detect in order increase awareness of situations where new environments or recent updates have broken historical functionality.

### Test Rules:

1. The script "/usr/local/maldetect/tests/test.sh" will iterate through all of the test scripts that follow the naming conventions (see below). Optionally, test.sh can be given an argument of the name of one or more specific test scripts to run (whether or not the meet the naming conventions).
2. The "test.sh" script will export the following variables to be used by the test scripts that it runs:
- $d\_MALDET - The directory "/usr/local/maldetect"
- $d\_MALDET\_TESTS - The directory "/usr/local/maldetect/tests"
- $d\_MALDET\_TESTS\_WORKING - The directory "/usr/local/maldetect/tests/working"
3. The test scripts should only create, modify, or destroy files within $d\_MALDET\_TESTS\_WORKING.
- Typical functions of Maldet may, of course, modify other files outside of this.
4. If the tests within a test script are considered to have failed, they need to exit with "exit 1" or some other non-zero exit code. test.sh will stop when it encounters a test that exited in this manner.
5. The tests scripts should not be executable; test.sh will toggle their executability as needed.
6. It is preferred when ever possible that the tests actually run the executable they are testing (usually "maldet"). If this is not possible, copying files from the project and modifying them in-line is the preferred alternative.
- Under such circumstances, the test script must give warning output indicating that such is the case.
- See tests_000299.sh for an example of this.
7. There is no expectation that every slightest change be tested.
- Example: The changes from pull request 300 are simple, self-evident, and should work across all versions of bash; writing a test for them seems silly.
- Example: Functionality unchanged from version 1.6.2 or before has been used heavily enough that if there are issues, it is likely that they either would have come to light by now, or are so edge-case that tests might fail to capture them anyway.
- This all being said, testing changes really is a best practice

### Conventions:

1. File Names: Test scripts should be named like the following: "tests_[NUMBER].[EXTENSION]"
- In the instances where the change is associated with a pull request or an issue, the number will be the number of that pull request or issue represented in six digits with leading zeros.
- Because the pull request number cannot be known for certain beforehand, it is acceptable when submitting pull requests that include a test script to give it any name beginning with "tests_". This will be changed at a later date to match the format specified above.
- In the instances where the change is NOT associated with a pull request or an issue, the number will be the date on which the commit was made in eight digit "YYYYMMDD" format, optionally with an underscore and more numbers following (if multiple test files need to be added on a single date).
- The purpose of this naming system is to make it easy to go back through the git history to find more details on what the test is supposed to be testing against.
2. Colored Output: When possible, colored output should be used under the following circumstances:
- Blue (\e[34m) at the beginning of the test for a brief description of what's being tested.
- Yellow (\e[33m) to indicate soft-fails or important information that the user needs to be aware of.
- Green (\e[32m) to indicate success.
- Red (\e[31m) to indicate hard failures or critical warnings.
112 changes: 112 additions & 0 deletions tests/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#! /bin/bash

#====================================#
#== Initial Variables and Settings ==#
#====================================#

set -e

export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

export d_MALDET="/usr/local/maldetect"
export d_MALDET_TESTS="$d_MALDET"/tests
export d_MALDET_TESTS_WORKING="$d_MALDET_TESTS"/working

b_SPECIFIC=false

#===============#
#== Functions ==#
#===============#

function fn_run_test {
local f_TEST=
### Make sure that we have everything in place necessary to run a test
if [[ -n "$1" && -n $d_MALDET && -n "$d_MALDET_TESTS" && -n "$d_MALDET_TESTS_WORKING" ]]; then
f_TEST="$1"
else
echo "Test file does not exist or required variables not set" > /dev/stderr
fn_unskip
exit 1
fi

### Destroy and create the working directory
if [[ -d "$d_MALDET_TESTS_WORKING" ]]; then
rm -rf "$d_MALDET_TESTS_WORKING"
fi
mkdir "$d_MALDET_TESTS_WORKING"

### Make the test executable
if [[ ! -x "$d_MALDET_TESTS"/"$f_TEST" ]]; then
chmod +x "$d_MALDET_TESTS"/"$f_TEST"
fi

### Run the test script
v_SUCCESS=true
echo -e "\e[34mRunning tests from file '$f_TEST'\e[00m"
"$d_MALDET_TESTS"/"$f_TEST" || v_SUCCESS=false

### Make the test no longer executable
chmod -x "$d_MALDET_TESTS"/"$f_TEST"

### Exit if the test failed
if [[ "$v_SUCCESS" == false ]]; then
### If the script failed, exit
echo -e "\e[31mFile '$f_TEST': Failed\e[00m" > /dev/stderr
if [[ "$b_SPECIFIC" == false ]]; then
echo "To skip these tests, add the flags '--skip $f_TEST'"
fi
fn_unskip
exit 1
fi
echo -e '\e[32mFile '$f_TEST': Success!\e[00m'
}

function fn_unskip {
local v_NAME=
for i in $( ls -1 "$d_MALDET_TESTS" | grep -E "^skipped_tests_[0-9_]+\.[^.]+$" ); do
v_NAME="$( echo "$i" | sed "s/^skipped_//" )"
mv -f "$d_MALDET_TESTS"/"$i" "$d_MALDET_TESTS"/"$v_NAME"
done
}

#=====================#
#== Parse Arguments ==#
#=====================#

echo "Warning: It's possible that running these tests while there are ongoing scans or monitoring jobs might negatively impact both the ongoing processes and the results of this test."
read -ep "Are you sure that you want to continue? (Y/n) " v_CONTINUE
v_CONTINUE="${v_CONTINUE:0:1}"
if [[ "$v_CONTINUE" == "n" || "$v_CONTINUE" == "N" ]]; then
exit 0
fi

fn_unskip
if [[ -n "$1" && "$1" != "--skip" ]]; then
### Run a single test script
b_SPECIFIC=true
while [[ -n "$1" ]]; do
if [[ -f "$d_MALDET_TESTS"/"$1" ]]; then
fn_run_test "$1"
fi
shift
done
exit 0
else
### Skip any tests that were specified to be skipped
if [[ "$1" == "--skip" ]]; then
shift
while [[ -n "$1" ]]; do
if [[ -f "$d_MALDET_TESTS"/"$1" && $( echo "$1" | grep -Ec "^tests_[0-9_]+\.[^.]+$" ) -gt 0 ]]; then
mv -f "$d_MALDET_TESTS"/"$1" "$d_MALDET_TESTS"/skipped_"$1"
fi
shift
done
fi

### Iterate through all of the test scripts
for i in $( ls -1 "$d_MALDET_TESTS" | sort -n | grep -E "^tests_[0-9_]+\.[^.]+$" ); do
### Announce the set of tests that we're running
fn_run_test "$i"
done
exit 0
fi
120 changes: 120 additions & 0 deletions tests/tests_000292.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#! /bin/bash

echo -e "\e[34mTesting '--monitor' and tlog functionality\e[00m"

v_INOTIFY="$( which inotifywait 2> /dev/null || true )"
if [[ -z "$v_INOTIFY" ]]; then
echo -e "\e[33mCommand 'inotifywait' not present. Skipping tests.\e[00m"
exit 0
fi

echo "Note: Instead of using the maldet executable, this test copies and makes modifications to it and other files. If this test fails, it might be a result of unexpected future changes to the following:"
echo "'$d_MALDET/maldet'"
echo "'$d_MALDET/internals/functions"

function fn_warn_maldet {
echo
echo -e "\e[33mWARNING: There is potentially an active maldet monitoring job ongoing. The above output will tell you whether or not this is the case. You will likely want to kill this with the following command:\e[00m"
echo "$d_MALDET_TESTS_WORKING/maldet -k"
echo
exit 1
}

### Make a copy of the files we need to change
cp -a "$d_MALDET"/maldet "$d_MALDET_TESTS_WORKING"/maldet
cp -a "$d_MALDET"/internals/functions "$d_MALDET_TESTS_WORKING"/functions

### Modify maldet executable to point to the modified functions file
sed -i '/source $intfunc/i intfunc="'"$d_MALDET_TESTS_WORKING/functions\"" "$d_MALDET_TESTS_WORKING"/maldet
### Modify functions file to set specific variables during the monitoring cycle
sed -i '/^monitor_cycle[(][)] {/a d_TEST="'"$d_MALDET_TESTS_WORKING"'"; inotify_log="$d_TEST"/inotify_log; inotify_trim=1100' "$d_MALDET_TESTS_WORKING"/functions
### Modify functions file to set specific variables when monitoring is initiated
sed -i '/^monitor_init[(][)] {/a d_TEST="'"$d_MALDET_TESTS_WORKING"'"; inotify_log="$d_TEST"/inotify_log' "$d_MALDET_TESTS_WORKING"/functions
### Truncate the monitor check process - we don't need all of it
sed -i '/monitor_scanlist=/a monitor_scanlist="'"$d_MALDET_TESTS_WORKING"'"/scan_list' "$d_MALDET_TESTS_WORKING"/functions
sed -i '/$tlog $inotify_log/a '"}\n\nsomething_else() {" "$d_MALDET_TESTS_WORKING"/functions
### Modify functions file allow the monitoring cycle to be triggered on demand
sed -i '/sleep $inotify_sleep/i while [ ! -f "$d_TEST"/loop ]; do inotify_sleep=1' "$d_MALDET_TESTS_WORKING"/functions
sed -i '/sleep $inotify_sleep/a done; rm -f "$d_TEST"/loop' "$d_MALDET_TESTS_WORKING"/functions
### Modify functions file to set a new file for inotify paths
sed -i '/inotify_fpaths="$sessdir\/inotify.paths.$$"/a inotify_fpaths="$d_TEST"/inotify.paths.12345' "$d_MALDET_TESTS_WORKING"/functions

### Make the directory that inotify will be monitoring:
mkdir "$d_MALDET_TESTS_WORKING"/monitor_test

### Clear the tlog inotify file
rm -f "$d_MALDET"/tmp/inotify

echo
### Start the inotify job
v_MALDET_RUN=true
"$d_MALDET_TESTS_WORKING"/maldet -m "$d_MALDET_TESTS_WORKING"/monitor_test || v_MALDET_RUN=false
echo

### Did maldet error out?
if [[ $v_MALDET_RUN == false ]]; then
echo "Maldet either failed to run, or exited with a non-zero exit code" > /dev/stderr
fn_warn_maldet
fi

### Did the inotify job start?
if [[ ! -f "$d_MALDET_TESTS_WORKING"/inotify_log ]]; then
echo "Either the inotify job failed to start, or the attempts to modify the functions file failed" > /dev/stderr
fn_warn_maldet
fi

### Modify a few files:
v_COUNT=0
echo -n "$v_COUNT" > "$d_MALDET_TESTS_WORKING"/monitor_test/foo
echo -n "$v_COUNT" > "$d_MALDET_TESTS_WORKING"/monitor_test/foobar

### Make sure the modifications were seen
sleep 1
v_TEST="$( grep -c "$d_MALDET_TESTS_WORKING/monitor_test/foo" "$d_MALDET_TESTS_WORKING"/inotify_log )"
if [[ "$v_TEST" -eq 0 ]]; then
echo "inotify job failed to recognize changes" > /dev/stderr
fn_warn_maldet
fi

### Modify a lot of files
echo "This may take a while"
while [[ "$( wc -l "$d_MALDET_TESTS_WORKING"/inotify_log | cut -d " " -f1 )" -lt 1200 ]]; do
echo -n "$v_COUNT" > "$d_MALDET_TESTS_WORKING"/monitor_test/bar
v_COUNT=$(( $v_COUNT + 1 ))
done
echo -n "$v_COUNT" > "$d_MALDET_TESTS_WORKING"/monitor_test/foo
echo -n "$v_COUNT" > "$d_MALDET_TESTS_WORKING"/monitor_test/foobar

### count the lines, initiate a loop, then count the lines again
v_LINES="$( wc -l "$d_MALDET_TESTS_WORKING"/inotify_log | cut -d " " -f1 )"
touch "$d_MALDET_TESTS_WORKING"/loop
sleep 2
v_LINES2="$( wc -l "$d_MALDET_TESTS_WORKING"/inotify_log | cut -d " " -f1 )"

### Test if the file length decreased
if [[ "$v_LINES" -le "$v_LINES2" ]]; then
echo "Failed to trim inotify log" > /dev/stderr
fn_warn_maldet
fi

### Test that the most recent changes are present
v_TEST1="$( tail -n6 "$d_MALDET_TESTS_WORKING"/inotify_log | grep -c "monitor_test/foo[[:blank:]]" )"
v_TEST2="$( tail -n6 "$d_MALDET_TESTS_WORKING"/inotify_log | grep -c "monitor_test/foobar[[:blank:]]" )"
if [[ "$v_TEST1" -lt 1 || "$v_TEST2" -lt 1 ]]; then
echo "Most recent changes are not present. Are we trimming from the end of the file?" > /dev/stderr
fn_warn_maldet
fi

### Test that the tlog file has the correct number of characters listed
v_CHARS="$( wc -c "$d_MALDET_TESTS_WORKING"/inotify_log | cut -d " " -f1 )"
v_CHARS2="$( cat "$d_MALDET"/tmp/inotify 2> /dev/null || true )"
if [[ "$v_CHARS" != "$v_CHARS2" ]]; then
echo "tlog file was not updated correctly" > /dev/stderr
fn_warn_maldet
fi

### Test that it was successfully killed
v_INOTIFY_PID="$( pgrep -f inotify.paths.[0-9]+ )"
kill -9 "$v_INOTIFY_PID"


68 changes: 68 additions & 0 deletions tests/tests_000299.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#! /bin/bash

echo -e "\e[34mTesting order of command line arguments\e[00m"

echo "Note: Instead of using the maldet executable, this test copies and makes modifications to it and other files. If this test fails, it might be a result of unexpected future changes to the following:"
echo "'$d_MALDET/maldet'"
echo "'$d_MALDET/internals/functions"

### Make a copy of the files we need to change
cp -a "$d_MALDET"/maldet "$d_MALDET_TESTS_WORKING"/maldet
cp -a "$d_MALDET"/internals/functions "$d_MALDET_TESTS_WORKING"/functions

### Modify maldet executable to print the word "background" and exit if background mode is invoked
sed -i '/-b|--background)/a echo "background";exit' "$d_MALDET_TESTS_WORKING"/maldet
### Modify maldet executable to point to the modified functions file
sed -i '/source $intfunc/i intfunc="'"$d_MALDET_TESTS_WORKING/functions\"" "$d_MALDET_TESTS_WORKING"/maldet
### Modify functions file so that the scan function prints the word "scan" and immediately exits
sed -i '/^scan[(][)] {/a echo "scan";exit' "$d_MALDET_TESTS_WORKING"/functions
### Modify functions file so that the sigup function prints the word "update" and immediately exits
sed -i '/^sigup[(][)] {/a echo "update";exit' "$d_MALDET_TESTS_WORKING"/functions
### Modify functions file so that the usage_long function prints the word "help" and immediately exits
sed -i '/^usage_long[(][)] {/a echo "help";exit' "$d_MALDET_TESTS_WORKING"/functions

### Run the modified scripts and see if they behave as expected
### Just "-a" launches a scan
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a /home/user/public_html | tail -n1 )"
if [[ $v_TEST != "scan" ]]; then
echo "Test 1 failed" > /dev/stderr
exit 1
fi
### "-u" at any position, with or without "--force" starts and update before the scan
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -u -a /home/user/public_html | tail -n1 )"
if [[ $v_TEST != "update" ]]; then
echo "Test 2 failed" > /dev/stderr
exit 1
fi
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a /home/user/public_html -u | tail -n1 )"
if [[ $v_TEST != "update" ]]; then
echo "Test 3 failed" > /dev/stderr
exit 1
fi
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -u --force -a /home/user/public_html | tail -n1 )"
if [[ $v_TEST != "update" ]]; then
echo "Test 4 failed" > /dev/stderr
exit 1
fi
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a /home/user/public_html -u --force | tail -n1 )"
if [[ $v_TEST != "update" ]]; then
echo "Test 5 failed" > /dev/stderr
exit 1
fi
### "--help" at any position launches help before anything else
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a --help /home/user/public_html -u --force | tail -n1 )"
if [[ $v_TEST != "help" ]]; then
echo "Test 6 failed" > /dev/stderr
exit 1
fi
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a /home/user/public_html -u --force --help | tail -n1 )"
if [[ $v_TEST != "help" ]]; then
echo "Test 7 failed" > /dev/stderr
exit 1
fi
### "-b" updates whether we're in the background even if it comes after "-a"
v_TEST="$( "$d_MALDET_TESTS_WORKING"/maldet -a /home/user/public_html -u --force -b | tail -n1 )"
if [[ $v_TEST != "background" ]]; then
echo "Test 8 failed" > /dev/stderr
exit 1
fi
45 changes: 45 additions & 0 deletions tests/tests_000304.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#! /bin/bash

echo -e "\e[34mTesting parsing of '--config-option' argument\e[00m"

echo "Note: Instead of using the maldet executable, this test copies and modifies it. If this test fails, it might be a result of unexpected future changes to '$d_MALDET/maldet'"

### Make a copy of the maldet executable
cp -a "$d_MALDET"/maldet "$d_MALDET_TESTS_WORKING"/maldet

### Find the line number for the "-co" / "--config-option" argument
v_CO_LINE="$( grep -En "^[[:blank:]]*-co" "$d_MALDET_TESTS_WORKING"/maldet | cut -d ":" -f1 )"
### Find the line number for where the next argument begins
v_NEXT_ARG_LINE="$( grep -En "^[[:blank:]]*(-|esac)" "$d_MALDET_TESTS_WORKING"/maldet | cut -d ":" -f1 | grep -A1 "^$v_CO_LINE" | tail -n1 )"

### Find the line number for the last time that we write something to "$tmpco" while processing the "-co" / "--config-option" argument
v_TMPCO_LINE=
for i in $( grep -En '> ?"?\$tmpco' "$d_MALDET_TESTS_WORKING"/maldet | cut -d ":" -f1 ); do
if [[ "$i" -gt "$v_CO_LINE" && "$i" -lt "$v_NEXT_ARG_LINE" ]]; then
v_TMPCO_LINE="$i"
elif [[ "$i" -ge "$v_NEXT_ARG_LINE" ]]; then
break
fi
done

if [[ -z "$v_TMPCO_LINE" ]]; then
echo 'Was not able to find file "$tmpco" being written to while processing the argument "-co" / "--config-option"' > /dev/stderr
exit 1
fi

### Modify maldet so that it will print the file and then exit rather than deleting the file and moving forward
sed -i "$(( $v_TMPCO_LINE + 1 ))i"'cat $tmpco; exit' "$d_MALDET_TESTS_WORKING"/maldet

### Create an import the temp conf file
v_CONF=scan_tmpdir_paths="/tmp /var/tmp /dev/shm",inotify_docroot="public_html,public_ftp",md5sum="/sbin/md5",MONITOR_MODE="users",compatcnf="/home/user/malicious_executable.sh"
"$d_MALDET_TESTS_WORKING"/maldet -co "$v_CONF" > "$d_MALDET_TESTS_WORKING"/source
. "$d_MALDET_TESTS_WORKING"/source

### Test the variables that were imported
if [[ "$scan_tmpdir_paths" != "/tmp /var/tmp /dev/shm" || "$inotify_docroot" != "public_html,public_ftp" || "$md5sum" != "/sbin/md5" || "$MONITOR_MODE" != "users" || -n "$compatcnf" ]]; then
echo 'Failed to parse "-co" arguments correctly' > /dev/stderr
exit 1
fi

exit 0

Loading