leukquant 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- leukquant-0.1.0/PKG-INFO +99 -0
- leukquant-0.1.0/README.md +72 -0
- leukquant-0.1.0/leukquant.egg-info/PKG-INFO +99 -0
- leukquant-0.1.0/leukquant.egg-info/SOURCES.txt +34 -0
- leukquant-0.1.0/leukquant.egg-info/dependency_links.txt +1 -0
- leukquant-0.1.0/leukquant.egg-info/entry_points.txt +2 -0
- leukquant-0.1.0/leukquant.egg-info/requires.txt +24 -0
- leukquant-0.1.0/leukquant.egg-info/top_level.txt +1 -0
- leukquant-0.1.0/pyproject.toml +39 -0
- leukquant-0.1.0/setup.cfg +4 -0
- leukquant-0.1.0/src/__init__.py +0 -0
- leukquant-0.1.0/src/behavior/__init__.py +0 -0
- leukquant-0.1.0/src/behavior/monitors.py +100 -0
- leukquant-0.1.0/src/behavior/profiler.py +99 -0
- leukquant-0.1.0/src/cli/__init__.py +0 -0
- leukquant-0.1.0/src/cli/main.py +616 -0
- leukquant-0.1.0/src/config.py +91 -0
- leukquant-0.1.0/src/crypto/__init__.py +0 -0
- leukquant-0.1.0/src/crypto/key_manager.py +130 -0
- leukquant-0.1.0/src/crypto/pq_encrypt.py +171 -0
- leukquant-0.1.0/src/db/__init__.py +0 -0
- leukquant-0.1.0/src/db/database.py +248 -0
- leukquant-0.1.0/src/offline/__init__.py +0 -0
- leukquant-0.1.0/src/offline/sync.py +165 -0
- leukquant-0.1.0/src/paths.py +70 -0
- leukquant-0.1.0/src/quarantine/__init__.py +0 -0
- leukquant-0.1.0/src/quarantine/manager.py +111 -0
- leukquant-0.1.0/src/scanner/__init__.py +0 -0
- leukquant-0.1.0/src/scanner/dataset_loaders.py +414 -0
- leukquant-0.1.0/src/scanner/extract.py +180 -0
- leukquant-0.1.0/src/scanner/scan.py +182 -0
- leukquant-0.1.0/src/scanner/train.py +340 -0
- leukquant-0.1.0/tests/test_behavior.py +112 -0
- leukquant-0.1.0/tests/test_crypto.py +148 -0
- leukquant-0.1.0/tests/test_quarantine.py +158 -0
- leukquant-0.1.0/tests/test_scanner.py +97 -0
leukquant-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: leukquant
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fully Local, Decentralized Threat Defense
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: onnxruntime>=1.16.0
|
|
8
|
+
Requires-Dist: numpy>=1.24.0
|
|
9
|
+
Requires-Dist: psutil>=5.9.0
|
|
10
|
+
Requires-Dist: cryptography>=41.0.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
12
|
+
Requires-Dist: click>=8.1.7
|
|
13
|
+
Requires-Dist: rich>=13.7.0
|
|
14
|
+
Requires-Dist: watchdog>=3.0.0
|
|
15
|
+
Requires-Dist: scikit-learn>=1.3.2
|
|
16
|
+
Requires-Dist: skl2onnx>=1.16.0
|
|
17
|
+
Provides-Extra: pqc
|
|
18
|
+
Requires-Dist: liboqs-python>=0.8.0; extra == "pqc"
|
|
19
|
+
Provides-Extra: ember
|
|
20
|
+
Requires-Dist: ember; extra == "ember"
|
|
21
|
+
Requires-Dist: lief; extra == "ember"
|
|
22
|
+
Provides-Extra: kaggle
|
|
23
|
+
Requires-Dist: kaggle; extra == "kaggle"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# Leukquant — Fully Local, Decentralized Threat Defense
|
|
29
|
+
|
|
30
|
+
> **Zero cloud. Zero trust in corporations. Zero single point of failure.**
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
Leukquant is a fully local, decentralized threat defense system. It uses on-device AI for malware classification, a behavior profiler for anomaly detection, and post-quantum cryptography for file encryption.
|
|
35
|
+
|
|
36
|
+
*Note: The blockchain threat ledger is currently disabled in this version. We use datasets like EMBER, VirusShare, and Kaggle Malware datasets for local AI scanning.*
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
1. **Local AI Scanning**: On-device malware classification using ONNX models. No telemetry. No cloud calls.
|
|
41
|
+
2. **Behavior Profiler**: Learns normal patterns over a 14-day baseline and flags deviations.
|
|
42
|
+
3. **Post-Quantum Crypto Vault**: Encrypts files with NIST PQC standards (ML-KEM, ML-DSA, SLH-DSA).
|
|
43
|
+
4. **Offline Mode**: Built for machines that never touch the internet.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install -r requirements.txt
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
### 1. Local AI Scanning
|
|
54
|
+
|
|
55
|
+
Scan a file using the local AI model:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python src/cli/main.py scan --file /path/to/file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Behavior Profiler
|
|
62
|
+
|
|
63
|
+
Start monitoring system behavior:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python src/cli/main.py monitor
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Post-Quantum File Encryption
|
|
70
|
+
|
|
71
|
+
Encrypt a file:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python src/cli/main.py encrypt --file secret.pdf --algo ml-kem-1024 --sign ml-dsa-87
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Decrypt a file:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python src/cli/main.py decrypt --file secret.pdf.sqe --key ~/.Leukquant/private.key
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## File Structure
|
|
84
|
+
|
|
85
|
+
- `models/`: Contains the ONNX/GGML models for malware detection.
|
|
86
|
+
- `db/`: Local SQLite databases for threat signatures and behavior baselines.
|
|
87
|
+
- `keys/`: Post-quantum cryptographic keys.
|
|
88
|
+
- `config/`: Configuration files (e.g., `Leukquant.yml`).
|
|
89
|
+
- `src/`: Source code for the scanner, behavior profiler, crypto vault, and CLI.
|
|
90
|
+
- `logs/`: Local logs for anomalies.
|
|
91
|
+
|
|
92
|
+
## Datasets for Training
|
|
93
|
+
|
|
94
|
+
To train the local AI model, you can use the following datasets:
|
|
95
|
+
- **EMBER**: Open PE malware dataset.
|
|
96
|
+
- **VirusShare**: Repository of malware samples.
|
|
97
|
+
- **Kaggle Malware Datasets**: Various datasets available on Kaggle.
|
|
98
|
+
|
|
99
|
+
*Note: The pre-trained model should be placed in `models/malware_detector.onnx`.*
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Leukquant — Fully Local, Decentralized Threat Defense
|
|
2
|
+
|
|
3
|
+
> **Zero cloud. Zero trust in corporations. Zero single point of failure.**
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Leukquant is a fully local, decentralized threat defense system. It uses on-device AI for malware classification, a behavior profiler for anomaly detection, and post-quantum cryptography for file encryption.
|
|
8
|
+
|
|
9
|
+
*Note: The blockchain threat ledger is currently disabled in this version. We use datasets like EMBER, VirusShare, and Kaggle Malware datasets for local AI scanning.*
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
1. **Local AI Scanning**: On-device malware classification using ONNX models. No telemetry. No cloud calls.
|
|
14
|
+
2. **Behavior Profiler**: Learns normal patterns over a 14-day baseline and flags deviations.
|
|
15
|
+
3. **Post-Quantum Crypto Vault**: Encrypts files with NIST PQC standards (ML-KEM, ML-DSA, SLH-DSA).
|
|
16
|
+
4. **Offline Mode**: Built for machines that never touch the internet.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -r requirements.txt
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### 1. Local AI Scanning
|
|
27
|
+
|
|
28
|
+
Scan a file using the local AI model:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
python src/cli/main.py scan --file /path/to/file
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Behavior Profiler
|
|
35
|
+
|
|
36
|
+
Start monitoring system behavior:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
python src/cli/main.py monitor
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Post-Quantum File Encryption
|
|
43
|
+
|
|
44
|
+
Encrypt a file:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python src/cli/main.py encrypt --file secret.pdf --algo ml-kem-1024 --sign ml-dsa-87
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Decrypt a file:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python src/cli/main.py decrypt --file secret.pdf.sqe --key ~/.Leukquant/private.key
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## File Structure
|
|
57
|
+
|
|
58
|
+
- `models/`: Contains the ONNX/GGML models for malware detection.
|
|
59
|
+
- `db/`: Local SQLite databases for threat signatures and behavior baselines.
|
|
60
|
+
- `keys/`: Post-quantum cryptographic keys.
|
|
61
|
+
- `config/`: Configuration files (e.g., `Leukquant.yml`).
|
|
62
|
+
- `src/`: Source code for the scanner, behavior profiler, crypto vault, and CLI.
|
|
63
|
+
- `logs/`: Local logs for anomalies.
|
|
64
|
+
|
|
65
|
+
## Datasets for Training
|
|
66
|
+
|
|
67
|
+
To train the local AI model, you can use the following datasets:
|
|
68
|
+
- **EMBER**: Open PE malware dataset.
|
|
69
|
+
- **VirusShare**: Repository of malware samples.
|
|
70
|
+
- **Kaggle Malware Datasets**: Various datasets available on Kaggle.
|
|
71
|
+
|
|
72
|
+
*Note: The pre-trained model should be placed in `models/malware_detector.onnx`.*
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: leukquant
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fully Local, Decentralized Threat Defense
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: onnxruntime>=1.16.0
|
|
8
|
+
Requires-Dist: numpy>=1.24.0
|
|
9
|
+
Requires-Dist: psutil>=5.9.0
|
|
10
|
+
Requires-Dist: cryptography>=41.0.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0.1
|
|
12
|
+
Requires-Dist: click>=8.1.7
|
|
13
|
+
Requires-Dist: rich>=13.7.0
|
|
14
|
+
Requires-Dist: watchdog>=3.0.0
|
|
15
|
+
Requires-Dist: scikit-learn>=1.3.2
|
|
16
|
+
Requires-Dist: skl2onnx>=1.16.0
|
|
17
|
+
Provides-Extra: pqc
|
|
18
|
+
Requires-Dist: liboqs-python>=0.8.0; extra == "pqc"
|
|
19
|
+
Provides-Extra: ember
|
|
20
|
+
Requires-Dist: ember; extra == "ember"
|
|
21
|
+
Requires-Dist: lief; extra == "ember"
|
|
22
|
+
Provides-Extra: kaggle
|
|
23
|
+
Requires-Dist: kaggle; extra == "kaggle"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# Leukquant — Fully Local, Decentralized Threat Defense
|
|
29
|
+
|
|
30
|
+
> **Zero cloud. Zero trust in corporations. Zero single point of failure.**
|
|
31
|
+
|
|
32
|
+
## Overview
|
|
33
|
+
|
|
34
|
+
Leukquant is a fully local, decentralized threat defense system. It uses on-device AI for malware classification, a behavior profiler for anomaly detection, and post-quantum cryptography for file encryption.
|
|
35
|
+
|
|
36
|
+
*Note: The blockchain threat ledger is currently disabled in this version. We use datasets like EMBER, VirusShare, and Kaggle Malware datasets for local AI scanning.*
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
1. **Local AI Scanning**: On-device malware classification using ONNX models. No telemetry. No cloud calls.
|
|
41
|
+
2. **Behavior Profiler**: Learns normal patterns over a 14-day baseline and flags deviations.
|
|
42
|
+
3. **Post-Quantum Crypto Vault**: Encrypts files with NIST PQC standards (ML-KEM, ML-DSA, SLH-DSA).
|
|
43
|
+
4. **Offline Mode**: Built for machines that never touch the internet.
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install -r requirements.txt
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
### 1. Local AI Scanning
|
|
54
|
+
|
|
55
|
+
Scan a file using the local AI model:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
python src/cli/main.py scan --file /path/to/file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Behavior Profiler
|
|
62
|
+
|
|
63
|
+
Start monitoring system behavior:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python src/cli/main.py monitor
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Post-Quantum File Encryption
|
|
70
|
+
|
|
71
|
+
Encrypt a file:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python src/cli/main.py encrypt --file secret.pdf --algo ml-kem-1024 --sign ml-dsa-87
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Decrypt a file:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python src/cli/main.py decrypt --file secret.pdf.sqe --key ~/.Leukquant/private.key
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## File Structure
|
|
84
|
+
|
|
85
|
+
- `models/`: Contains the ONNX/GGML models for malware detection.
|
|
86
|
+
- `db/`: Local SQLite databases for threat signatures and behavior baselines.
|
|
87
|
+
- `keys/`: Post-quantum cryptographic keys.
|
|
88
|
+
- `config/`: Configuration files (e.g., `Leukquant.yml`).
|
|
89
|
+
- `src/`: Source code for the scanner, behavior profiler, crypto vault, and CLI.
|
|
90
|
+
- `logs/`: Local logs for anomalies.
|
|
91
|
+
|
|
92
|
+
## Datasets for Training
|
|
93
|
+
|
|
94
|
+
To train the local AI model, you can use the following datasets:
|
|
95
|
+
- **EMBER**: Open PE malware dataset.
|
|
96
|
+
- **VirusShare**: Repository of malware samples.
|
|
97
|
+
- **Kaggle Malware Datasets**: Various datasets available on Kaggle.
|
|
98
|
+
|
|
99
|
+
*Note: The pre-trained model should be placed in `models/malware_detector.onnx`.*
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
leukquant.egg-info/PKG-INFO
|
|
4
|
+
leukquant.egg-info/SOURCES.txt
|
|
5
|
+
leukquant.egg-info/dependency_links.txt
|
|
6
|
+
leukquant.egg-info/entry_points.txt
|
|
7
|
+
leukquant.egg-info/requires.txt
|
|
8
|
+
leukquant.egg-info/top_level.txt
|
|
9
|
+
src/__init__.py
|
|
10
|
+
src/config.py
|
|
11
|
+
src/paths.py
|
|
12
|
+
src/behavior/__init__.py
|
|
13
|
+
src/behavior/monitors.py
|
|
14
|
+
src/behavior/profiler.py
|
|
15
|
+
src/cli/__init__.py
|
|
16
|
+
src/cli/main.py
|
|
17
|
+
src/crypto/__init__.py
|
|
18
|
+
src/crypto/key_manager.py
|
|
19
|
+
src/crypto/pq_encrypt.py
|
|
20
|
+
src/db/__init__.py
|
|
21
|
+
src/db/database.py
|
|
22
|
+
src/offline/__init__.py
|
|
23
|
+
src/offline/sync.py
|
|
24
|
+
src/quarantine/__init__.py
|
|
25
|
+
src/quarantine/manager.py
|
|
26
|
+
src/scanner/__init__.py
|
|
27
|
+
src/scanner/dataset_loaders.py
|
|
28
|
+
src/scanner/extract.py
|
|
29
|
+
src/scanner/scan.py
|
|
30
|
+
src/scanner/train.py
|
|
31
|
+
tests/test_behavior.py
|
|
32
|
+
tests/test_crypto.py
|
|
33
|
+
tests/test_quarantine.py
|
|
34
|
+
tests/test_scanner.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
onnxruntime>=1.16.0
|
|
2
|
+
numpy>=1.24.0
|
|
3
|
+
psutil>=5.9.0
|
|
4
|
+
cryptography>=41.0.0
|
|
5
|
+
pyyaml>=6.0.1
|
|
6
|
+
click>=8.1.7
|
|
7
|
+
rich>=13.7.0
|
|
8
|
+
watchdog>=3.0.0
|
|
9
|
+
scikit-learn>=1.3.2
|
|
10
|
+
skl2onnx>=1.16.0
|
|
11
|
+
|
|
12
|
+
[dev]
|
|
13
|
+
pytest>=7.4.0
|
|
14
|
+
pytest-cov>=4.1.0
|
|
15
|
+
|
|
16
|
+
[ember]
|
|
17
|
+
ember
|
|
18
|
+
lief
|
|
19
|
+
|
|
20
|
+
[kaggle]
|
|
21
|
+
kaggle
|
|
22
|
+
|
|
23
|
+
[pqc]
|
|
24
|
+
liboqs-python>=0.8.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
src
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "leukquant"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Fully Local, Decentralized Threat Defense"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"onnxruntime>=1.16.0",
|
|
13
|
+
"numpy>=1.24.0",
|
|
14
|
+
"psutil>=5.9.0",
|
|
15
|
+
"cryptography>=41.0.0",
|
|
16
|
+
"pyyaml>=6.0.1",
|
|
17
|
+
"click>=8.1.7",
|
|
18
|
+
"rich>=13.7.0",
|
|
19
|
+
"watchdog>=3.0.0",
|
|
20
|
+
"scikit-learn>=1.3.2",
|
|
21
|
+
"skl2onnx>=1.16.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
pqc = ["liboqs-python>=0.8.0"]
|
|
26
|
+
ember = ["ember", "lief"]
|
|
27
|
+
kaggle = ["kaggle"]
|
|
28
|
+
dev = ["pytest>=7.4.0", "pytest-cov>=4.1.0"]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
leukquant = "src.cli.main:cli"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["."]
|
|
35
|
+
include = ["src*"]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
python_files = ["test_*.py"]
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from collections import deque
|
|
5
|
+
from watchdog.observers import Observer
|
|
6
|
+
from watchdog.events import FileSystemEventHandler
|
|
7
|
+
import psutil
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class FileEventTracker(FileSystemEventHandler):
|
|
13
|
+
"""Tracks file system create/modify events using a rolling time window."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, window_seconds: int = 60):
|
|
16
|
+
super().__init__()
|
|
17
|
+
self.window_seconds = window_seconds
|
|
18
|
+
self._events: deque = deque()
|
|
19
|
+
self._lock = threading.Lock()
|
|
20
|
+
|
|
21
|
+
def on_created(self, event):
|
|
22
|
+
if not event.is_directory:
|
|
23
|
+
self._record("created", event.src_path)
|
|
24
|
+
|
|
25
|
+
def on_modified(self, event):
|
|
26
|
+
if not event.is_directory:
|
|
27
|
+
self._record("modified", event.src_path)
|
|
28
|
+
|
|
29
|
+
def on_deleted(self, event):
|
|
30
|
+
if not event.is_directory:
|
|
31
|
+
self._record("deleted", event.src_path)
|
|
32
|
+
|
|
33
|
+
def _record(self, kind: str, path: str):
|
|
34
|
+
now = time.monotonic()
|
|
35
|
+
with self._lock:
|
|
36
|
+
self._events.append((now, kind, path))
|
|
37
|
+
cutoff = now - self.window_seconds
|
|
38
|
+
while self._events and self._events[0][0] < cutoff:
|
|
39
|
+
self._events.popleft()
|
|
40
|
+
|
|
41
|
+
def get_rate(self) -> float:
|
|
42
|
+
"""Return events-per-minute in the current window."""
|
|
43
|
+
now = time.monotonic()
|
|
44
|
+
cutoff = now - self.window_seconds
|
|
45
|
+
with self._lock:
|
|
46
|
+
recent = sum(1 for ts, _, _ in self._events if ts >= cutoff)
|
|
47
|
+
# Normalize to events/minute
|
|
48
|
+
return (recent / self.window_seconds) * 60
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class SystemMonitor:
|
|
52
|
+
"""
|
|
53
|
+
Aggregates real-time system metrics:
|
|
54
|
+
- File system event rate (watchdog)
|
|
55
|
+
- Active process count (psutil)
|
|
56
|
+
- Network connection count (psutil)
|
|
57
|
+
- Memory utilization % (psutil)
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, watch_path: str = "/tmp", window_seconds: int = 60):
|
|
61
|
+
self.watch_path = watch_path
|
|
62
|
+
self.file_tracker = FileEventTracker(window_seconds)
|
|
63
|
+
self._observer = Observer()
|
|
64
|
+
self._observer.schedule(self.file_tracker, watch_path, recursive=True)
|
|
65
|
+
self._running = False
|
|
66
|
+
|
|
67
|
+
def start(self):
|
|
68
|
+
if not self._running:
|
|
69
|
+
try:
|
|
70
|
+
self._observer.start()
|
|
71
|
+
self._running = True
|
|
72
|
+
logger.info(f"SystemMonitor started. Watching: {self.watch_path}")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.warning(f"Could not start file watcher: {e}")
|
|
75
|
+
|
|
76
|
+
def stop(self):
|
|
77
|
+
if self._running:
|
|
78
|
+
self._observer.stop()
|
|
79
|
+
self._observer.join()
|
|
80
|
+
self._running = False
|
|
81
|
+
logger.info("SystemMonitor stopped.")
|
|
82
|
+
|
|
83
|
+
def get_snapshot(self) -> dict:
|
|
84
|
+
try:
|
|
85
|
+
mem = psutil.virtual_memory()
|
|
86
|
+
net = psutil.net_connections(kind="inet")
|
|
87
|
+
return {
|
|
88
|
+
"process_spawn_rate": len(psutil.pids()),
|
|
89
|
+
"file_write_rate": self.file_tracker.get_rate(),
|
|
90
|
+
"network_socket_count": len(net),
|
|
91
|
+
"memory_alloc_spikes": mem.percent,
|
|
92
|
+
}
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.warning(f"SystemMonitor snapshot error: {e}")
|
|
95
|
+
return {
|
|
96
|
+
"process_spawn_rate": 0,
|
|
97
|
+
"file_write_rate": 0.0,
|
|
98
|
+
"network_socket_count": 0,
|
|
99
|
+
"memory_alloc_spikes": 0.0,
|
|
100
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import numpy as np
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from src.db.database import DatabaseManager
|
|
7
|
+
from src.behavior.monitors import SystemMonitor
|
|
8
|
+
from src.config import Config
|
|
9
|
+
from src.paths import app_path
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
MIN_BASELINE_SAMPLES = 10
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BehaviorProfiler:
|
|
17
|
+
"""
|
|
18
|
+
Collects system metrics every `poll_interval` seconds, stores them in SQLite,
|
|
19
|
+
and computes a Z-score anomaly metric against the rolling `baseline_days`-day window.
|
|
20
|
+
|
|
21
|
+
A(t) = (1/N) * Σ |x_i(t) − μ_i| / σ_i
|
|
22
|
+
|
|
23
|
+
Scores above `alert_threshold` are logged; scores above `quarantine_threshold`
|
|
24
|
+
are also written as anomaly events for downstream quarantine decisions.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
PID_FILE = app_path("logs", "monitor.pid")
|
|
28
|
+
|
|
29
|
+
def __init__(self, poll_interval: int = 60):
|
|
30
|
+
cfg = Config.get()
|
|
31
|
+
self.baseline_days: int = cfg.get_value("behavior", "baseline_period_days", default=14)
|
|
32
|
+
self.alert_threshold: float = cfg.get_value("behavior", "alert_threshold", default=3.5)
|
|
33
|
+
self.quarantine_threshold: float = cfg.get_value("behavior", "quarantine_threshold", default=5.0)
|
|
34
|
+
self.poll_interval = poll_interval
|
|
35
|
+
self.db = DatabaseManager()
|
|
36
|
+
self.monitor = SystemMonitor()
|
|
37
|
+
|
|
38
|
+
# ─── metric helpers ───────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
def _collect(self) -> dict:
|
|
41
|
+
snap = self.monitor.get_snapshot()
|
|
42
|
+
snap["timestamp"] = datetime.now()
|
|
43
|
+
return snap
|
|
44
|
+
|
|
45
|
+
def calculate_anomaly_score(self, metrics: dict) -> float:
|
|
46
|
+
rows = self.db.get_baseline_metrics(days=self.baseline_days)
|
|
47
|
+
if len(rows) < MIN_BASELINE_SAMPLES:
|
|
48
|
+
logger.debug(f"Only {len(rows)} samples in baseline — skipping anomaly scoring.")
|
|
49
|
+
return 0.0
|
|
50
|
+
|
|
51
|
+
baseline = np.array(rows, dtype=float)
|
|
52
|
+
means = np.mean(baseline, axis=0)
|
|
53
|
+
stds = np.std(baseline, axis=0)
|
|
54
|
+
stds[stds == 0] = 1e-6
|
|
55
|
+
|
|
56
|
+
current = np.array([
|
|
57
|
+
metrics["process_spawn_rate"],
|
|
58
|
+
metrics["file_write_rate"],
|
|
59
|
+
metrics["network_socket_count"],
|
|
60
|
+
metrics["memory_alloc_spikes"],
|
|
61
|
+
], dtype=float)
|
|
62
|
+
|
|
63
|
+
z_scores = np.abs(current - means) / stds
|
|
64
|
+
return float(np.mean(z_scores))
|
|
65
|
+
|
|
66
|
+
# ─── main loop ────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
def monitor_loop(self):
|
|
69
|
+
os.makedirs(app_path("logs"), exist_ok=True)
|
|
70
|
+
with open(self.PID_FILE, "w") as f:
|
|
71
|
+
f.write(str(os.getpid()))
|
|
72
|
+
|
|
73
|
+
self.monitor.start()
|
|
74
|
+
logger.info(f"Behavior monitor started (PID {os.getpid()}, interval {self.poll_interval}s).")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
while True:
|
|
78
|
+
metrics = self._collect()
|
|
79
|
+
self.db.insert_metrics(metrics)
|
|
80
|
+
score = self.calculate_anomaly_score(metrics)
|
|
81
|
+
|
|
82
|
+
if score > self.alert_threshold:
|
|
83
|
+
details = (
|
|
84
|
+
f"score={score:.3f} | "
|
|
85
|
+
f"procs={metrics['process_spawn_rate']} | "
|
|
86
|
+
f"files/min={metrics['file_write_rate']:.1f} | "
|
|
87
|
+
f"sockets={metrics['network_socket_count']} | "
|
|
88
|
+
f"mem%={metrics['memory_alloc_spikes']:.1f}"
|
|
89
|
+
)
|
|
90
|
+
level = "CRITICAL" if score > self.quarantine_threshold else "WARNING"
|
|
91
|
+
logger.warning(f"[{level}] Anomaly score {score:.3f} — {details}")
|
|
92
|
+
self.db.log_anomaly(score, details)
|
|
93
|
+
|
|
94
|
+
time.sleep(self.poll_interval)
|
|
95
|
+
finally:
|
|
96
|
+
self.monitor.stop()
|
|
97
|
+
if os.path.exists(self.PID_FILE):
|
|
98
|
+
os.remove(self.PID_FILE)
|
|
99
|
+
logger.info("Behavior monitor stopped.")
|
|
File without changes
|