feat(privacy): scan ingested sources for customer-identifying data
Detection-only scan (internal/privacy) attached to every AnalysisResult: a customer-domain guess plus a findings list (category, file, line, match, hint), ported from the KB grep playbook. Runs on archive uploads and the serialized Redfish tree; gated by LOGPILE_PRIVACY_SCAN (default on). Surfaced at GET /api/privacy-scan, in the "Customer data" UI panel, and as privacy_report.json in the raw-export bundle. IP policy keeps RFC1918 and example ranges out of findings; allowlist covers standards-body and vendor infrastructure domains. No customer tokens in the repo. See ADL-066 and bible-local/docs/privacy-scan.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
3311bafd8e
commit
4a4910f207
+74
-33
@@ -9,19 +9,60 @@ const (
|
||||
|
||||
// AnalysisResult contains all parsed data from an archive
|
||||
type AnalysisResult struct {
|
||||
Filename string `json:"filename"`
|
||||
SourceType string `json:"source_type,omitempty"` // archive | api
|
||||
Protocol string `json:"protocol,omitempty"` // redfish | ipmi
|
||||
TargetHost string `json:"target_host,omitempty"` // BMC host for live collect
|
||||
SourceTimezone string `json:"source_timezone,omitempty"` // Source timezone/offset used during collection (e.g. +08:00)
|
||||
CollectedAt time.Time `json:"collected_at,omitempty"` // Collection/upload timestamp
|
||||
InventoryLastModifiedAt time.Time `json:"inventory_last_modified_at,omitempty"` // Redfish inventory last modified (InventoryData/Status)
|
||||
RawPayloads map[string]any `json:"raw_payloads,omitempty"` // Additional source payloads (e.g. Redfish tree)
|
||||
CollectionErrors []CollectionError `json:"collection_errors,omitempty"` // BMC-reported failures to collect specific sections
|
||||
Events []Event `json:"events"`
|
||||
FRU []FRUInfo `json:"fru"`
|
||||
Sensors []SensorReading `json:"sensors"`
|
||||
Hardware *HardwareConfig `json:"hardware"`
|
||||
Filename string `json:"filename"`
|
||||
SourceType string `json:"source_type,omitempty"` // archive | api
|
||||
Protocol string `json:"protocol,omitempty"` // redfish | ipmi
|
||||
TargetHost string `json:"target_host,omitempty"` // BMC host for live collect
|
||||
SourceTimezone string `json:"source_timezone,omitempty"` // Source timezone/offset used during collection (e.g. +08:00)
|
||||
CollectedAt time.Time `json:"collected_at,omitempty"` // Collection/upload timestamp
|
||||
InventoryLastModifiedAt time.Time `json:"inventory_last_modified_at,omitempty"` // Redfish inventory last modified (InventoryData/Status)
|
||||
RawPayloads map[string]any `json:"raw_payloads,omitempty"` // Additional source payloads (e.g. Redfish tree)
|
||||
CollectionErrors []CollectionError `json:"collection_errors,omitempty"` // BMC-reported failures to collect specific sections
|
||||
Events []Event `json:"events"`
|
||||
FRU []FRUInfo `json:"fru"`
|
||||
Sensors []SensorReading `json:"sensors"`
|
||||
Hardware *HardwareConfig `json:"hardware"`
|
||||
PrivacyScan *PrivacyScan `json:"privacy_scan,omitempty"` // customer-data / anonymization scan of the source files
|
||||
}
|
||||
|
||||
// PrivacyScan is the result of scanning the source files for customer-identifying
|
||||
// and site-identifying data (DNS suffix, AD domain, NTP/DNS/syslog host names,
|
||||
// timezone, e-mail, public IPs, cert CN/SAN, FRU location fields). Detection only:
|
||||
// it never modifies the source. See bible-local/docs/privacy-scan.md.
|
||||
type PrivacyScan struct {
|
||||
FilesScanned int `json:"files_scanned"`
|
||||
Customers []CustomerGuess `json:"customers,omitempty"`
|
||||
Findings []PrivacyFinding `json:"findings,omitempty"`
|
||||
Summary PrivacySummary `json:"summary"`
|
||||
}
|
||||
|
||||
// CustomerGuess is a registrable domain that most likely identifies the customer,
|
||||
// derived from the scan findings.
|
||||
type CustomerGuess struct {
|
||||
Domain string `json:"domain"`
|
||||
Confidence string `json:"confidence"` // high | medium | low
|
||||
Hits int `json:"hits"`
|
||||
Evidence []string `json:"evidence,omitempty"` // up to 3 "path:line: excerpt" strings
|
||||
}
|
||||
|
||||
// PrivacyFinding is one matched sensitive token.
|
||||
type PrivacyFinding struct {
|
||||
Category string `json:"category"`
|
||||
Severity string `json:"severity"` // high | medium | low
|
||||
Path string `json:"path"`
|
||||
Line int `json:"line,omitempty"`
|
||||
Match string `json:"match"`
|
||||
Excerpt string `json:"excerpt,omitempty"`
|
||||
Hint string `json:"hint,omitempty"`
|
||||
}
|
||||
|
||||
// PrivacySummary is the finding count breakdown.
|
||||
type PrivacySummary struct {
|
||||
Total int `json:"total"`
|
||||
High int `json:"high"`
|
||||
Medium int `json:"medium"`
|
||||
Low int `json:"low"`
|
||||
ByCategory map[string]int `json:"by_category,omitempty"`
|
||||
}
|
||||
|
||||
// CollectionError represents a BMC-reported failure to collect a specific data section.
|
||||
@@ -263,26 +304,26 @@ type MemoryDIMM struct {
|
||||
|
||||
// Storage represents a storage device
|
||||
type Storage struct {
|
||||
Slot string `json:"slot"`
|
||||
Type string `json:"type"`
|
||||
Model string `json:"model"`
|
||||
Description string `json:"description,omitempty"`
|
||||
SizeGB int `json:"size_gb"`
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
Manufacturer string `json:"manufacturer,omitempty"`
|
||||
Firmware string `json:"firmware,omitempty"`
|
||||
Interface string `json:"interface,omitempty"`
|
||||
Present bool `json:"present"`
|
||||
Location string `json:"location,omitempty"` // Front/Rear
|
||||
BackplaneID int `json:"backplane_id,omitempty"`
|
||||
VendorID int `json:"vendor_id,omitempty"` // PCI Vendor ID (decimal), NVMe drives only
|
||||
DeviceID int `json:"device_id,omitempty"` // PCI Device ID (decimal), NVMe drives only
|
||||
RemainingEndurancePct *int `json:"remaining_endurance_pct,omitempty"` // 0-100 %; nil = not reported
|
||||
LogicalBlockSizeBytes int64 `json:"logical_block_size_bytes,omitempty"`
|
||||
PhysicalBlockSizeBytes int64 `json:"physical_block_size_bytes,omitempty"`
|
||||
MetadataBytesPerBlock int64 `json:"metadata_bytes_per_block,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Details map[string]any `json:"details,omitempty"`
|
||||
Slot string `json:"slot"`
|
||||
Type string `json:"type"`
|
||||
Model string `json:"model"`
|
||||
Description string `json:"description,omitempty"`
|
||||
SizeGB int `json:"size_gb"`
|
||||
SerialNumber string `json:"serial_number,omitempty"`
|
||||
Manufacturer string `json:"manufacturer,omitempty"`
|
||||
Firmware string `json:"firmware,omitempty"`
|
||||
Interface string `json:"interface,omitempty"`
|
||||
Present bool `json:"present"`
|
||||
Location string `json:"location,omitempty"` // Front/Rear
|
||||
BackplaneID int `json:"backplane_id,omitempty"`
|
||||
VendorID int `json:"vendor_id,omitempty"` // PCI Vendor ID (decimal), NVMe drives only
|
||||
DeviceID int `json:"device_id,omitempty"` // PCI Device ID (decimal), NVMe drives only
|
||||
RemainingEndurancePct *int `json:"remaining_endurance_pct,omitempty"` // 0-100 %; nil = not reported
|
||||
LogicalBlockSizeBytes int64 `json:"logical_block_size_bytes,omitempty"`
|
||||
PhysicalBlockSizeBytes int64 `json:"physical_block_size_bytes,omitempty"`
|
||||
MetadataBytesPerBlock int64 `json:"metadata_bytes_per_block,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Details map[string]any `json:"details,omitempty"`
|
||||
|
||||
StatusCheckedAt *time.Time `json:"status_checked_at,omitempty"`
|
||||
StatusChangedAt *time.Time `json:"status_changed_at,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user