220 lines
7.7 KiB
Go
220 lines
7.7 KiB
Go
|
|
package model
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ==================== M-013: 凭证暴露事件详情 ====================
|
|||
|
|
|
|||
|
|
// CredentialExposureDetail M-013: 凭证暴露事件专用
|
|||
|
|
type CredentialExposureDetail struct {
|
|||
|
|
EventID string `json:"event_id"` // 事件ID(关联audit_events)
|
|||
|
|
ExposureType string `json:"exposure_type"` // exposed_in_response/exposed_in_log/exposed_in_export
|
|||
|
|
ExposureLocation string `json:"exposure_location"` // response_body/response_header/log_file/export_file
|
|||
|
|
ExposurePattern string `json:"exposure_pattern"` // 匹配到的正则模式
|
|||
|
|
ExposedFragment string `json:"exposed_fragment"` // 暴露的片段(已脱敏)
|
|||
|
|
ScanRuleID string `json:"scan_rule_id"` // 触发扫描规则ID
|
|||
|
|
Resolved bool `json:"resolved"` // 是否已解决
|
|||
|
|
ResolvedAt *time.Time `json:"resolved_at"` // 解决时间
|
|||
|
|
ResolvedBy *int64 `json:"resolved_by"` // 解决人
|
|||
|
|
ResolutionNotes string `json:"resolution_notes"` // 解决备注
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewCredentialExposureDetail 创建凭证暴露详情
|
|||
|
|
func NewCredentialExposureDetail(
|
|||
|
|
exposureType string,
|
|||
|
|
exposureLocation string,
|
|||
|
|
exposurePattern string,
|
|||
|
|
exposedFragment string,
|
|||
|
|
scanRuleID string,
|
|||
|
|
) *CredentialExposureDetail {
|
|||
|
|
return &CredentialExposureDetail{
|
|||
|
|
ExposureType: exposureType,
|
|||
|
|
ExposureLocation: exposureLocation,
|
|||
|
|
ExposurePattern: exposurePattern,
|
|||
|
|
ExposedFragment: exposedFragment,
|
|||
|
|
ScanRuleID: scanRuleID,
|
|||
|
|
Resolved: false,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Resolve 标记为已解决
|
|||
|
|
func (d *CredentialExposureDetail) Resolve(resolvedBy int64, notes string) {
|
|||
|
|
now := time.Now()
|
|||
|
|
d.Resolved = true
|
|||
|
|
d.ResolvedAt = &now
|
|||
|
|
d.ResolvedBy = &resolvedBy
|
|||
|
|
d.ResolutionNotes = notes
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== M-014: 凭证入站事件详情 ====================
|
|||
|
|
|
|||
|
|
// CredentialIngressDetail M-014: 凭证入站类型专用
|
|||
|
|
type CredentialIngressDetail struct {
|
|||
|
|
EventID string `json:"event_id"` // 事件ID
|
|||
|
|
RequestCredentialType string `json:"request_credential_type"` // 请求中的凭证类型
|
|||
|
|
ExpectedCredentialType string `json:"expected_credential_type"` // 期望的凭证类型
|
|||
|
|
CoverageCompliant bool `json:"coverage_compliant"` // 是否合规
|
|||
|
|
PlatformTokenPresent bool `json:"platform_token_present"` // 平台Token是否存在
|
|||
|
|
UpstreamKeyPresent bool `json:"upstream_key_present"` // 上游Key是否存在
|
|||
|
|
Reviewed bool `json:"reviewed"` // 是否已审核
|
|||
|
|
ReviewedAt *time.Time `json:"reviewed_at"` // 审核时间
|
|||
|
|
ReviewedBy *int64 `json:"reviewed_by"` // 审核人
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewCredentialIngressDetail 创建凭证入站详情
|
|||
|
|
func NewCredentialIngressDetail(
|
|||
|
|
requestCredentialType string,
|
|||
|
|
expectedCredentialType string,
|
|||
|
|
coverageCompliant bool,
|
|||
|
|
platformTokenPresent bool,
|
|||
|
|
upstreamKeyPresent bool,
|
|||
|
|
) *CredentialIngressDetail {
|
|||
|
|
return &CredentialIngressDetail{
|
|||
|
|
RequestCredentialType: requestCredentialType,
|
|||
|
|
ExpectedCredentialType: expectedCredentialType,
|
|||
|
|
CoverageCompliant: coverageCompliant,
|
|||
|
|
PlatformTokenPresent: platformTokenPresent,
|
|||
|
|
UpstreamKeyPresent: upstreamKeyPresent,
|
|||
|
|
Reviewed: false,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Review 标记为已审核
|
|||
|
|
func (d *CredentialIngressDetail) Review(reviewedBy int64) {
|
|||
|
|
now := time.Now()
|
|||
|
|
d.Reviewed = true
|
|||
|
|
d.ReviewedAt = &now
|
|||
|
|
d.ReviewedBy = &reviewedBy
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== M-015: 直连绕过事件详情 ====================
|
|||
|
|
|
|||
|
|
// DirectCallDetail M-015: 直连绕过专用
|
|||
|
|
type DirectCallDetail struct {
|
|||
|
|
EventID string `json:"event_id"` // 事件ID
|
|||
|
|
ConsumerID int64 `json:"consumer_id"` // 消费者ID
|
|||
|
|
SupplierID int64 `json:"supplier_id"` // 供应商ID
|
|||
|
|
DirectEndpoint string `json:"direct_endpoint"` // 直连端点
|
|||
|
|
ViaPlatform bool `json:"via_platform"` // 是否通过平台
|
|||
|
|
BypassType string `json:"bypass_type"` // ip_bypass/proxy_bypass/config_bypass/dns_bypass
|
|||
|
|
DetectionMethod string `json:"detection_method"` // 检测方法
|
|||
|
|
Blocked bool `json:"blocked"` // 是否被阻断
|
|||
|
|
BlockedAt *time.Time `json:"blocked_at"` // 阻断时间
|
|||
|
|
BlockReason string `json:"block_reason"` // 阻断原因
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewDirectCallDetail 创建直连详情
|
|||
|
|
func NewDirectCallDetail(
|
|||
|
|
consumerID int64,
|
|||
|
|
supplierID int64,
|
|||
|
|
directEndpoint string,
|
|||
|
|
viaPlatform bool,
|
|||
|
|
bypassType string,
|
|||
|
|
detectionMethod string,
|
|||
|
|
) *DirectCallDetail {
|
|||
|
|
return &DirectCallDetail{
|
|||
|
|
ConsumerID: consumerID,
|
|||
|
|
SupplierID: supplierID,
|
|||
|
|
DirectEndpoint: directEndpoint,
|
|||
|
|
ViaPlatform: viaPlatform,
|
|||
|
|
BypassType: bypassType,
|
|||
|
|
DetectionMethod: detectionMethod,
|
|||
|
|
Blocked: false,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Block 标记为已阻断
|
|||
|
|
func (d *DirectCallDetail) Block(reason string) {
|
|||
|
|
now := time.Now()
|
|||
|
|
d.Blocked = true
|
|||
|
|
d.BlockedAt = &now
|
|||
|
|
d.BlockReason = reason
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== M-016: Query Key 拒绝事件详情 ====================
|
|||
|
|
|
|||
|
|
// QueryKeyRejectDetail M-016: query key 拒绝专用
|
|||
|
|
type QueryKeyRejectDetail struct {
|
|||
|
|
EventID string `json:"event_id"` // 事件ID
|
|||
|
|
QueryKeyID string `json:"query_key_id"` // Query Key ID
|
|||
|
|
RequestedEndpoint string `json:"requested_endpoint"` // 请求端点
|
|||
|
|
RejectReason string `json:"reject_reason"` // not_allowed/expired/malformed/revoked/rate_limited
|
|||
|
|
RejectCode string `json:"reject_code"` // 拒绝码
|
|||
|
|
FirstOccurrence bool `json:"first_occurrence"` // 是否首次发生
|
|||
|
|
OccurrenceCount int `json:"occurrence_count"` // 发生次数
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewQueryKeyRejectDetail 创建Query Key拒绝详情
|
|||
|
|
func NewQueryKeyRejectDetail(
|
|||
|
|
queryKeyID string,
|
|||
|
|
requestedEndpoint string,
|
|||
|
|
rejectReason string,
|
|||
|
|
rejectCode string,
|
|||
|
|
) *QueryKeyRejectDetail {
|
|||
|
|
return &QueryKeyRejectDetail{
|
|||
|
|
QueryKeyID: queryKeyID,
|
|||
|
|
RequestedEndpoint: requestedEndpoint,
|
|||
|
|
RejectReason: rejectReason,
|
|||
|
|
RejectCode: rejectCode,
|
|||
|
|
FirstOccurrence: true,
|
|||
|
|
OccurrenceCount: 1,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// RecordOccurrence 记录再次发生
|
|||
|
|
func (d *QueryKeyRejectDetail) RecordOccurrence(firstOccurrence bool) {
|
|||
|
|
d.FirstOccurrence = firstOccurrence
|
|||
|
|
d.OccurrenceCount++
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 指标常量 ====================
|
|||
|
|
|
|||
|
|
// M-013 暴露类型常量
|
|||
|
|
const (
|
|||
|
|
ExposureTypeResponse = "exposed_in_response"
|
|||
|
|
ExposureTypeLog = "exposed_in_log"
|
|||
|
|
ExposureTypeExport = "exposed_in_export"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// M-013 暴露位置常量
|
|||
|
|
const (
|
|||
|
|
ExposureLocationResponseBody = "response_body"
|
|||
|
|
ExposureLocationResponseHeader = "response_header"
|
|||
|
|
ExposureLocationLogFile = "log_file"
|
|||
|
|
ExposureLocationExportFile = "export_file"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// M-015 绕过类型常量
|
|||
|
|
const (
|
|||
|
|
BypassTypeIPBypass = "ip_bypass"
|
|||
|
|
BypassTypeProxyBypass = "proxy_bypass"
|
|||
|
|
BypassTypeConfigBypass = "config_bypass"
|
|||
|
|
BypassTypeDNSBypass = "dns_bypass"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// M-015 检测方法常量
|
|||
|
|
const (
|
|||
|
|
DetectionMethodUpstreamAPIPattern = "upstream_api_pattern_match"
|
|||
|
|
DetectionMethodDNSResolution = "dns_resolution_check"
|
|||
|
|
DetectionMethodConnectionSource = "connection_source_check"
|
|||
|
|
DetectionMethodIPWhitelist = "ip_whitelist_check"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// M-016 拒绝原因常量
|
|||
|
|
const (
|
|||
|
|
RejectReasonNotAllowed = "not_allowed"
|
|||
|
|
RejectReasonExpired = "expired"
|
|||
|
|
RejectReasonMalformed = "malformed"
|
|||
|
|
RejectReasonRevoked = "revoked"
|
|||
|
|
RejectReasonRateLimited = "rate_limited"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// M-016 拒绝码常量
|
|||
|
|
const (
|
|||
|
|
RejectCodeNotAllowed = "QUERY_KEY_NOT_ALLOWED"
|
|||
|
|
RejectCodeExpired = "QUERY_KEY_EXPIRED"
|
|||
|
|
RejectCodeMalformed = "QUERY_KEY_MALFORMED"
|
|||
|
|
RejectCodeRevoked = "QUERY_KEY_REVOKED"
|
|||
|
|
RejectCodeRateLimited = "QUERY_KEY_RATE_LIMITED"
|
|||
|
|
)
|