Skip to content

Commit 02ae5c3

Browse files
authored
Merge pull request #279 from vlvkobal/test-python
2 parents 4959ea3 + 1016ecf commit 02ae5c3

36 files changed

+3290
-0
lines changed

.github/workflows/python-test.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Python - Test
2+
3+
on:
4+
push:
5+
paths:
6+
- '.github/workflows/python-test.yml'
7+
- 'python/**'
8+
pull_request:
9+
paths:
10+
- '.github/workflows/python-test.yml'
11+
- 'python/**'
12+
13+
jobs:
14+
run-python-tests:
15+
name: Run Python tests
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Install required packages
21+
run: |
22+
sudo add-apt-repository ppa:wireshark-dev/stable
23+
sudo apt update
24+
sudo apt upgrade -y
25+
sudo apt -y install \
26+
tshark \
27+
python3-pytest
28+
29+
- name: Run tests
30+
run: pytest
31+
working-directory: python/test
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/bin/bash
2+
3+
# This script generates output files for the JA4 Python tests.
4+
# Run the script from its directory.
5+
6+
PCAP_DIR="../../pcap"
7+
OUT_DIR="./testdata"
8+
JA4_SCRIPT="../ja4.py"
9+
10+
mkdir -p "$OUT_DIR"
11+
12+
# If arguments are given, use them as files; otherwise, use all files in $PCAP_DIR
13+
if [ "$#" -gt 0 ]; then
14+
PCAP_FILES=("$@")
15+
else
16+
PCAP_FILES=("$PCAP_DIR"/*.pcap*)
17+
fi
18+
19+
# Loop through each pcap file and generate the output
20+
for pcap in "${PCAP_FILES[@]}"; do
21+
base=$(basename "$pcap")
22+
out="$OUT_DIR/${base}.json"
23+
python3 "$JA4_SCRIPT" "$pcap" -J -f "$out"
24+
done

python/test/test_ja4_output.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import json
2+
import subprocess
3+
import sys
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
SCRIPT_DIR = Path(__file__).resolve().parent
9+
ROOT_DIR = SCRIPT_DIR.parent.parent
10+
11+
PCAP_DIR = ROOT_DIR / "pcap"
12+
EXPECTED_DIR = SCRIPT_DIR / "testdata"
13+
JA4_SCRIPT = ROOT_DIR / "python" / "ja4.py"
14+
15+
pcap_files = sorted(PCAP_DIR.rglob("*.pcap*"))
16+
if not pcap_files:
17+
pytest.fail(f"No PCAP files found in {PCAP_DIR.resolve()}")
18+
19+
20+
def get_expected_output(pcap_file: Path):
21+
expected_file = EXPECTED_DIR / f"{pcap_file.name}.json"
22+
with expected_file.open() as f:
23+
return json.load(f)
24+
25+
26+
@pytest.mark.parametrize("pcap_file", pcap_files)
27+
def test_ja4_output_matches_expected(pcap_file, tmp_path):
28+
output_file = tmp_path / f"{pcap_file.name}.json"
29+
result = subprocess.run(
30+
[
31+
sys.executable,
32+
str(JA4_SCRIPT),
33+
str(pcap_file),
34+
"-J",
35+
"-f",
36+
str(output_file),
37+
],
38+
stdout=subprocess.PIPE,
39+
stderr=subprocess.PIPE,
40+
text=True,
41+
)
42+
43+
assert result.returncode == 0, f"ja4.py failed: {result.stderr}"
44+
45+
actual = json.loads(output_file.read_text())
46+
expected = get_expected_output(pcap_file)
47+
48+
assert actual == expected, f"Mismatch for {pcap_file.name}"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"stream": 0,
4+
"src": "192.168.235.136",
5+
"dst": "192.168.235.1",
6+
"srcport": "8089",
7+
"dstport": "53649",
8+
"JA4H": "ge11nn07ruru_6cd0fb54989b_000000000000_000000000000"
9+
},
10+
{
11+
"stream": 1,
12+
"src": "192.168.235.136",
13+
"dst": "192.168.235.1",
14+
"srcport": "8089",
15+
"dstport": "53656",
16+
"JA4H": "ge11nr06ruru_cc6ec9a91856_000000000000_000000000000"
17+
}
18+
]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[
2+
{
3+
"stream": 0,
4+
"src": "172.130.128.76",
5+
"dst": "54.226.182.138",
6+
"srcport": "55318",
7+
"dstport": "443",
8+
"client_ttl": "64",
9+
"server_ttl": "238",
10+
"JA4L-S": "781_238",
11+
"JA4L-C": "2181_64",
12+
"domain": "bad.curveballtest.com",
13+
"JA4.1": "t00d1715h2_dd2c26892b57_8201b1be11a4",
14+
"JA4_r.1": "t00d1715h2_,,,171,172,195,196,199,200,392,393,6,65,66,67,690,7_0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,ff01_27,52,25,83,53,81,54,37,3",
15+
"JA4_o.1": "t00d1715h2_6c4ba73770eb_4848efb2a220",
16+
"JA4_ro.1": "t00d1715h2_690,65,66,67,195,199,196,200,393,392,171,172,6,7,,,_0000,0017,ff01,000a,000b,0023,0010,0005,000d,0012,0033,002d,002b,001b,0015_27,52,25,83,53,81,54,37,3",
17+
"JA4S": "t0005h1_195_845f7282a956",
18+
"JA4X.1": "2e9214a636bc_a373a9f83c6b_0e17604154c5",
19+
"JA4X.2": "2e9214a636bc_2e9214a636bc_795797892f9c"
20+
}
21+
]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[
2+
{
3+
"stream": 0,
4+
"src": "172.27.7.31",
5+
"dst": "13.107.21.239",
6+
"srcport": "54524",
7+
"dstport": "443",
8+
"client_ttl": "128",
9+
"server_ttl": "112",
10+
"JA4L-S": "1907_112",
11+
"JA4L-C": "278_128",
12+
"domain": "edge.microsoft.com",
13+
"JA4.1": "t00d1616h2_4109672baa2e_bed3546ee6f4",
14+
"JA4_r.1": "t00d1616h2_,,171,172,195,196,199,200,392,393,6,65,66,67,690,7_0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_27,52,25,83,53,81,54,37",
15+
"JA4_o.1": "t00d1616h2_0200e8047a78_8a0afe6f3afd",
16+
"JA4_ro.1": "t00d1616h2_690,65,66,67,195,199,196,200,393,392,171,172,6,7,,_000d,0000,000a,0005,000b,002b,001b,ff01,0033,4469,002d,0023,0017,0012,0010,0015_27,52,25,83,53,81,54,37",
17+
"JA4X.1": "a373a9f83c6b_2bab15409345_0f2217ba412e",
18+
"JA4X.2": "7d5dbb3783b4_a373a9f83c6b_c34b04c10969"
19+
},
20+
{
21+
"stream": 1,
22+
"src": "172.27.7.31",
23+
"dst": "68.67.160.117",
24+
"srcport": "54525",
25+
"dstport": "443",
26+
"client_ttl": "128",
27+
"server_ttl": "41",
28+
"JA4L-S": "7166_41",
29+
"JA4L-C": "349_128",
30+
"domain": "nym1-ib.adnxs.com",
31+
"JA4.1": "t00d1616h2_c4e216e269f4_bed3546ee6f4",
32+
"JA4_r.1": "t00d1616h2_,,171,172,195,196,199,200,354,392,393,6,65,66,67,7_0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_27,52,25,83,53,81,54,37",
33+
"JA4_o.1": "t00d1616h2_00d8772d9166_548bd83a577c",
34+
"JA4_ro.1": "t00d1616h2_354,65,66,67,195,199,196,200,393,392,171,172,6,7,,_002b,4469,000b,0017,000d,0000,001b,0005,0033,ff01,0010,000a,002d,0012,0023,0015_27,52,25,83,53,81,54,37",
35+
"JA4S": "t0007h2_195_cf25e267ce22",
36+
"JA4X.1": "7d5dbb3783b4_2bab15409345_7bf9a7bf7029",
37+
"JA4X.2": "7d5dbb3783b4_7d5dbb3783b4_44440d41940c"
38+
},
39+
{
40+
"stream": 2,
41+
"src": "172.27.7.31",
42+
"dst": "103.42.133.15",
43+
"srcport": "54603",
44+
"dstport": "443",
45+
"client_ttl": "128",
46+
"server_ttl": "229",
47+
"JA4L-S": "2948_229",
48+
"JA4L-C": "247_128",
49+
"domain": "lptag.liveperson.net",
50+
"JA4.1": "t00d1616h2_73d9d18e4e10_bed3546ee6f4",
51+
"JA4_r.1": "t00d1616h2_,,018,171,172,195,196,199,200,392,393,6,65,66,67,7_0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_27,52,25,83,53,81,54,37",
52+
"JA4_o.1": "t00d1616h2_828fc7e24cd3_0069bd55eedf",
53+
"JA4_ro.1": "t00d1616h2_018,65,66,67,195,199,196,200,393,392,171,172,6,7,,_0000,0033,0010,0017,ff01,0012,002b,000d,000a,002d,0005,0023,000b,4469,001b,0015_27,52,25,83,53,81,54,37",
54+
"JA4S": "t0005h2_199_845f7282a956",
55+
"JA4X.1": "2bab15409345_2e9214a636bc_b891c0ad6f32",
56+
"JA4X.2": "2bab15409345_2bab15409345_2367ce7fbc5b",
57+
"JA4X.3": "2bab15409345_2bab15409345_2030e37f3421"
58+
}
59+
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[
2+
{
3+
"stream": 0,
4+
"src": "2001:db8:1::1",
5+
"dst": "2606:4700:10::6816:826",
6+
"srcport": "57098",
7+
"dstport": "443",
8+
"client_ttl": "64",
9+
"server_ttl": "56",
10+
"JA4L-S": "5749_56",
11+
"JA4L-C": "149_64",
12+
"domain": "cloudflare-quic.com",
13+
"JA4.1": "t00d1616h2_06835249484a_bed3546ee6f4",
14+
"JA4_r.1": "t00d1616h2_,,171,172,195,196,199,200,392,393,6,65,66,67,7,802_0005,000a,000b,000d,0012,0015,0017,001b,0023,002b,002d,0033,4469,ff01_27,52,25,83,53,81,54,37",
15+
"JA4_o.1": "t00d1616h2_572e68ba0241_ac2009940b69",
16+
"JA4_ro.1": "t00d1616h2_802,65,66,67,195,199,196,200,393,392,171,172,6,7,,_0000,0017,ff01,000a,000b,0023,0010,0005,000d,0012,0033,002d,002b,001b,4469,0015_27,52,25,83,53,81,54,37",
17+
"JA4S": "t000200_65_234ea6891581",
18+
"JA4X.1": "a373a9f83c6b_2bab15409345_7bf9a7bf7029",
19+
"JA4X.2": "7d5dbb3783b4_a373a9f83c6b_44440d41940c"
20+
},
21+
{
22+
"stream": 0,
23+
"src": "2001:db8:1::1",
24+
"dst": "2606:4700:10::6816:826",
25+
"srcport": "57098",
26+
"dstport": "443",
27+
"JA4H": "ge20nn12enus_60f823d07c94_000000000000_000000000000"
28+
},
29+
{
30+
"stream": 0,
31+
"src": "2001:db8:1::1",
32+
"dst": "2606:4700:10::6816:826",
33+
"srcport": "50280",
34+
"dstport": "443",
35+
"client_ttl": "64",
36+
"server_ttl": "56",
37+
"JA4L-S": "10990_56",
38+
"JA4L-C": "113_64"
39+
}
40+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[
2+
{
3+
"stream": 0,
4+
"src": "100.20.9.2",
5+
"dst": "100.20.9.1",
6+
"srcport": "65174",
7+
"dstport": "80",
8+
"client_ttl": "64",
9+
"server_ttl": "64",
10+
"JA4L-S": "997_64",
11+
"JA4L-C": "953_64"
12+
}
13+
]

0 commit comments

Comments
 (0)