diogenesis-sdk 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.
- diogenesis_sdk-0.1.0/LICENSE +21 -0
- diogenesis_sdk-0.1.0/PKG-INFO +91 -0
- diogenesis_sdk-0.1.0/README.md +72 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/__init__.py +52 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/agents/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/agents/swarm.py +152 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/agents/xenobot.py +366 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/baseline.py +76 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/core/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/core/classifier.py +104 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/core/config.py +62 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/core/event_buffer.py +52 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/engine.py +308 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/field/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/field/propagation.py +90 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/field/voltage.py +226 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/base.py +70 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/file_hook.py +43 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/import_hook.py +41 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/network_hook.py +55 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/interceptors/subprocess_hook.py +69 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/policy/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/policy/engine.py +58 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/policy/pattern.py +157 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/policy/response.py +55 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/timing/__init__.py +0 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/timing/fibonacci.py +206 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk/timing/scheduler.py +154 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk.egg-info/PKG-INFO +91 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk.egg-info/SOURCES.txt +47 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk.egg-info/dependency_links.txt +1 -0
- diogenesis_sdk-0.1.0/diogenesis_sdk.egg-info/top_level.txt +1 -0
- diogenesis_sdk-0.1.0/pyproject.toml +25 -0
- diogenesis_sdk-0.1.0/setup.cfg +4 -0
- diogenesis_sdk-0.1.0/tests/test_engine.py +294 -0
- diogenesis_sdk-0.1.0/tests/test_fibonacci.py +130 -0
- diogenesis_sdk-0.1.0/tests/test_file_hook.py +81 -0
- diogenesis_sdk-0.1.0/tests/test_graduated_response.py +65 -0
- diogenesis_sdk-0.1.0/tests/test_import_hook.py +64 -0
- diogenesis_sdk-0.1.0/tests/test_network_hook.py +55 -0
- diogenesis_sdk-0.1.0/tests/test_pattern.py +148 -0
- diogenesis_sdk-0.1.0/tests/test_policy_engine.py +92 -0
- diogenesis_sdk-0.1.0/tests/test_propagation.py +83 -0
- diogenesis_sdk-0.1.0/tests/test_scheduler.py +88 -0
- diogenesis_sdk-0.1.0/tests/test_subprocess_hook.py +60 -0
- diogenesis_sdk-0.1.0/tests/test_swarm.py +119 -0
- diogenesis_sdk-0.1.0/tests/test_voltage.py +117 -0
- diogenesis_sdk-0.1.0/tests/test_xenobot.py +150 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Garry Anderson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: diogenesis-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Behavioral health for Python applications. Not signatures. Not rules. Coherence.
|
|
5
|
+
Author-email: Garry Anderson <prnt3dman1@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/prnt3dman1/diogenesis-sdk
|
|
8
|
+
Keywords: security,behavioral,monitoring,runtime,anomaly,detection
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Security
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# Diogenesis SDK
|
|
21
|
+
|
|
22
|
+
**Behavioral health for Python applications.**
|
|
23
|
+
|
|
24
|
+
Not signature-based detection. Not rule-based blocking. Behavioral coherence.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
pip install diogenesis-sdk
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import diogenesis_sdk
|
|
36
|
+
|
|
37
|
+
diogenesis_sdk.activate()
|
|
38
|
+
# your application runs normally — every import, file access,
|
|
39
|
+
# subprocess call, and network request is observed
|
|
40
|
+
|
|
41
|
+
print(diogenesis_sdk.status()) # what's being tracked
|
|
42
|
+
print(diogenesis_sdk.log(10)) # last 10 events
|
|
43
|
+
print(diogenesis_sdk.baseline()) # behavioral fingerprint (after 1000+ events)
|
|
44
|
+
|
|
45
|
+
diogenesis_sdk.deactivate() # cleanly restore all originals
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## What It Does
|
|
49
|
+
|
|
50
|
+
Diogenesis intercepts every import, file access, subprocess call, and network request in your Python application. It classifies each as KNOWN, UNEXPECTED, or SUSPICIOUS based on behavioral patterns. After observing 1000+ events, it generates a behavioral baseline that defines what "normal" looks like. Deviations are anomalies — whether from attacks, bugs, or dependency rot.
|
|
51
|
+
|
|
52
|
+
## Zero Dependencies
|
|
53
|
+
|
|
54
|
+
Diogenesis uses only the Python standard library. A security product with supply chain risk is a contradiction.
|
|
55
|
+
|
|
56
|
+
## Philosophy
|
|
57
|
+
|
|
58
|
+
Inspired by Michael Levin's xenobot research: biological systems maintain health through behavioral coherence, not programmed rules. Diogenesis applies the same principle to software.
|
|
59
|
+
|
|
60
|
+
## Design Principles
|
|
61
|
+
|
|
62
|
+
- **Never break the host application.** Every interceptor is try/except wrapped. If Diogenesis fails, your code runs normally.
|
|
63
|
+
- **Shadow mode only.** Diogenesis observes. It does not block. Policy enforcement comes in future versions.
|
|
64
|
+
- **Zero dependencies.** stdlib only. No supply chain risk.
|
|
65
|
+
- **Clean activate/deactivate.** One function call to start. One to stop. All originals restored.
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
Pass a config dict to `activate()`:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
diogenesis_sdk.activate({
|
|
73
|
+
"buffer_size": 5000,
|
|
74
|
+
"interceptors": {"import": True, "file": True, "subprocess": False, "network": True},
|
|
75
|
+
"whitelist": {
|
|
76
|
+
"import_modules": ["myapp", "mylib"],
|
|
77
|
+
"file_paths": ["/app/data/"],
|
|
78
|
+
"network_hosts": ["localhost", "api.myservice.com"],
|
|
79
|
+
},
|
|
80
|
+
"suspicious_patterns": {
|
|
81
|
+
"imports": ["ctypes"],
|
|
82
|
+
"subprocess_commands": ["rm -rf"],
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or place a `diogenesis_sdk.json` file in your working directory.
|
|
88
|
+
|
|
89
|
+
## License
|
|
90
|
+
|
|
91
|
+
MIT
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Diogenesis SDK
|
|
2
|
+
|
|
3
|
+
**Behavioral health for Python applications.**
|
|
4
|
+
|
|
5
|
+
Not signature-based detection. Not rule-based blocking. Behavioral coherence.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
pip install diogenesis-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import diogenesis_sdk
|
|
17
|
+
|
|
18
|
+
diogenesis_sdk.activate()
|
|
19
|
+
# your application runs normally — every import, file access,
|
|
20
|
+
# subprocess call, and network request is observed
|
|
21
|
+
|
|
22
|
+
print(diogenesis_sdk.status()) # what's being tracked
|
|
23
|
+
print(diogenesis_sdk.log(10)) # last 10 events
|
|
24
|
+
print(diogenesis_sdk.baseline()) # behavioral fingerprint (after 1000+ events)
|
|
25
|
+
|
|
26
|
+
diogenesis_sdk.deactivate() # cleanly restore all originals
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## What It Does
|
|
30
|
+
|
|
31
|
+
Diogenesis intercepts every import, file access, subprocess call, and network request in your Python application. It classifies each as KNOWN, UNEXPECTED, or SUSPICIOUS based on behavioral patterns. After observing 1000+ events, it generates a behavioral baseline that defines what "normal" looks like. Deviations are anomalies — whether from attacks, bugs, or dependency rot.
|
|
32
|
+
|
|
33
|
+
## Zero Dependencies
|
|
34
|
+
|
|
35
|
+
Diogenesis uses only the Python standard library. A security product with supply chain risk is a contradiction.
|
|
36
|
+
|
|
37
|
+
## Philosophy
|
|
38
|
+
|
|
39
|
+
Inspired by Michael Levin's xenobot research: biological systems maintain health through behavioral coherence, not programmed rules. Diogenesis applies the same principle to software.
|
|
40
|
+
|
|
41
|
+
## Design Principles
|
|
42
|
+
|
|
43
|
+
- **Never break the host application.** Every interceptor is try/except wrapped. If Diogenesis fails, your code runs normally.
|
|
44
|
+
- **Shadow mode only.** Diogenesis observes. It does not block. Policy enforcement comes in future versions.
|
|
45
|
+
- **Zero dependencies.** stdlib only. No supply chain risk.
|
|
46
|
+
- **Clean activate/deactivate.** One function call to start. One to stop. All originals restored.
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
Pass a config dict to `activate()`:
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
diogenesis_sdk.activate({
|
|
54
|
+
"buffer_size": 5000,
|
|
55
|
+
"interceptors": {"import": True, "file": True, "subprocess": False, "network": True},
|
|
56
|
+
"whitelist": {
|
|
57
|
+
"import_modules": ["myapp", "mylib"],
|
|
58
|
+
"file_paths": ["/app/data/"],
|
|
59
|
+
"network_hosts": ["localhost", "api.myservice.com"],
|
|
60
|
+
},
|
|
61
|
+
"suspicious_patterns": {
|
|
62
|
+
"imports": ["ctypes"],
|
|
63
|
+
"subprocess_commands": ["rm -rf"],
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or place a `diogenesis_sdk.json` file in your working directory.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Diogenesis SDK — Behavioral health for Python applications.
|
|
2
|
+
|
|
3
|
+
Not signature-based detection. Not rule-based blocking. Behavioral coherence.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
import diogenesis_sdk
|
|
7
|
+
|
|
8
|
+
diogenesis_sdk.activate()
|
|
9
|
+
print(diogenesis_sdk.status()) # interceptor counts + field coherence
|
|
10
|
+
print(diogenesis_sdk.field_state()) # per-module voltage map
|
|
11
|
+
print(diogenesis_sdk.alerts()) # policy alerts
|
|
12
|
+
print(diogenesis_sdk.investigations()) # xenobot findings
|
|
13
|
+
print(diogenesis_sdk.threat_summary()) # threat/suspicious/benign counts
|
|
14
|
+
diogenesis_sdk.deactivate()
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from .engine import DiogenesisEngine
|
|
18
|
+
from .policy.pattern import BehavioralPattern
|
|
19
|
+
from .timing.fibonacci import FibonacciClock
|
|
20
|
+
from .agents.xenobot import XenobotAgent
|
|
21
|
+
|
|
22
|
+
_engine = DiogenesisEngine()
|
|
23
|
+
|
|
24
|
+
activate = _engine.activate
|
|
25
|
+
deactivate = _engine.deactivate
|
|
26
|
+
status = _engine.status
|
|
27
|
+
log = _engine.log
|
|
28
|
+
baseline = _engine.baseline
|
|
29
|
+
configure = _engine.configure
|
|
30
|
+
alerts = _engine.alerts
|
|
31
|
+
alert_summary = _engine.alert_summary
|
|
32
|
+
escalations = _engine.escalations
|
|
33
|
+
add_pattern = _engine.add_pattern
|
|
34
|
+
evaluate_policies = _engine.evaluate_policies
|
|
35
|
+
field_state = _engine.field_state
|
|
36
|
+
module_voltage = _engine.module_voltage
|
|
37
|
+
voltage_history = _engine.voltage_history
|
|
38
|
+
connection_graph = _engine.connection_graph
|
|
39
|
+
create_scheduler = _engine.create_scheduler
|
|
40
|
+
investigations = _engine.investigations
|
|
41
|
+
threat_summary = _engine.threat_summary
|
|
42
|
+
add_agent = _engine.add_agent
|
|
43
|
+
|
|
44
|
+
__version__ = "0.1.0"
|
|
45
|
+
__author__ = "Garry Anderson"
|
|
46
|
+
__all__ = [
|
|
47
|
+
"activate", "deactivate", "status", "log", "baseline", "configure",
|
|
48
|
+
"alerts", "alert_summary", "escalations", "add_pattern", "evaluate_policies",
|
|
49
|
+
"field_state", "module_voltage", "voltage_history", "connection_graph",
|
|
50
|
+
"create_scheduler", "investigations", "threat_summary", "add_agent",
|
|
51
|
+
"BehavioralPattern", "FibonacciClock", "XenobotAgent",
|
|
52
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Xenobot swarm — coordinate multiple autonomous investigation agents."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections import deque
|
|
5
|
+
|
|
6
|
+
from .xenobot import XenobotAgent, Investigation
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class XenobotSwarm:
|
|
10
|
+
"""Coordinate multiple xenobot agents. Each specializes in a domain."""
|
|
11
|
+
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.agents = {}
|
|
14
|
+
self.investigation_log = deque(maxlen=2000)
|
|
15
|
+
self.shared_model = {}
|
|
16
|
+
|
|
17
|
+
def create_default_swarm(self):
|
|
18
|
+
"""Create the standard 4-agent swarm."""
|
|
19
|
+
self.agents = {
|
|
20
|
+
"import_guardian": XenobotAgent("import_guardian", domain="import"),
|
|
21
|
+
"file_sentinel": XenobotAgent("file_sentinel", domain="file"),
|
|
22
|
+
"network_watcher": XenobotAgent("network_watcher", domain="network"),
|
|
23
|
+
"subprocess_monitor": XenobotAgent("subprocess_monitor", domain="subprocess"),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def add_agent(self, agent):
|
|
27
|
+
"""Add a custom agent to the swarm."""
|
|
28
|
+
self.agents[agent.name] = agent
|
|
29
|
+
|
|
30
|
+
def remove_agent(self, name):
|
|
31
|
+
"""Remove an agent by name."""
|
|
32
|
+
self.agents.pop(name, None)
|
|
33
|
+
|
|
34
|
+
def respond_to_alert(self, alert, context) -> list:
|
|
35
|
+
"""Dispatch appropriate agent(s) to investigate an alert."""
|
|
36
|
+
results = []
|
|
37
|
+
matched_events = alert.get("matched_events", [])
|
|
38
|
+
event_types = set()
|
|
39
|
+
for evt in matched_events:
|
|
40
|
+
event_types.add(evt.get("type", "unknown"))
|
|
41
|
+
|
|
42
|
+
# Find agents whose domain matches any event type in the alert
|
|
43
|
+
dispatched = set()
|
|
44
|
+
for etype in event_types:
|
|
45
|
+
for name, agent in self.agents.items():
|
|
46
|
+
if agent.domain == etype and name not in dispatched:
|
|
47
|
+
dispatched.add(name)
|
|
48
|
+
# Use the first event of this agent's domain as trigger
|
|
49
|
+
trigger = next((e for e in matched_events if e.get("type") == etype), matched_events[0])
|
|
50
|
+
inv = agent.investigate(trigger, context)
|
|
51
|
+
self.investigation_log.append(inv)
|
|
52
|
+
results.append(inv)
|
|
53
|
+
|
|
54
|
+
# If no domain-specific agent matched, use first available agent
|
|
55
|
+
if not results and self.agents and matched_events:
|
|
56
|
+
agent = next(iter(self.agents.values()))
|
|
57
|
+
inv = agent.investigate(matched_events[0], context)
|
|
58
|
+
self.investigation_log.append(inv)
|
|
59
|
+
results.append(inv)
|
|
60
|
+
|
|
61
|
+
return results
|
|
62
|
+
|
|
63
|
+
def respond_to_voltage_drop(self, module_name, voltage, context) -> Investigation:
|
|
64
|
+
"""Investigate a module with dangerously low voltage."""
|
|
65
|
+
# Determine the module's primary event type from recent events
|
|
66
|
+
recent = context.get("recent_events", [])
|
|
67
|
+
module_events = [e for e in recent if module_name in str(e.get("caller_file", ""))]
|
|
68
|
+
|
|
69
|
+
# Count event types for this module
|
|
70
|
+
type_counts = {}
|
|
71
|
+
for e in module_events[-50:]:
|
|
72
|
+
t = e.get("type", "unknown")
|
|
73
|
+
type_counts[t] = type_counts.get(t, 0) + 1
|
|
74
|
+
|
|
75
|
+
primary_type = max(type_counts, key=type_counts.get) if type_counts else "unknown"
|
|
76
|
+
|
|
77
|
+
# Find the right agent
|
|
78
|
+
agent = None
|
|
79
|
+
for a in self.agents.values():
|
|
80
|
+
if a.domain == primary_type:
|
|
81
|
+
agent = a
|
|
82
|
+
break
|
|
83
|
+
if agent is None and self.agents:
|
|
84
|
+
agent = next(iter(self.agents.values()))
|
|
85
|
+
if agent is None:
|
|
86
|
+
inv = Investigation()
|
|
87
|
+
inv.agent_name = "none"
|
|
88
|
+
inv.verdict = "SUSPICIOUS"
|
|
89
|
+
inv.reasoning = "No agents available"
|
|
90
|
+
return inv
|
|
91
|
+
|
|
92
|
+
# Create a synthetic trigger for the voltage drop
|
|
93
|
+
trigger = {
|
|
94
|
+
"type": primary_type,
|
|
95
|
+
"classification": "UNEXPECTED",
|
|
96
|
+
"caller_file": f"{module_name}.py",
|
|
97
|
+
"detail": {"voltage_drop": True, "voltage": voltage, "module": module_name},
|
|
98
|
+
}
|
|
99
|
+
inv = agent.investigate(trigger, context)
|
|
100
|
+
self.investigation_log.append(inv)
|
|
101
|
+
return inv
|
|
102
|
+
|
|
103
|
+
def get_findings(self, n=50, verdict=None) -> list:
|
|
104
|
+
"""Return recent findings, optionally filtered by verdict."""
|
|
105
|
+
items = list(self.investigation_log)
|
|
106
|
+
if verdict:
|
|
107
|
+
items = [i for i in items if i.verdict == verdict]
|
|
108
|
+
return [i.to_dict() if hasattr(i, "to_dict") else i for i in items[-n:]]
|
|
109
|
+
|
|
110
|
+
def get_threat_summary(self) -> dict:
|
|
111
|
+
"""Count of threats, suspicious, benign across the swarm."""
|
|
112
|
+
items = list(self.investigation_log)
|
|
113
|
+
counts = {"THREAT": 0, "SUSPICIOUS": 0, "BENIGN": 0}
|
|
114
|
+
for inv in items:
|
|
115
|
+
v = inv.verdict if hasattr(inv, "verdict") else "UNKNOWN"
|
|
116
|
+
counts[v] = counts.get(v, 0) + 1
|
|
117
|
+
|
|
118
|
+
agent_stats = {}
|
|
119
|
+
for name, agent in self.agents.items():
|
|
120
|
+
agent_stats[name] = dict(agent.stats)
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
"threats": counts.get("THREAT", 0),
|
|
124
|
+
"suspicious": counts.get("SUSPICIOUS", 0),
|
|
125
|
+
"benign": counts.get("BENIGN", 0),
|
|
126
|
+
"total_investigations": len(items),
|
|
127
|
+
"agents": agent_stats,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
def share_learning(self):
|
|
131
|
+
"""Sync behavioral models across all agents.
|
|
132
|
+
|
|
133
|
+
When one agent learns a pattern is BENIGN, all agents learn it.
|
|
134
|
+
When one agent identifies a THREAT pattern, all agents watch for it.
|
|
135
|
+
"""
|
|
136
|
+
# Merge all agent models into shared model
|
|
137
|
+
for agent in self.agents.values():
|
|
138
|
+
for phash, entry in agent.behavioral_model.items():
|
|
139
|
+
existing = self.shared_model.get(phash)
|
|
140
|
+
if existing is None or entry["last_seen"] > existing["last_seen"]:
|
|
141
|
+
self.shared_model[phash] = dict(entry)
|
|
142
|
+
|
|
143
|
+
# Distribute shared model back to all agents
|
|
144
|
+
for agent in self.agents.values():
|
|
145
|
+
for phash, entry in self.shared_model.items():
|
|
146
|
+
if phash not in agent.behavioral_model:
|
|
147
|
+
agent.behavioral_model[phash] = dict(entry)
|
|
148
|
+
else:
|
|
149
|
+
existing = agent.behavioral_model[phash]
|
|
150
|
+
if entry["last_seen"] > existing["last_seen"]:
|
|
151
|
+
existing["verdict"] = entry["verdict"]
|
|
152
|
+
existing["last_seen"] = entry["last_seen"]
|