sys-scan-agent 5.0.3__py3-none-any.whl → 5.0.3.dev0__py3-none-any.whl

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.
@@ -1 +1,162 @@
1
- # Model data files package
1
+ from __future__ import annotations
2
+ from typing import List, Dict, Optional, Any, Union
3
+ from pydantic import BaseModel, Field
4
+ import hashlib
5
+
6
+ # -----------------
7
+ # Core Report Schema (subset + extensions)
8
+ # -----------------
9
+
10
+ class Finding(BaseModel):
11
+ id: str
12
+ title: str
13
+ severity: str
14
+ # Legacy field name expected throughout enrichment pipeline. The C++ layer now emits
15
+ # base_severity_score instead; ingestion normalizes by copying that value into risk_score
16
+ # (and risk_total) if risk_score is absent. We retain risk_score as required so downstream
17
+ # code need not handle Optional[int].
18
+ risk_score: int
19
+ # Transitional visibility: surface base_severity_score if present in raw report (or if
20
+ # synthesized during normalization) for transparency / future migration to holistic risk.
21
+ base_severity_score: Optional[int] = None
22
+ description: Optional[str] = ""
23
+ metadata: Dict[str, Any] = Field(default_factory=dict)
24
+ operational_error: bool = False # if true, represents scanner operational issue, not security signal
25
+ # Extensions
26
+ category: Optional[str] = None
27
+ tags: List[str] = Field(default_factory=list)
28
+ risk_subscores: Optional[Dict[str, float]] = None # impact/exposure/anomaly/confidence
29
+ correlation_refs: List[str] = Field(default_factory=list)
30
+ baseline_status: Optional[str] = None # new|existing|unknown
31
+ severity_source: Optional[str] = None # raw|bumped|rule_engine
32
+ allowlist_reason: Optional[str] = None
33
+ probability_actionable: Optional[float] = None # calibrated probability (0-1)
34
+ graph_degree: Optional[int] = None # number of correlations referencing this finding
35
+ cluster_id: Optional[int] = None # correlation graph connected component id
36
+ rationale: Optional[list[str]] = None # explanations for risk
37
+ risk_total: Optional[int] = None # duplicate of risk_score as stable name for external consumption
38
+ host_role: Optional[str] = None # inferred host role classification (workstation|dev_workstation|bastion|lightweight_router|container_host)
39
+ host_role_rationale: Optional[List[str]] = None # signals used for role classification
40
+ metric_drift: Optional[dict] = None # when synthetic metric drift finding, carry metrics metadata
41
+
42
+ def identity_hash(self) -> str:
43
+ h = hashlib.sha256()
44
+ # Use stable core identity (scanner + id + title) stored externally
45
+ core = f"{self.id}\n{self.title}\n{self.severity}\n"
46
+ h.update(core.encode())
47
+ return h.hexdigest()
48
+
49
+ class ScannerResult(BaseModel):
50
+ scanner: str
51
+ finding_count: int
52
+ findings: List[Finding]
53
+
54
+ class Meta(BaseModel):
55
+ hostname: Optional[str] = None
56
+ tool_version: Optional[str] = None
57
+ json_schema_version: Optional[str] = None
58
+ # Extensions
59
+ host_id: Optional[str] = None
60
+ scan_id: Optional[str] = None
61
+
62
+ class Summary(BaseModel):
63
+ finding_count_total: Optional[int] = None
64
+ finding_count_emitted: Optional[int] = None
65
+ severity_counts: Dict[str, int] = Field(default_factory=dict)
66
+
67
+ class SummaryExtension(BaseModel):
68
+ total_risk_score: int
69
+ emitted_risk_score: Optional[int] = None
70
+
71
+ class Report(BaseModel):
72
+ meta: Meta
73
+ summary: Summary
74
+ results: List[ScannerResult]
75
+ collection_warnings: List[dict] = Field(default_factory=list)
76
+ scanner_errors: List[dict] = Field(default_factory=list)
77
+ summary_extension: SummaryExtension
78
+
79
+ # -----------------
80
+ # Correlation / Enrichment Models
81
+ # -----------------
82
+
83
+ class Correlation(BaseModel):
84
+ id: str
85
+ title: str
86
+ rationale: str
87
+ related_finding_ids: List[str]
88
+ risk_score_delta: int = 0
89
+ tags: List[str] = Field(default_factory=list)
90
+ severity: Optional[str] = None
91
+ predicate_hits: Optional[dict[str, list[str]]] = None # finding_id -> list of condition descriptors satisfied
92
+
93
+ class Reductions(BaseModel):
94
+ module_summary: Optional[dict] = None
95
+ suid_summary: Optional[dict] = None
96
+ network_summary: Optional[dict] = None
97
+ top_findings: List[dict] = Field(default_factory=list)
98
+ # Alias list for evaluation metrics (same content as top_findings) to express "top_risks" semantics
99
+ top_risks: Optional[List[dict]] = None
100
+
101
+ class MultiHostCorrelation(BaseModel):
102
+ type: str # e.g., module_propagation
103
+ key: str # module name
104
+ host_ids: List[str]
105
+ first_seen_recent: bool = True
106
+ rationale: Optional[str] = None
107
+
108
+ class Summaries(BaseModel):
109
+ executive_summary: Optional[str] = None
110
+ analyst: Optional[dict] = None
111
+ consistency_findings: Optional[list] = None # output of Prompt A
112
+ triage_summary: Optional[dict] = None # structured triage output (Prompt B)
113
+ action_narrative: Optional[str] = None # human narrative of actions (Prompt C)
114
+ metrics: Optional[dict] = None # token/latency metrics & alerts
115
+ causal_hypotheses: Optional[list[dict]] = None # experimental speculative root cause hypotheses
116
+ attack_coverage: Optional[dict] = None # ATT&CK technique coverage summary
117
+
118
+ class ActionItem(BaseModel):
119
+ priority: int
120
+ action: str
121
+ correlation_refs: List[str] = Field(default_factory=list)
122
+
123
+ class AgentWarning(BaseModel):
124
+ module: str
125
+ stage: str
126
+ error_type: str
127
+ message: str
128
+ severity: str = "warning" # warning|error|info
129
+ hint: Optional[str] = None
130
+
131
+ class FollowupResult(BaseModel):
132
+ finding_id: str
133
+ plan: List[str] = Field(default_factory=list)
134
+ results: dict = Field(default_factory=dict)
135
+ status: str = "executed" # executed|skipped|error
136
+ notes: Optional[str] = None
137
+
138
+ class EnrichedOutput(BaseModel):
139
+ version: str = "agent_mvp_1"
140
+ correlations: List[Correlation]
141
+ reductions: dict
142
+ summaries: Summaries
143
+ actions: List[ActionItem]
144
+ raw_reference: Optional[str] = None # sha256 of original report file
145
+ enriched_findings: Optional[List[Finding]] = None # flattened findings with subscores
146
+ correlation_graph: Optional[dict] = None # clusters & metrics
147
+ followups: Optional[List[FollowupResult]] = None
148
+ enrichment_results: Optional[dict] = None # aggregated follow-up verification outcomes
149
+ multi_host_correlation: Optional[List[MultiHostCorrelation]] = None
150
+ integrity: Optional[dict] = None # sha256 + signature verification status
151
+
152
+ class AgentState(BaseModel):
153
+ raw_report: Optional[dict] = None
154
+ report: Optional[Report] = None
155
+ correlations: List[Correlation] = Field(default_factory=list)
156
+ reductions: dict = Field(default_factory=dict)
157
+ summaries: Summaries = Field(default_factory=Summaries)
158
+ actions: List[ActionItem] = Field(default_factory=list)
159
+ followups: List[FollowupResult] = Field(default_factory=list)
160
+ enrichment_results: dict = Field(default_factory=dict)
161
+ multi_host_correlation: List[MultiHostCorrelation] = Field(default_factory=list)
162
+ agent_warnings: List[dict] = Field(default_factory=list)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sys-scan-agent
3
- Version: 5.0.3
3
+ Version: 5.0.3.dev0
4
4
  Summary: AI-powered intelligence layer for the sys-scan-graph security scanner.
5
5
  Home-page: https://github.com/J-mazz/sys-scan-graph
6
6
  Author: Joseph Mazzini
@@ -37,7 +37,6 @@ sys_scan_agent/metrics.py,sha256=G8h8G3Kgrcee0kgQ37ihtu8iHkasXbXyywY5YQOIF54,409
37
37
  sys_scan_agent/metrics_exporter.py,sha256=MBfpuxn077lEgIiKPJEzhz5qsZ21weEKPHXQCwIXJKU,8661
38
38
  sys_scan_agent/metrics_node.py,sha256=F7OQ3yU06Lftjmm_pbzfDu6ybrRrAqN4gfyAy0xBIFQ,6752
39
39
  sys_scan_agent/migration_v3.py,sha256=LTmzRtjQUwHfNtr-gpBdyCLQdiu_HjdU73oYqFVCIJQ,2190
40
- sys_scan_agent/models.py,sha256=e_bD4sJ31lpNt8jgfFSNNznVwB8ZO4m9qVMNmofbitM,6890
41
40
  sys_scan_agent/performance_baseline.py,sha256=btFtN943S2GahaHcTEJFrDJCnA-78XG0H6Gqf_q1I50,7520
42
41
  sys_scan_agent/pipeline.py,sha256=c3xdk5-gPK-UKq3ahYZDsjGZ3IEBU8EtDAFtDOo1w2M,14395
43
42
  sys_scan_agent/rarity_generate.py,sha256=BM2bgyUmNM_PhVIsCG7jFXTf3D8d5QQkohdnFbbl9t4,1851
@@ -68,10 +67,10 @@ sys_scan_agent/graph/routing.py,sha256=Tqs-F1QMAdCf9HecUvAQwZeD1X11HsvxB8uXZ_ZBf
68
67
  sys_scan_agent/graph/rules.py,sha256=SIh4lxVaBAkYNkRLB4KFvk7jL4rkD3MrL0IverfzAzc,9641
69
68
  sys_scan_agent/graph/summarization.py,sha256=cXc5Lh8-mBLy4PK_BHOchOAyT2OqdL52X2pBNoexuRc,14140
70
69
  sys_scan_agent/graph/utils.py,sha256=oM5-USXt9oIplvhb_5l0eJdq4MEtORs6mzF4RTzUu3Y,13891
71
- sys_scan_agent/models/__init__.py,sha256=E1y6UpMTykSzmlpCkoajfR-PzjUyhyVsFSBVKHaA8h4,26
70
+ sys_scan_agent/models/__init__.py,sha256=wgylTYY2kP1k03CFg5s9Q2DS2slpHfhvP0kf_w6xU3E,6888
72
71
  sys_scan_agent/models/mistral-security-scanner/adapter_config.json,sha256=MOx5bdifYA3m-aWPdxAIYnfluOJWJcMg3GYtSySPGkg,945
73
72
  sys_scan_agent/models/mistral-security-scanner/tokenizer.json,sha256=PFz0QCNxT7ObBeceQl-Ne5KAX_c_eYiwg7jIfwv4c5M,17209961
74
- sys_scan_agent-5.0.3.dist-info/licenses/LICENSE,sha256=VXRfqQBS6tVMaaDjs09EmMmLbC5Hb5yzhxdUgL0GHow,12135
73
+ sys_scan_agent-5.0.3.dev0.dist-info/licenses/LICENSE,sha256=VXRfqQBS6tVMaaDjs09EmMmLbC5Hb5yzhxdUgL0GHow,12135
75
74
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
75
  tests/conftest.py,sha256=YzOXysZy-Sw1WCGdeHeINiE1jrSNMA_4AwLw-0sFFys,512
77
76
  tests/test_advanced_router.py,sha256=GbE3PPDkgiKswr-VNTH9b3qhRG53ea2cVmFBCZQxYFY,1297
@@ -99,7 +98,7 @@ tests/test_fleet_report_schema.py,sha256=P3fH_BxVQoBfW9lDPLxLaf3EFTK9BvbJPsqOTaT
99
98
  tests/test_graph.py,sha256=iU1JMf4OWCcwh1cWjaN-L3rM0DXocq03E1SBPpNXwYM,86256
100
99
  tests/test_graph_analysis.py,sha256=D24NossWc1vi4HPiCKYjyf67TbSicIyvqZu3LwGGJP8,14107
101
100
  tests/test_graph_cycle.py,sha256=vRktx0h2SX0PxirWC9L68nSLvCSt_YmhdjS-g-w9FJs,2617
102
- tests/test_graph_main.py,sha256=XcT51eSaezsO330BKXNOAdMXNQCBklbY_KE6m8-exa4,44024
101
+ tests/test_graph_main.py,sha256=WTrXih5WiJcUYf3G82g7wn5E_GK-dw0QRR8sbFl1KPU,44062
103
102
  tests/test_graph_scaffold_workflow.py,sha256=fD8_6K3qZO8wO60bfAp_a5_Z02bg3Q1H5GqDOhXR_ho,3597
104
103
  tests/test_graph_utils.py,sha256=b4jnOiBGMb6GXFF2qerFOlHw7Z83b0PvnsCurCsX4Nw,25783
105
104
  tests/test_graph_with_toolnode.py,sha256=UXwTA_YNOs8npGR9InX8p_NHmpWQPmXrE1iwDQ7VBsQ,14870
@@ -141,8 +140,8 @@ tests/test_tools_enhanced.py,sha256=rJK6Hczu2mp24gdqubI7fy_m6VfPpm6dwbaTHTMHW6M,
141
140
  tests/test_utils.py,sha256=-iUT5p_a1R90QJndAJazYgO2xHZBjfErQnSKRIkHj54,7534
142
141
  tests/test_workflow_equivalence.py,sha256=OyGkCHeYLaVs47FG1098o6fyn0TAtuwh12b7oM5uWwI,25127
143
142
  tests/test_yaml_corruption.py,sha256=_5FLGIv7jppCDZncR_gwxDYTHjlxlErLjQD3vVO37AQ,2107
144
- sys_scan_agent-5.0.3.dist-info/METADATA,sha256=J1Gd4k0nhSJCEVYcmwRGx7RluxS8F4AEXYoCT0HLYIw,8537
145
- sys_scan_agent-5.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
146
- sys_scan_agent-5.0.3.dist-info/entry_points.txt,sha256=7rmYGTp4NvfbpxCqWu7wrHCKd-2BEZfnlPJoxSDKokg,105
147
- sys_scan_agent-5.0.3.dist-info/top_level.txt,sha256=3uTXqS-hAr46En_EzXhUkyBEZ6TrlSvUT_toOPE4Ero,48
148
- sys_scan_agent-5.0.3.dist-info/RECORD,,
143
+ sys_scan_agent-5.0.3.dev0.dist-info/METADATA,sha256=CTD8JqrwdShPlGEtInUZb5UTOuQT62MWYvT0bUpHIzE,8542
144
+ sys_scan_agent-5.0.3.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
145
+ sys_scan_agent-5.0.3.dev0.dist-info/entry_points.txt,sha256=7rmYGTp4NvfbpxCqWu7wrHCKd-2BEZfnlPJoxSDKokg,105
146
+ sys_scan_agent-5.0.3.dev0.dist-info/top_level.txt,sha256=3uTXqS-hAr46En_EzXhUkyBEZ6TrlSvUT_toOPE4Ero,48
147
+ sys_scan_agent-5.0.3.dev0.dist-info/RECORD,,
tests/test_graph_main.py CHANGED
@@ -12,7 +12,8 @@ import os
12
12
  import importlib.util
13
13
 
14
14
  # Load the main graph.py module directly
15
- graph_spec = importlib.util.spec_from_file_location("graph_main", "/home/joseph-mazzini/sys-scan-graph/agent/sys_scan_agent/graph.py")
15
+ graph_py_path = os.path.join(os.path.dirname(__file__), '..', 'sys_scan_agent', 'graph.py')
16
+ graph_spec = importlib.util.spec_from_file_location("graph_main", graph_py_path)
16
17
  if graph_spec is None or graph_spec.loader is None:
17
18
  raise ImportError("Could not load graph.py module")
18
19
  graph_main = importlib.util.module_from_spec(graph_spec)
sys_scan_agent/models.py DELETED
@@ -1,163 +0,0 @@
1
- from __future__ import annotations
2
- from typing import List, Dict, Optional, Any, Union
3
- from pydantic import BaseModel, Field
4
- import hashlib
5
-
6
- # -----------------
7
- # Core Report Schema (subset + extensions)
8
- # -----------------
9
-
10
- class Finding(BaseModel):
11
- id: str
12
- title: str
13
- severity: str
14
- # Legacy field name expected throughout enrichment pipeline. The C++ layer now emits
15
- # base_severity_score instead; ingestion normalizes by copying that value into risk_score
16
- # (and risk_total) if risk_score is absent. We retain risk_score as required so downstream
17
- # code need not handle Optional[int].
18
- risk_score: int
19
- # Transitional visibility: surface base_severity_score if present in raw report (or if
20
- # synthesized during normalization) for transparency / future migration to holistic risk.
21
- base_severity_score: Optional[int] = None
22
- description: Optional[str] = ""
23
- metadata: Dict[str, Any] = Field(default_factory=dict)
24
- operational_error: bool = False # if true, represents scanner operational issue, not security signal
25
- # Extensions
26
- category: Optional[str] = None
27
- tags: List[str] = Field(default_factory=list)
28
- risk_subscores: Optional[Dict[str, float]] = None # impact/exposure/anomaly/confidence
29
- correlation_refs: List[str] = Field(default_factory=list)
30
- baseline_status: Optional[str] = None # new|existing|unknown
31
- severity_source: Optional[str] = None # raw|bumped|rule_engine
32
- allowlist_reason: Optional[str] = None
33
- probability_actionable: Optional[float] = None # calibrated probability (0-1)
34
- graph_degree: Optional[int] = None # number of correlations referencing this finding
35
- cluster_id: Optional[int] = None # correlation graph connected component id
36
- rationale: Optional[list[str]] = None # explanations for risk
37
- risk_total: Optional[int] = None # duplicate of risk_score as stable name for external consumption
38
- host_role: Optional[str] = None # inferred host role classification (workstation|dev_workstation|bastion|lightweight_router|container_host)
39
- host_role_rationale: Optional[List[str]] = None # signals used for role classification
40
- metric_drift: Optional[dict] = None # when synthetic metric drift finding, carry metrics metadata
41
-
42
- def identity_hash(self) -> str:
43
- h = hashlib.sha256()
44
- # Use stable core identity (scanner + id + title) stored externally
45
- core = f"{self.id}\n{self.title}\n{self.severity}\n"
46
- h.update(core.encode())
47
- return h.hexdigest()
48
-
49
- class ScannerResult(BaseModel):
50
- scanner: str
51
- finding_count: int
52
- findings: List[Finding]
53
-
54
- class Meta(BaseModel):
55
- hostname: Optional[str] = None
56
- tool_version: Optional[str] = None
57
- json_schema_version: Optional[str] = None
58
- # Extensions
59
- host_id: Optional[str] = None
60
- scan_id: Optional[str] = None
61
-
62
- class Summary(BaseModel):
63
- finding_count_total: Optional[int] = None
64
- finding_count_emitted: Optional[int] = None
65
- severity_counts: Dict[str, int] = Field(default_factory=dict)
66
-
67
- class SummaryExtension(BaseModel):
68
- total_risk_score: int
69
- emitted_risk_score: Optional[int] = None
70
-
71
- class Report(BaseModel):
72
- meta: Meta
73
- summary: Summary
74
- results: List[ScannerResult]
75
- collection_warnings: List[dict] = Field(default_factory=list)
76
- scanner_errors: List[dict] = Field(default_factory=list)
77
- summary_extension: SummaryExtension
78
-
79
- # -----------------
80
- # Correlation / Enrichment Models
81
- # -----------------
82
-
83
- class Correlation(BaseModel):
84
- id: str
85
- title: str
86
- rationale: str
87
- related_finding_ids: List[str]
88
- risk_score_delta: int = 0
89
- tags: List[str] = Field(default_factory=list)
90
- severity: Optional[str] = None
91
- predicate_hits: Optional[dict[str, list[str]]] = None # finding_id -> list of condition descriptors satisfied
92
-
93
- class Reductions(BaseModel):
94
- module_summary: Optional[dict] = None
95
- suid_summary: Optional[dict] = None
96
- network_summary: Optional[dict] = None
97
- top_findings: List[dict] = Field(default_factory=list)
98
- # Alias list for evaluation metrics (same content as top_findings) to express "top_risks" semantics
99
- top_risks: Optional[List[dict]] = None
100
-
101
- class MultiHostCorrelation(BaseModel):
102
- type: str # e.g., module_propagation
103
- key: str # module name
104
- host_ids: List[str]
105
- first_seen_recent: bool = True
106
- rationale: Optional[str] = None
107
-
108
- class Summaries(BaseModel):
109
- executive_summary: Optional[str] = None
110
- analyst: Optional[dict] = None
111
- consistency_findings: Optional[list] = None # output of Prompt A
112
- triage_summary: Optional[dict] = None # structured triage output (Prompt B)
113
- action_narrative: Optional[str] = None # human narrative of actions (Prompt C)
114
- metrics: Optional[dict] = None # token/latency metrics & alerts
115
- causal_hypotheses: Optional[list[dict]] = None # experimental speculative root cause hypotheses
116
- attack_coverage: Optional[dict] = None # ATT&CK technique coverage summary
117
-
118
- class ActionItem(BaseModel):
119
- priority: int
120
- action: str
121
- correlation_refs: List[str] = Field(default_factory=list)
122
-
123
- class AgentWarning(BaseModel):
124
- module: str
125
- stage: str
126
- error_type: str
127
- message: str
128
- severity: str = "warning" # warning|error|info
129
- hint: Optional[str] = None
130
-
131
- class FollowupResult(BaseModel):
132
- finding_id: str
133
- plan: List[str] = Field(default_factory=list)
134
- results: dict = Field(default_factory=dict)
135
- status: str = "executed" # executed|skipped|error
136
- notes: Optional[str] = None
137
-
138
- class EnrichedOutput(BaseModel):
139
- version: str = "agent_mvp_1"
140
- correlations: List[Correlation]
141
- reductions: dict
142
- summaries: Summaries
143
- actions: List[ActionItem]
144
- raw_reference: Optional[str] = None # sha256 of original report file
145
- enriched_findings: Optional[List[Finding]] = None # flattened findings with subscores
146
- correlation_graph: Optional[dict] = None # clusters & metrics
147
- followups: Optional[List[FollowupResult]] = None
148
- enrichment_results: Optional[dict] = None # aggregated follow-up verification outcomes
149
- multi_host_correlation: Optional[List[MultiHostCorrelation]] = None
150
- integrity: Optional[dict] = None # sha256 + signature verification status
151
-
152
- class AgentState(BaseModel):
153
- raw_report: Optional[dict] = None
154
- report: Optional[Report] = None
155
- correlations: List[Correlation] = Field(default_factory=list)
156
- reductions: dict = Field(default_factory=dict)
157
- summaries: Summaries = Field(default_factory=Summaries)
158
- actions: List[ActionItem] = Field(default_factory=list)
159
- followups: List[FollowupResult] = Field(default_factory=list)
160
- enrichment_results: dict = Field(default_factory=dict)
161
- multi_host_correlation: List[MultiHostCorrelation] = Field(default_factory=list)
162
- agent_warnings: List[dict] = Field(default_factory=list)
163
-