Skip to content

Commit 076d834

Browse files
authored
Merge pull request #236 from cawalch/tests/scanner-coverage
Adds Scanner Test Coverage and CI/CD Step
2 parents 7ce62bf + 70af1c4 commit 076d834

File tree

10 files changed

+251
-1
lines changed

10 files changed

+251
-1
lines changed

.github/workflows/pr-actions.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: pr-actions
2+
on: [pull_request]
3+
4+
jobs:
5+
backend-strelka-test:
6+
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- uses: actions/checkout@v3
11+
- uses: actions/setup-python@v4
12+
with:
13+
14+
python-version: '3.x'
15+
16+
architecture: 'x64'
17+
- name: Install dependencies
18+
run: |
19+
sudo apt-get -q update
20+
sudo apt-get install --no-install-recommends -qq automake \
21+
build-essential \
22+
libfuzzy-dev \
23+
gcc \
24+
git \
25+
libarchive-dev \
26+
libmagic-dev \
27+
libssl-dev \
28+
libzbar0 \
29+
libgl1 \
30+
python3-setuptools \
31+
libgmp-dev \
32+
libpcap-dev \
33+
libbz2-dev \
34+
libgomp1 \
35+
python3-dev \
36+
python3-wheel \
37+
mupdf-tools \
38+
mupdf \
39+
libglu1-mesa \
40+
libtool \
41+
pkg-config \
42+
swig \
43+
tesseract-ocr
44+
python -m pip install --upgrade pip
45+
pip install validators setuptools --upgrade
46+
pip install --no-cache-dir -r src/python/requirements.txt
47+
- name: Test with pytest
48+
run: |
49+
pytest
50+
51+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ __pycache__/
99
# Distribution / packaging
1010
.Python
1111
develop-eggs/
12+
src/python/build
1213
dist/
1314
downloads/
1415
eggs/

docs/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Strelka differs from its sibling projects in a few significant ways:
5353
* [Protobuf](#protobuf)
5454
* [Scanners](#scanners)
5555
* [Scanner List](#scanner-list)
56+
* [Tests](#tests)
57+
* [Setup](#tests-setup)
5658
* [Use Cases](#use-cases)
5759
* [Contributing](#contributing)
5860
* [Related Projects](#related-projects)
@@ -594,6 +596,46 @@ The table below describes each scanner and its options. Each scanner has the hid
594596
| ScanZip | Extracts files from zip archives | "limit" -- maximum number of files to extract (defaults to 1000)<br>"password_file" -- location of passwords file for zip archives (defaults to etc/strelka/passwords.txt) |
595597
| ScanZlib | Decompresses gzip files | N/A
596598

599+
## Tests
600+
As Strelka consists of many scanners and dependencies for those scanners, Pytests are particularly valuable for testing the ongoing functionality of Strelka and it's scanners. Tests allow users to write test cases that verify the correct behavior of Strelka scanners to ensure that the scanners remain reliable and accurate. Additionally, using pytests can help streamline the development process, allowing developers to focus on writing new features and improvements for the scanners. The following section details how to setup Pytests.
601+
602+
If using Strelka on Github, this repository supports Github Actions which runs on Pull Requests
603+
604+
### Tests Setup
605+
Here are the steps for setting up a virtualenv virtual environment, installing requirements from src/python/requirements.txt, and running pytest:
606+
607+
1. Install virtualenv, if it is not already installed:
608+
609+
```
610+
pip install virtualenv
611+
```
612+
2. Create a new virtual environment:
613+
614+
```
615+
virtualenv <environment-name>
616+
```
617+
618+
3. Activate the virtual environment:
619+
620+
```
621+
source <environment-name>/bin/activate
622+
```
623+
624+
4. Install the requirements from src/python/requirements.txt:
625+
626+
```
627+
pip install -r src/python/requirements.txt
628+
```
629+
630+
5. Run pytest to execute the test cases:
631+
632+
```
633+
pytest
634+
```
635+
636+
Upon execution, you will be provided the successes and failures for any available scanner test.
637+
638+
597639
## Use Cases
598640
Below are some select use cases that show the value Strelka can add to a threat detection tech stack. Keep in mind that these results are parsed in real time without post-processing and are typically correlated with other detection/response tools (e.g. Bro, Volatility, etc.). The file metadata shown below was derived from files found in [VirusShare](https://virusshare.com/) torrent no. 323 and from a test file in the [MaliciousMacroBot (MMBot) repository](https://github.com/egaus/MaliciousMacroBot).
599641

src/python/requirements.txt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
arc4==0.0.4
2+
beautifulsoup4==4.9.3
3+
boltons==20.2.1
4+
construct==2.10.67
5+
cryptography==3.4.7
6+
docker==5.0.0
7+
esprima==4.0.1
8+
eml-parser>=1.17
9+
git+https://github.com/jshlbrd/python-entropy.git # v0.11 as of this freeze (package installed as 'entropy')
10+
html5lib==1.1
11+
inflection==0.5.1
12+
jsbeautifier==1.13.13
13+
libarchive-c==2.9
14+
lief==0.12.3
15+
lxml==4.9.1
16+
M2Crypto==0.38.0
17+
nested-lookup==0.2.22
18+
numpy==1.22.1
19+
olefile==0.46
20+
oletools==0.56.1
21+
opencv-python==4.6.0.66
22+
opencv-contrib-python==4.6.0.66
23+
pefile==2019.4.18
24+
pgpdump3==1.5.2
25+
pyelftools==0.27
26+
pygments==2.9.0
27+
pylzma==0.5.0
28+
pytesseract==0.3.7
29+
python-docx==0.8.10
30+
python-magic==0.4.22
31+
py-tlsh==4.7.2
32+
pyyaml>=5.4.1
33+
pyzbar==0.1.8
34+
pytz>=2022.1
35+
rarfile==4.0
36+
redis==3.5.3
37+
requests==2.25.1
38+
rpmfile==1.0.8
39+
signify==0.3.0
40+
speakeasy-emulator==1.5.2
41+
ssdeep==3.4
42+
tldextract==3.1.0
43+
tnefparse==1.4.0
44+
validators==0.18.2
45+
xmltodict==0.12.0
46+
pytest==7.2.0
47+
pytest-mock==3.10.0

src/python/strelka/strelka.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class Scanner(object):
9292
This is referenced in the scanner metadata.
9393
key: String that contains the scanner's metadata key.
9494
This is used to identify the scanner metadata in scan results.
95+
event: Dictionary containing the result of scan
9596
backend_cfg: Dictionary that contains the parsed backend configuration.
9697
scanner_timeout: Amount of time (in seconds) that a scanner can spend
9798
scanning a file. Can be overridden on a per-scanner basis
@@ -104,6 +105,7 @@ def __init__(self, backend_cfg, coordinator):
104105
self.key = inflection.underscore(self.name.replace('Scan', ''))
105106
self.scanner_timeout = backend_cfg.get('limits').get('scanner')
106107
self.coordinator = coordinator
108+
self.event = dict()
107109
self.iocs = []
108110
self.type = IocOptions
109111
self.extract = TLDExtract(suffix_list_urls=None)
@@ -159,7 +161,7 @@ def scan_wrapper(self,
159161
start = time.time()
160162
self.files = []
161163
self.flags = []
162-
self.event = {}
164+
self.event = dict()
163165
self.scanner_timeout = options.get('scanner_timeout',
164166
self.scanner_timeout)
165167

src/python/strelka/tests/__init__.py

Whitespace-only changes.
3.73 KB
Loading
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import datetime
2+
from strelka.scanners.scan_footer import ScanFooter
3+
4+
5+
def test_scan_footer():
6+
"""
7+
This tests the ScanFooter scanner.
8+
It attempts to validate the extraction of a string from a file's content.
9+
10+
Pass: File is loaded, scanned, and footer value "mcee" is successfully extracted.
11+
Failure: Unable to load, scan, or extract value "mcee"
12+
"""
13+
14+
scanner = ScanFooter(
15+
{
16+
"name": "ScanFooter",
17+
"key": "scan_footer",
18+
"limits": {"scanner": 10},
19+
},
20+
"test_coordinate",
21+
)
22+
scanner.scan_wrapper(
23+
"foo bar mcee",
24+
{"uid": "12345", "name": "somename"},
25+
{"length": 4, "scanner_timeout": 5},
26+
datetime.date.today(),
27+
)
28+
assert scanner.event.get("footer") == "mcee"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import datetime
2+
from pathlib import Path
3+
from strelka.scanners.scan_gif import ScanGif
4+
5+
6+
def test_scan_gif(mocker):
7+
"""
8+
This tests the ScanGif scanner.
9+
It attempts to validate a given GIFs "trailer index" value.
10+
11+
Pass: Trailer index matches specified value.
12+
Failure: Unable to load file or trailer index does not match specified value.
13+
"""
14+
15+
scanner = ScanGif(
16+
{
17+
"name": "ScanGif",
18+
"key": "scan_gif",
19+
"limits": {"scanner": 10}
20+
},
21+
"test_coordinate",
22+
)
23+
24+
mocker.patch.object(ScanGif, "upload_to_coordinator", return_value=None)
25+
scanner.scan_wrapper(
26+
Path(Path(__file__).parent / "fixtures/test.gif").read_bytes(),
27+
{
28+
"uid": "12345",
29+
"name": "somename"
30+
},
31+
{
32+
"scanner_timeout": 5
33+
},
34+
datetime.date.today(),
35+
)
36+
assert scanner.event.get("trailer_index") == 3806
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import pytest
2+
import datetime
3+
from strelka.scanners.scan_url import ScanUrl
4+
5+
scanner = ScanUrl(
6+
{
7+
"name": "ScanUrl",
8+
"key": "scan_url",
9+
"limits": {"scanner": 10}
10+
},
11+
"test_coordinate",
12+
)
13+
14+
tests = [
15+
(b"some othervalue foo", []),
16+
(b"http://foobar.test.com", [b"http://foobar.test.com"]),
17+
(b"foo http://foobar.test.com bar", [b"http://foobar.test.com"]),
18+
(b"http://\n", []),
19+
(b"noschema.foo\n", [b"noschema.foo"]),
20+
]
21+
22+
23+
@pytest.mark.parametrize("data,expected", tests)
24+
def test_scan_simple_url(data, expected):
25+
"""
26+
This tests the ScanURL scanner.
27+
It attempts to validate the extraction of several URLs against
28+
their URLs extracted from the ScanURL scanner.
29+
30+
Pass: All URLs successfully extracted or tests passed.
31+
Failure: Unable to extract URLs successfully or extracts undefined URLs.
32+
"""
33+
34+
scanner.scan_wrapper(
35+
data,
36+
"somefile.foo",
37+
{
38+
"length": 4,
39+
"scanner_timeout": 5
40+
},
41+
datetime.date.today(),
42+
)
43+
assert scanner.event.get("urls") == expected

0 commit comments

Comments
 (0)