Repository navigation
Expand file tree
/
Copy pathtypes.go
More file actions
333 lines (303 loc) · 15 KB
/
Copy pathtypes.go
File metadata and controls
333 lines (303 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
package scanner
import (
"encoding/json"
"time"
)
// Scanner status constants
const (
// ScannerStatusAvailable means the scanner is known to mcpproxy but
// not enabled. Toggling it on moves it into ScannerStatusPulling.
ScannerStatusAvailable = "available"
// ScannerStatusPulling means the Docker image is currently being
// downloaded in the background. UI should show a spinner.
ScannerStatusPulling = "pulling"
// ScannerStatusInstalled means the image is present locally and the
// scanner is ready to run. No required API keys are configured yet.
ScannerStatusInstalled = "installed"
// ScannerStatusConfigured means the image is present AND user-supplied
// env (e.g. API keys) have been stored.
ScannerStatusConfigured = "configured"
// ScannerStatusError means the last operation (typically a pull) failed.
// ErrorMsg carries the reason. The UI should offer a "Retry" button.
ScannerStatusError = "error"
)
// Scan job status constants
const (
ScanJobStatusPending = "pending"
ScanJobStatusRunning = "running"
ScanJobStatusCompleted = "completed"
ScanJobStatusFailed = "failed"
ScanJobStatusCancelled = "cancelled"
)
// Scan finding severity constants
const (
SeverityCritical = "critical"
SeverityHigh = "high"
SeverityMedium = "medium"
SeverityLow = "low"
SeverityInfo = "info"
)
// EnvRequirement represents a required or optional environment variable for a scanner
type EnvRequirement struct {
Key string `json:"key"`
Label string `json:"label"`
Secret bool `json:"secret"`
}
// ScannerPlugin represents a security scanner plugin
type ScannerPlugin struct {
ID string `json:"id"`
Name string `json:"name"`
Vendor string `json:"vendor"`
Description string `json:"description"`
License string `json:"license"`
Homepage string `json:"homepage"`
DockerImage string `json:"docker_image"`
Inputs []string `json:"inputs"` // "source", "mcp_connection", "container_image"
Outputs []string `json:"outputs"` // "sarif"
RequiredEnv []EnvRequirement `json:"required_env"`
OptionalEnv []EnvRequirement `json:"optional_env"`
Command []string `json:"command"`
Timeout string `json:"timeout"`
NetworkReq bool `json:"network_required"`
// InProcess marks a Docker-less, built-in scanner that the engine runs
// in-process (e.g. the tool-description TPA analyzer). Such scanners have
// no Docker image to pull, are always "installed", and skip the
// image-availability gate so they run even for remote servers with no
// source/Docker (MCP-2082).
InProcess bool `json:"in_process,omitempty"`
// Runtime state (not in registry)
Status string `json:"status"` // available, installed, configured, error
InstalledAt time.Time `json:"installed_at,omitempty"`
ConfiguredEnv map[string]string `json:"configured_env,omitempty"` // Set env values (secrets redacted in API)
ImageOverride string `json:"image_override,omitempty"` // User override for DockerImage
LastUsedAt time.Time `json:"last_used_at,omitempty"`
ErrorMsg string `json:"error_message,omitempty"`
Custom bool `json:"custom,omitempty"` // User-added (not from registry)
}
// EffectiveImage returns ImageOverride if set, otherwise DockerImage.
func (s *ScannerPlugin) EffectiveImage() string {
if s.ImageOverride != "" {
return s.ImageOverride
}
return s.DockerImage
}
// Scan pass constants
const (
ScanPassSecurityScan = 1 // Fast pass: source code + lockfile scanning
ScanPassSupplyChainAudit = 2 // Background pass: full filesystem CVE analysis
)
// Scan context limits
const (
MaxScanLogLines = 5000 // Max lines of stdout/stderr per scanner
MaxScannedFiles = 10000 // Max file entries in scanned_files list
MaxScansPerServer = 20 // Keep last N scans per server
MaxLogBytes = 500000 // ~500KB max per log field
)
// ScanJob represents a scan execution job
type ScanJob struct {
ID string `json:"id"`
ServerName string `json:"server_name"`
Status string `json:"status"` // pending, running, completed, failed, cancelled
ScanPass int `json:"scan_pass"` // 1 = security scan (fast), 2 = supply chain audit (background)
Scanners []string `json:"scanners"`
StartedAt time.Time `json:"started_at"`
CompletedAt time.Time `json:"completed_at,omitempty"`
Error string `json:"error,omitempty"`
DryRun bool `json:"dry_run,omitempty"`
// Per-scanner status
ScannerStatuses []ScannerJobStatus `json:"scanner_statuses"`
// Scan context — what was scanned and how
ScanContext *ScanContext `json:"scan_context,omitempty"`
}
// ScanJobMeta is a lightweight projection of a scan job, persisted in a
// dedicated index bucket so that companion-job lookups during report
// aggregation never deserialize the full job payload (whose ScannerStatuses can
// carry large stdout/stderr blobs). This keeps report latency independent of a
// server's scan history. See MCP-2205.
type ScanJobMeta struct {
ID string `json:"id"`
ServerName string `json:"server_name"`
Status string `json:"status"`
ScanPass int `json:"scan_pass"`
StartedAt time.Time `json:"started_at"`
CompletedAt time.Time `json:"completed_at,omitempty"`
}
// ScanJobSummary is a lightweight view of a scan job for history listing
type ScanJobSummary struct {
ID string `json:"id"`
ServerName string `json:"server_name"`
Status string `json:"status"`
ScanPass int `json:"scan_pass"`
StartedAt time.Time `json:"started_at"`
CompletedAt time.Time `json:"completed_at,omitempty"`
FindingsCount int `json:"findings_count"`
RiskScore int `json:"risk_score"`
Scanners []string `json:"scanners"`
}
// ScanContext describes what was scanned and how the source was resolved.
// This gives users full transparency into what the scanners actually checked.
type ScanContext struct {
SourceMethod string `json:"source_method"` // "docker_extract", "working_dir", "local_path", "url", "none"
SourcePath string `json:"source_path"` // Actual path/URL that was scanned
DockerIsolation bool `json:"docker_isolation"` // Whether server runs in Docker
ContainerID string `json:"container_id,omitempty"` // Docker container ID (if applicable)
ContainerImage string `json:"container_image,omitempty"` // Docker image used
ServerProtocol string `json:"server_protocol"` // stdio, http, sse
ServerCommand string `json:"server_command,omitempty"` // Command used to start server
ToolsExported int `json:"tools_exported,omitempty"` // Number of tool definitions exported for scanning
ScannedFiles []string `json:"scanned_files,omitempty"` // List of files that were scanned (capped at MaxScannedFiles)
TotalFiles int `json:"total_files"` // Total file count (may be > len(ScannedFiles) if capped)
TotalSizeBytes int64 `json:"total_size_bytes"` // Total size of scanned source
}
// ScannerJobStatus tracks a single scanner's execution within a scan job
type ScannerJobStatus struct {
ScannerID string `json:"scanner_id"`
Status string `json:"status"`
StartedAt time.Time `json:"started_at,omitempty"`
CompletedAt time.Time `json:"completed_at,omitempty"`
Error string `json:"error,omitempty"`
FindingsCount int `json:"findings_count"`
Stdout string `json:"stdout,omitempty"` // Scanner stdout (for log viewing)
Stderr string `json:"stderr,omitempty"` // Scanner stderr (for log viewing)
ExitCode int `json:"exit_code"`
}
// User-facing threat category constants
const (
ThreatToolPoisoning = "tool_poisoning" // Hidden instructions in tool descriptions
ThreatPromptInjection = "prompt_injection" // Malicious payloads in tool responses
ThreatRugPull = "rug_pull" // Tool definitions changed after approval
ThreatSupplyChain = "supply_chain" // Known CVEs in dependencies
ThreatMaliciousCode = "malicious_code" // Malware, backdoors, suspicious code
ThreatUncategorized = "uncategorized" // Other findings
)
// User-facing severity levels (simpler than CVSS)
const (
ThreatLevelDangerous = "dangerous" // Blocks approval: tool poisoning, active injection
ThreatLevelWarning = "warning" // Rug pull, high CVEs
ThreatLevelInfo = "info" // Low CVEs, informational
)
// ScanFinding represents an individual security finding
type ScanFinding struct {
RuleID string `json:"rule_id"`
Severity string `json:"severity"` // critical, high, medium, low, info
Category string `json:"category"` // SARIF category
ThreatType string `json:"threat_type"` // User-facing: tool_poisoning, prompt_injection, rug_pull, supply_chain
ThreatLevel string `json:"threat_level"` // User-facing: dangerous, warning, info
Title string `json:"title"`
Description string `json:"description"`
Location string `json:"location,omitempty"`
Scanner string `json:"scanner"`
HelpURI string `json:"help_uri,omitempty"` // Link to CVE/advisory details
CVSSScore float64 `json:"cvss_score,omitempty"` // CVSS severity score (0-10)
PackageName string `json:"package_name,omitempty"` // Affected package
InstalledVersion string `json:"installed_version,omitempty"` // Current version
FixedVersion string `json:"fixed_version,omitempty"` // Version with fix
ScanPass int `json:"scan_pass,omitempty"` // 1 = security scan, 2 = supply chain audit
Evidence string `json:"evidence,omitempty"` // The text/content that triggered the finding
// SupplyChainAudit marks findings that belong in the "Supply Chain Audit (CVEs)"
// UI section regardless of which pass produced them. True only for real CVE/package
// vulnerabilities (CVE-prefixed rule ID or a populated PackageName). AI scanner and
// other non-package findings stay false so the UI can route them to their proper
// threat_type group instead of the CVE section.
SupplyChainAudit bool `json:"supply_chain_audit,omitempty"`
}
// ScanReport represents aggregated scan results for a server
type ScanReport struct {
ID string `json:"id"`
JobID string `json:"job_id"`
ServerName string `json:"server_name"`
ScannerID string `json:"scanner_id"`
Findings []ScanFinding `json:"findings"`
RiskScore int `json:"risk_score"` // 0-100
SarifRaw json.RawMessage `json:"sarif_raw,omitempty"`
ScannedAt time.Time `json:"scanned_at"`
}
// AggregatedReport combines results from all scanners for a single scan job
type AggregatedReport struct {
JobID string `json:"job_id"`
ServerName string `json:"server_name"`
Findings []ScanFinding `json:"findings"`
RiskScore int `json:"risk_score"`
Summary ReportSummary `json:"summary"`
ScannedAt time.Time `json:"scanned_at"`
Reports []ScanReport `json:"reports"`
ScannersRun int `json:"scanners_run"` // How many scanners actually produced results
ScannersFailed int `json:"scanners_failed"` // How many scanners failed
ScannersTotal int `json:"scanners_total"` // Total scanners attempted
ScanComplete bool `json:"scan_complete"` // True only if at least one scanner succeeded
EmptyScan bool `json:"empty_scan"` // True when scanners ran but had no files to analyze
// Two-pass scan tracking
Pass1Complete bool `json:"pass1_complete"` // Security scan (fast) done
Pass2Complete bool `json:"pass2_complete"` // Supply chain audit done
Pass2Running bool `json:"pass2_running"` // Supply chain audit in progress
// Scan context from the primary job (for report page display)
ScanContext *ScanContext `json:"scan_context,omitempty"`
ScannerStatuses []ScannerJobStatus `json:"scanner_statuses,omitempty"` // Per-scanner execution logs
}
// ReportSummary provides counts by severity and threat level
type ReportSummary struct {
Critical int `json:"critical"`
High int `json:"high"`
Medium int `json:"medium"`
Low int `json:"low"`
Info int `json:"info"`
Total int `json:"total"`
Dangerous int `json:"dangerous"` // Threat level: tool poisoning, prompt injection
Warnings int `json:"warnings"` // Threat level: rug pull, high CVEs
InfoLevel int `json:"info_level"` // Threat level: low CVEs, informational
}
// IntegrityBaseline represents the approved integrity state for a server
type IntegrityBaseline struct {
ServerName string `json:"server_name"`
ImageDigest string `json:"image_digest"`
SourceHash string `json:"source_hash"`
LockfileHash string `json:"lockfile_hash"`
DiffManifest []string `json:"diff_manifest,omitempty"`
ToolHashes map[string]string `json:"tool_hashes,omitempty"`
ScanReportIDs []string `json:"scan_report_ids,omitempty"`
ApprovedAt time.Time `json:"approved_at"`
ApprovedBy string `json:"approved_by"`
}
// SecurityOverview provides dashboard aggregate stats
type SecurityOverview struct {
TotalScans int `json:"total_scans"`
ActiveScans int `json:"active_scans"`
FindingsBySeverity ReportSummary `json:"findings_by_severity"`
ScannersInstalled int `json:"scanners_installed"`
ScannersEnabled int `json:"scanners_enabled"` // Subset of installed that the engine will run (status installed or configured)
ServersScanned int `json:"servers_scanned"`
LastScanAt time.Time `json:"last_scan_at,omitempty"`
DockerAvailable bool `json:"docker_available"`
}
// MarshalBinary implements encoding.BinaryMarshaler
func (s *ScannerPlugin) MarshalBinary() ([]byte, error) {
return json.Marshal(s)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (s *ScannerPlugin) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, s)
}
// MarshalBinary implements encoding.BinaryMarshaler
func (j *ScanJob) MarshalBinary() ([]byte, error) {
return json.Marshal(j)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (j *ScanJob) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, j)
}
// MarshalBinary implements encoding.BinaryMarshaler
func (r *ScanReport) MarshalBinary() ([]byte, error) {
return json.Marshal(r)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (r *ScanReport) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, r)
}
// MarshalBinary implements encoding.BinaryMarshaler
func (b *IntegrityBaseline) MarshalBinary() ([]byte, error) {
return json.Marshal(b)
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler
func (b *IntegrityBaseline) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, b)
}