agent-audit 0.1.0__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.
Files changed (37) hide show
  1. agent_audit/__init__.py +3 -0
  2. agent_audit/__main__.py +13 -0
  3. agent_audit/cli/__init__.py +1 -0
  4. agent_audit/cli/commands/__init__.py +1 -0
  5. agent_audit/cli/commands/init.py +44 -0
  6. agent_audit/cli/commands/inspect.py +236 -0
  7. agent_audit/cli/commands/scan.py +329 -0
  8. agent_audit/cli/formatters/__init__.py +1 -0
  9. agent_audit/cli/formatters/json.py +138 -0
  10. agent_audit/cli/formatters/sarif.py +155 -0
  11. agent_audit/cli/formatters/terminal.py +221 -0
  12. agent_audit/cli/main.py +34 -0
  13. agent_audit/config/__init__.py +1 -0
  14. agent_audit/config/ignore.py +477 -0
  15. agent_audit/core_utils/__init__.py +1 -0
  16. agent_audit/models/__init__.py +18 -0
  17. agent_audit/models/finding.py +159 -0
  18. agent_audit/models/risk.py +77 -0
  19. agent_audit/models/tool.py +182 -0
  20. agent_audit/rules/__init__.py +6 -0
  21. agent_audit/rules/engine.py +503 -0
  22. agent_audit/rules/loader.py +160 -0
  23. agent_audit/scanners/__init__.py +5 -0
  24. agent_audit/scanners/base.py +32 -0
  25. agent_audit/scanners/config_scanner.py +390 -0
  26. agent_audit/scanners/mcp_config_scanner.py +321 -0
  27. agent_audit/scanners/mcp_inspector.py +421 -0
  28. agent_audit/scanners/python_scanner.py +544 -0
  29. agent_audit/scanners/secret_scanner.py +521 -0
  30. agent_audit/utils/__init__.py +21 -0
  31. agent_audit/utils/compat.py +98 -0
  32. agent_audit/utils/mcp_client.py +343 -0
  33. agent_audit/version.py +3 -0
  34. agent_audit-0.1.0.dist-info/METADATA +219 -0
  35. agent_audit-0.1.0.dist-info/RECORD +37 -0
  36. agent_audit-0.1.0.dist-info/WHEEL +4 -0
  37. agent_audit-0.1.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,77 @@
1
+ """Risk assessment models."""
2
+
3
+ from enum import Enum
4
+ from dataclasses import dataclass
5
+ from typing import Optional
6
+
7
+
8
+ def _normalize_path(path: str) -> str:
9
+ """Normalize path to use forward slashes for cross-platform consistency."""
10
+ return path.replace("\\", "/")
11
+
12
+
13
+ class Severity(Enum):
14
+ """Severity levels for findings."""
15
+ CRITICAL = "critical"
16
+ HIGH = "high"
17
+ MEDIUM = "medium"
18
+ LOW = "low"
19
+ INFO = "info"
20
+
21
+ def __lt__(self, other: "Severity") -> bool:
22
+ order = [Severity.INFO, Severity.LOW, Severity.MEDIUM, Severity.HIGH, Severity.CRITICAL]
23
+ return order.index(self) < order.index(other)
24
+
25
+ def __le__(self, other: "Severity") -> bool:
26
+ return self == other or self < other
27
+
28
+ def __gt__(self, other: "Severity") -> bool:
29
+ return not self <= other
30
+
31
+ def __ge__(self, other: "Severity") -> bool:
32
+ return not self < other
33
+
34
+
35
+ class Category(Enum):
36
+ """Categories for security findings."""
37
+ COMMAND_INJECTION = "command_injection"
38
+ DATA_EXFILTRATION = "data_exfiltration"
39
+ PRIVILEGE_ESCALATION = "privilege_escalation"
40
+ SUPPLY_CHAIN = "supply_chain"
41
+ CREDENTIAL_EXPOSURE = "credential_exposure"
42
+ PROMPT_INJECTION = "prompt_injection"
43
+ EXCESSIVE_PERMISSION = "excessive_permission"
44
+
45
+
46
+ @dataclass
47
+ class Location:
48
+ """Code location for a finding."""
49
+ file_path: str
50
+ start_line: int
51
+ end_line: int
52
+ start_column: Optional[int] = None
53
+ end_column: Optional[int] = None
54
+ snippet: Optional[str] = None
55
+
56
+ def __post_init__(self):
57
+ """Normalize file_path to use forward slashes for cross-platform consistency."""
58
+ self.file_path = _normalize_path(self.file_path)
59
+
60
+
61
+ @dataclass
62
+ class RiskScore:
63
+ """Risk score calculation result."""
64
+ score: float # 0.0 - 10.0
65
+ factors: dict # Contributing factors and their weights
66
+
67
+ def is_high_risk(self) -> bool:
68
+ """Check if this represents high risk."""
69
+ return self.score >= 7.0
70
+
71
+ def is_medium_risk(self) -> bool:
72
+ """Check if this represents medium risk."""
73
+ return 4.0 <= self.score < 7.0
74
+
75
+ def is_low_risk(self) -> bool:
76
+ """Check if this represents low risk."""
77
+ return self.score < 4.0
@@ -0,0 +1,182 @@
1
+ """Tool definition models for agent analysis."""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Optional, Set
5
+ from enum import Enum, auto
6
+
7
+
8
+ class PermissionType(Enum):
9
+ """Permission types that tools may require."""
10
+ FILE_READ = auto()
11
+ FILE_WRITE = auto()
12
+ FILE_DELETE = auto()
13
+ SHELL_EXEC = auto()
14
+ NETWORK_OUTBOUND = auto()
15
+ NETWORK_INBOUND = auto()
16
+ DATABASE_READ = auto()
17
+ DATABASE_WRITE = auto()
18
+ SECRET_ACCESS = auto()
19
+ BROWSER_CONTROL = auto()
20
+ PROCESS_SPAWN = auto()
21
+
22
+
23
+ class RiskLevel(Enum):
24
+ """Risk levels for tools."""
25
+ SAFE = 1
26
+ LOW = 2
27
+ MEDIUM = 3
28
+ HIGH = 4
29
+ CRITICAL = 5
30
+
31
+
32
+ @dataclass
33
+ class ToolParameter:
34
+ """Tool parameter definition."""
35
+ name: str
36
+ type: str
37
+ required: bool = False
38
+ description: Optional[str] = None
39
+ allows_arbitrary_input: bool = False
40
+ sanitization_present: bool = False
41
+
42
+
43
+ @dataclass
44
+ class ToolDefinition:
45
+ """
46
+ Agent tool definition.
47
+
48
+ Represents a tool that an agent can call, with its permissions,
49
+ risk level, and security properties.
50
+ """
51
+ name: str
52
+ description: str
53
+ source_file: str
54
+ source_line: int
55
+
56
+ permissions: Set[PermissionType] = field(default_factory=set)
57
+ risk_level: RiskLevel = RiskLevel.LOW
58
+ parameters: List[ToolParameter] = field(default_factory=list)
59
+
60
+ # MCP-specific fields
61
+ mcp_server: Optional[str] = None
62
+ mcp_server_verified: bool = False
63
+
64
+ # Security properties
65
+ has_input_validation: bool = False
66
+ has_output_sanitization: bool = False
67
+ runs_in_sandbox: bool = False
68
+ requires_approval: bool = False
69
+
70
+ # Capability flags (derived from permissions)
71
+ can_execute_code: bool = False
72
+ can_access_filesystem: bool = False
73
+ can_access_network: bool = False
74
+ can_access_secrets: bool = False
75
+
76
+ def calculate_risk_score(self) -> float:
77
+ """
78
+ Calculate risk score (0.0 - 10.0).
79
+
80
+ Takes into account:
81
+ - Permission weights (shell exec is highest risk)
82
+ - Mitigating factors (input validation, sandbox)
83
+ - MCP server verification status
84
+ """
85
+ score = 0.0
86
+
87
+ # Permission weights
88
+ permission_weights = {
89
+ PermissionType.SHELL_EXEC: 3.0,
90
+ PermissionType.SECRET_ACCESS: 2.5,
91
+ PermissionType.FILE_DELETE: 2.0,
92
+ PermissionType.DATABASE_WRITE: 2.0,
93
+ PermissionType.NETWORK_OUTBOUND: 1.5,
94
+ PermissionType.FILE_WRITE: 1.5,
95
+ PermissionType.PROCESS_SPAWN: 2.0,
96
+ PermissionType.BROWSER_CONTROL: 1.5,
97
+ PermissionType.FILE_READ: 0.5,
98
+ PermissionType.DATABASE_READ: 0.5,
99
+ PermissionType.NETWORK_INBOUND: 1.0,
100
+ }
101
+
102
+ for perm in self.permissions:
103
+ score += permission_weights.get(perm, 0.5)
104
+
105
+ # Mitigating factors
106
+ if self.has_input_validation:
107
+ score *= 0.7
108
+ if self.runs_in_sandbox:
109
+ score *= 0.5
110
+ if self.requires_approval:
111
+ score *= 0.6
112
+
113
+ # MCP verification penalty
114
+ if self.mcp_server and not self.mcp_server_verified:
115
+ score *= 1.3
116
+
117
+ return min(10.0, score)
118
+
119
+ def infer_risk_level(self) -> RiskLevel:
120
+ """Infer risk level from calculated score."""
121
+ score = self.calculate_risk_score()
122
+ if score >= 8.0:
123
+ return RiskLevel.CRITICAL
124
+ elif score >= 6.0:
125
+ return RiskLevel.HIGH
126
+ elif score >= 4.0:
127
+ return RiskLevel.MEDIUM
128
+ elif score >= 2.0:
129
+ return RiskLevel.LOW
130
+ else:
131
+ return RiskLevel.SAFE
132
+
133
+ def update_capability_flags(self):
134
+ """Update capability flags based on permissions."""
135
+ self.can_execute_code = PermissionType.SHELL_EXEC in self.permissions
136
+ self.can_access_filesystem = any(
137
+ p in self.permissions for p in [
138
+ PermissionType.FILE_READ,
139
+ PermissionType.FILE_WRITE,
140
+ PermissionType.FILE_DELETE
141
+ ]
142
+ )
143
+ self.can_access_network = any(
144
+ p in self.permissions for p in [
145
+ PermissionType.NETWORK_OUTBOUND,
146
+ PermissionType.NETWORK_INBOUND
147
+ ]
148
+ )
149
+ self.can_access_secrets = PermissionType.SECRET_ACCESS in self.permissions
150
+
151
+ def to_dict(self) -> dict:
152
+ """Convert to dictionary for JSON serialization."""
153
+ return {
154
+ "name": self.name,
155
+ "description": self.description,
156
+ "source_file": self.source_file,
157
+ "source_line": self.source_line,
158
+ "permissions": [p.name for p in self.permissions],
159
+ "risk_level": self.risk_level.name,
160
+ "risk_score": self.calculate_risk_score(),
161
+ "parameters": [
162
+ {
163
+ "name": p.name,
164
+ "type": p.type,
165
+ "required": p.required,
166
+ "description": p.description,
167
+ "allows_arbitrary_input": p.allows_arbitrary_input,
168
+ "sanitization_present": p.sanitization_present,
169
+ }
170
+ for p in self.parameters
171
+ ],
172
+ "mcp_server": self.mcp_server,
173
+ "mcp_server_verified": self.mcp_server_verified,
174
+ "has_input_validation": self.has_input_validation,
175
+ "has_output_sanitization": self.has_output_sanitization,
176
+ "runs_in_sandbox": self.runs_in_sandbox,
177
+ "requires_approval": self.requires_approval,
178
+ "can_execute_code": self.can_execute_code,
179
+ "can_access_filesystem": self.can_access_filesystem,
180
+ "can_access_network": self.can_access_network,
181
+ "can_access_secrets": self.can_access_secrets,
182
+ }
@@ -0,0 +1,6 @@
1
+ """Rule engine and loader for Agent Security Suite."""
2
+
3
+ from agent_audit.rules.engine import RuleEngine
4
+ from agent_audit.rules.loader import RuleLoader
5
+
6
+ __all__ = ["RuleEngine", "RuleLoader"]