feat(P1/P2): 完成TDD开发及P1/P2设计文档
## 设计文档 - multi_role_permission_design: 多角色权限设计 (CONDITIONAL GO) - audit_log_enhancement_design: 审计日志增强 (CONDITIONAL GO) - routing_strategy_template_design: 路由策略模板 (CONDITIONAL GO) - sso_saml_technical_research: SSO/SAML调研 (CONDITIONAL GO) - compliance_capability_package_design: 合规能力包设计 (CONDITIONAL GO) ## TDD开发成果 - IAM模块: supply-api/internal/iam/ (111个测试) - 审计日志模块: supply-api/internal/audit/ (40+测试) - 路由策略模块: gateway/internal/router/ (33+测试) - 合规能力包: gateway/internal/compliance/ + scripts/ci/compliance/ ## 规范文档 - parallel_agent_output_quality_standards: 并行Agent产出质量规范 - project_experience_summary: 项目经验总结 (v2) - 2026-04-02-p1-p2-tdd-execution-plan: TDD执行计划 ## 评审报告 - 5个CONDITIONAL GO设计文档评审报告 - fix_verification_report: 修复验证报告 - full_verification_report: 全面质量验证报告 - tdd_module_quality_verification: TDD模块质量验证 - tdd_execution_summary: TDD执行总结 依据: Superpowers执行框架 + TDD规范
This commit is contained in:
186
supply-api/internal/audit/events/cred_events.go
Normal file
186
supply-api/internal/audit/events/cred_events.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CRED事件类别常量
|
||||
const (
|
||||
CategoryCRED = "CRED"
|
||||
SubCategoryEXPOSE = "EXPOSE"
|
||||
SubCategoryINGRESS = "INGRESS"
|
||||
SubCategoryROTATE = "ROTATE"
|
||||
SubCategoryREVOKE = "REVOKE"
|
||||
SubCategoryVALIDATE = "VALIDATE"
|
||||
SubCategoryDIRECT = "DIRECT"
|
||||
)
|
||||
|
||||
// CRED事件列表
|
||||
var credEvents = []string{
|
||||
// 凭证暴露事件 (CRED-EXPOSE)
|
||||
"CRED-EXPOSE-RESPONSE", // 响应中暴露凭证
|
||||
"CRED-EXPOSE-LOG", // 日志中暴露凭证
|
||||
"CRED-EXPOSE-EXPORT", // 导出文件中暴露凭证
|
||||
|
||||
// 凭证入站事件 (CRED-INGRESS)
|
||||
"CRED-INGRESS-PLATFORM", // 平台凭证入站
|
||||
"CRED-INGRESS-SUPPLIER", // 供应商凭证入站
|
||||
|
||||
// 凭证轮换事件 (CRED-ROTATE)
|
||||
"CRED-ROTATE",
|
||||
|
||||
// 凭证吊销事件 (CRED-REVOKE)
|
||||
"CRED-REVOKE",
|
||||
|
||||
// 凭证验证事件 (CRED-VALIDATE)
|
||||
"CRED-VALIDATE",
|
||||
|
||||
// 直连绕过事件 (CRED-DIRECT)
|
||||
"CRED-DIRECT-SUPPLIER", // 直连供应商
|
||||
"CRED-DIRECT-BYPASS", // 绕过直连
|
||||
}
|
||||
|
||||
// CRED事件结果码映射
|
||||
var credResultCodes = map[string]string{
|
||||
"CRED-EXPOSE-RESPONSE": "SEC_CRED_EXPOSED",
|
||||
"CRED-EXPOSE-LOG": "SEC_CRED_EXPOSED",
|
||||
"CRED-EXPOSE-EXPORT": "SEC_CRED_EXPOSED",
|
||||
"CRED-INGRESS-PLATFORM": "CRED_INGRESS_OK",
|
||||
"CRED-INGRESS-SUPPLIER": "CRED_INGRESS_OK",
|
||||
"CRED-DIRECT-SUPPLIER": "SEC_DIRECT_BYPASS",
|
||||
"CRED-DIRECT-BYPASS": "SEC_DIRECT_BYPASS",
|
||||
"CRED-ROTATE": "CRED_ROTATE_OK",
|
||||
"CRED-REVOKE": "CRED_REVOKE_OK",
|
||||
"CRED-VALIDATE": "CRED_VALIDATE_OK",
|
||||
}
|
||||
|
||||
// CRED指标名称映射
|
||||
var credMetricNames = map[string]string{
|
||||
"CRED-EXPOSE-RESPONSE": "supplier_credential_exposure_events",
|
||||
"CRED-EXPOSE-LOG": "supplier_credential_exposure_events",
|
||||
"CRED-EXPOSE-EXPORT": "supplier_credential_exposure_events",
|
||||
"CRED-INGRESS-PLATFORM": "platform_credential_ingress_coverage_pct",
|
||||
"CRED-INGRESS-SUPPLIER": "platform_credential_ingress_coverage_pct",
|
||||
"CRED-DIRECT-SUPPLIER": "direct_supplier_call_by_consumer_events",
|
||||
"CRED-DIRECT-BYPASS": "direct_supplier_call_by_consumer_events",
|
||||
}
|
||||
|
||||
// GetCREDEvents 返回所有CRED事件
|
||||
func GetCREDEvents() []string {
|
||||
return credEvents
|
||||
}
|
||||
|
||||
// GetCREDExposeEvents 返回所有凭证暴露事件
|
||||
func GetCREDExposeEvents() []string {
|
||||
return []string{
|
||||
"CRED-EXPOSE-RESPONSE",
|
||||
"CRED-EXPOSE-LOG",
|
||||
"CRED-EXPOSE-EXPORT",
|
||||
}
|
||||
}
|
||||
|
||||
// GetCREDFngressEvents 返回所有凭证入站事件
|
||||
func GetCREDFngressEvents() []string {
|
||||
return []string{
|
||||
"CRED-INGRESS-PLATFORM",
|
||||
"CRED-INGRESS-SUPPLIER",
|
||||
}
|
||||
}
|
||||
|
||||
// GetCREDDnirectEvents 返回所有直连绕过事件
|
||||
func GetCREDDnirectEvents() []string {
|
||||
return []string{
|
||||
"CRED-DIRECT-SUPPLIER",
|
||||
"CRED-DIRECT-BYPASS",
|
||||
}
|
||||
}
|
||||
|
||||
// GetCREDEventCategory 返回CRED事件的类别
|
||||
func GetCREDEventCategory(eventName string) string {
|
||||
if strings.HasPrefix(eventName, "CRED-") {
|
||||
return CategoryCRED
|
||||
}
|
||||
if eventName == "CRED-ROTATE" || eventName == "CRED-REVOKE" || eventName == "CRED-VALIDATE" {
|
||||
return CategoryCRED
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetCREDEventSubCategory 返回CRED事件的子类别
|
||||
func GetCREDEventSubCategory(eventName string) string {
|
||||
if strings.HasPrefix(eventName, "CRED-EXPOSE") {
|
||||
return SubCategoryEXPOSE
|
||||
}
|
||||
if strings.HasPrefix(eventName, "CRED-INGRESS") {
|
||||
return SubCategoryINGRESS
|
||||
}
|
||||
if strings.HasPrefix(eventName, "CRED-DIRECT") {
|
||||
return SubCategoryDIRECT
|
||||
}
|
||||
if strings.HasPrefix(eventName, "CRED-ROTATE") {
|
||||
return SubCategoryROTATE
|
||||
}
|
||||
if strings.HasPrefix(eventName, "CRED-REVOKE") {
|
||||
return SubCategoryREVOKE
|
||||
}
|
||||
if strings.HasPrefix(eventName, "CRED-VALIDATE") {
|
||||
return SubCategoryVALIDATE
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsValidCREDEvent 检查事件名称是否为有效的CRED事件
|
||||
func IsValidCREDEvent(eventName string) bool {
|
||||
for _, e := range credEvents {
|
||||
if e == eventName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCREDExposeEvent 检查是否为凭证暴露事件(M-013相关)
|
||||
func IsCREDExposeEvent(eventName string) bool {
|
||||
return strings.HasPrefix(eventName, "CRED-EXPOSE")
|
||||
}
|
||||
|
||||
// IsCREDFngressEvent 检查是否为凭证入站事件(M-014相关)
|
||||
func IsCREDFngressEvent(eventName string) bool {
|
||||
return strings.HasPrefix(eventName, "CRED-INGRESS")
|
||||
}
|
||||
|
||||
// IsCREDDnirectEvent 检查是否为直连绕过事件(M-015相关)
|
||||
func IsCREDDnirectEvent(eventName string) bool {
|
||||
return strings.HasPrefix(eventName, "CRED-DIRECT")
|
||||
}
|
||||
|
||||
// GetCREDMetricName 获取CRED事件对应的指标名称
|
||||
func GetCREDMetricName(eventName string) string {
|
||||
if metric, ok := credMetricNames[eventName]; ok {
|
||||
return metric
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetCREDEventResultCode 获取CRED事件对应的结果码
|
||||
func GetCREDEventResultCode(eventName string) string {
|
||||
if code, ok := credResultCodes[eventName]; ok {
|
||||
return code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsCREDExposeEvent 检查是否为M-013事件(凭证暴露)
|
||||
func IsM013RelatedEvent(eventName string) bool {
|
||||
return IsCREDExposeEvent(eventName)
|
||||
}
|
||||
|
||||
// IsCREDFngressEvent 检查是否为M-014事件(凭证入站)
|
||||
func IsM014RelatedEvent(eventName string) bool {
|
||||
return IsCREDFngressEvent(eventName)
|
||||
}
|
||||
|
||||
// IsCREDDnirectEvent 检查是否为M-015事件(直连绕过)
|
||||
func IsM015RelatedEvent(eventName string) bool {
|
||||
return IsCREDDnirectEvent(eventName)
|
||||
}
|
||||
145
supply-api/internal/audit/events/cred_events_test.go
Normal file
145
supply-api/internal/audit/events/cred_events_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCREDEvents_Categories(t *testing.T) {
|
||||
// 测试 CRED 事件类别
|
||||
events := GetCREDEvents()
|
||||
|
||||
// CRED-EXPOSE-RESPONSE: 响应中暴露凭证
|
||||
assert.Contains(t, events, "CRED-EXPOSE-RESPONSE", "Should contain CRED-EXPOSE-RESPONSE")
|
||||
|
||||
// CRED-INGRESS-PLATFORM: 平台凭证入站
|
||||
assert.Contains(t, events, "CRED-INGRESS-PLATFORM", "Should contain CRED-INGRESS-PLATFORM")
|
||||
|
||||
// CRED-DIRECT-SUPPLIER: 直连供应商
|
||||
assert.Contains(t, events, "CRED-DIRECT-SUPPLIER", "Should contain CRED-DIRECT-SUPPLIER")
|
||||
}
|
||||
|
||||
func TestCREDEvents_ExposeEvents(t *testing.T) {
|
||||
// 测试 CRED-EXPOSE 事件
|
||||
events := GetCREDExposeEvents()
|
||||
|
||||
assert.Contains(t, events, "CRED-EXPOSE-RESPONSE")
|
||||
assert.Contains(t, events, "CRED-EXPOSE-LOG")
|
||||
assert.Contains(t, events, "CRED-EXPOSE-EXPORT")
|
||||
}
|
||||
|
||||
func TestCREDEvents_IngressEvents(t *testing.T) {
|
||||
// 测试 CRED-INGRESS 事件
|
||||
events := GetCREDFngressEvents()
|
||||
|
||||
assert.Contains(t, events, "CRED-INGRESS-PLATFORM")
|
||||
assert.Contains(t, events, "CRED-INGRESS-SUPPLIER")
|
||||
}
|
||||
|
||||
func TestCREDEvents_DirectEvents(t *testing.T) {
|
||||
// 测试 CRED-DIRECT 事件
|
||||
events := GetCREDDnirectEvents()
|
||||
|
||||
assert.Contains(t, events, "CRED-DIRECT-SUPPLIER")
|
||||
assert.Contains(t, events, "CRED-DIRECT-BYPASS")
|
||||
}
|
||||
|
||||
func TestCREDEvents_GetEventCategory(t *testing.T) {
|
||||
// 所有CRED事件的类别应该是CRED
|
||||
events := GetCREDEvents()
|
||||
for _, eventName := range events {
|
||||
category := GetCREDEventCategory(eventName)
|
||||
assert.Equal(t, "CRED", category, "Event %s should have category CRED", eventName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCREDEvents_GetEventSubCategory(t *testing.T) {
|
||||
// 测试CRED事件的子类别
|
||||
testCases := []struct {
|
||||
eventName string
|
||||
expectedSubCategory string
|
||||
}{
|
||||
{"CRED-EXPOSE-RESPONSE", "EXPOSE"},
|
||||
{"CRED-INGRESS-PLATFORM", "INGRESS"},
|
||||
{"CRED-DIRECT-SUPPLIER", "DIRECT"},
|
||||
{"CRED-ROTATE", "ROTATE"},
|
||||
{"CRED-REVOKE", "REVOKE"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.eventName, func(t *testing.T) {
|
||||
subCategory := GetCREDEventSubCategory(tc.eventName)
|
||||
assert.Equal(t, tc.expectedSubCategory, subCategory)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCREDEvents_IsValidEvent(t *testing.T) {
|
||||
// 测试有效事件验证
|
||||
assert.True(t, IsValidCREDEvent("CRED-EXPOSE-RESPONSE"))
|
||||
assert.True(t, IsValidCREDEvent("CRED-INGRESS-PLATFORM"))
|
||||
assert.True(t, IsValidCREDEvent("CRED-DIRECT-SUPPLIER"))
|
||||
assert.False(t, IsValidCREDEvent("INVALID-EVENT"))
|
||||
assert.False(t, IsValidCREDEvent("AUTH-TOKEN-OK"))
|
||||
}
|
||||
|
||||
func TestCREDEvents_IsM013Event(t *testing.T) {
|
||||
// 测试M-013相关事件
|
||||
assert.True(t, IsCREDExposeEvent("CRED-EXPOSE-RESPONSE"))
|
||||
assert.True(t, IsCREDExposeEvent("CRED-EXPOSE-LOG"))
|
||||
assert.False(t, IsCREDExposeEvent("CRED-INGRESS-PLATFORM"))
|
||||
}
|
||||
|
||||
func TestCREDEvents_IsM014Event(t *testing.T) {
|
||||
// 测试M-014相关事件
|
||||
assert.True(t, IsCREDFngressEvent("CRED-INGRESS-PLATFORM"))
|
||||
assert.True(t, IsCREDFngressEvent("CRED-INGRESS-SUPPLIER"))
|
||||
assert.False(t, IsCREDFngressEvent("CRED-EXPOSE-RESPONSE"))
|
||||
}
|
||||
|
||||
func TestCREDEvents_IsM015Event(t *testing.T) {
|
||||
// 测试M-015相关事件
|
||||
assert.True(t, IsCREDDnirectEvent("CRED-DIRECT-SUPPLIER"))
|
||||
assert.True(t, IsCREDDnirectEvent("CRED-DIRECT-BYPASS"))
|
||||
assert.False(t, IsCREDDnirectEvent("CRED-INGRESS-PLATFORM"))
|
||||
}
|
||||
|
||||
func TestCREDEvents_GetMetricName(t *testing.T) {
|
||||
// 测试指标名称映射
|
||||
testCases := []struct {
|
||||
eventName string
|
||||
expectedMetric string
|
||||
}{
|
||||
{"CRED-EXPOSE-RESPONSE", "supplier_credential_exposure_events"},
|
||||
{"CRED-EXPOSE-LOG", "supplier_credential_exposure_events"},
|
||||
{"CRED-INGRESS-PLATFORM", "platform_credential_ingress_coverage_pct"},
|
||||
{"CRED-DIRECT-SUPPLIER", "direct_supplier_call_by_consumer_events"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.eventName, func(t *testing.T) {
|
||||
metric := GetCREDMetricName(tc.eventName)
|
||||
assert.Equal(t, tc.expectedMetric, metric)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCREDEvents_GetResultCode(t *testing.T) {
|
||||
// 测试CRED事件结果码
|
||||
testCases := []struct {
|
||||
eventName string
|
||||
expectedCode string
|
||||
}{
|
||||
{"CRED-EXPOSE-RESPONSE", "SEC_CRED_EXPOSED"},
|
||||
{"CRED-INGRESS-PLATFORM", "CRED_INGRESS_OK"},
|
||||
{"CRED-DIRECT-SUPPLIER", "SEC_DIRECT_BYPASS"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.eventName, func(t *testing.T) {
|
||||
code := GetCREDEventResultCode(tc.eventName)
|
||||
assert.Equal(t, tc.expectedCode, code)
|
||||
})
|
||||
}
|
||||
}
|
||||
195
supply-api/internal/audit/events/security_events.go
Normal file
195
supply-api/internal/audit/events/security_events.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SECURITY事件类别常量
|
||||
const (
|
||||
CategorySECURITY = "SECURITY"
|
||||
SubCategoryVIOLATION = "VIOLATION"
|
||||
SubCategoryALERT = "ALERT"
|
||||
SubCategoryBREACH = "BREACH"
|
||||
)
|
||||
|
||||
// SECURITY事件列表
|
||||
var securityEvents = []string{
|
||||
// 不变量违反事件 (INVARIANT-VIOLATION)
|
||||
"INV-PKG-001", // 供应方资质过期
|
||||
"INV-PKG-002", // 供应方余额为负
|
||||
"INV-PKG-003", // 售价不得低于保护价
|
||||
"INV-SET-001", // processing/completed 不可撤销
|
||||
"INV-SET-002", // 提现金额不得超过可提现余额
|
||||
"INV-SET-003", // 结算单金额与余额流水必须平衡
|
||||
|
||||
// 安全突破事件 (SECURITY-BREACH)
|
||||
"SEC-BREACH-001", // 凭证泄露突破
|
||||
"SEC-BREACH-002", // 权限绕过突破
|
||||
|
||||
// 安全告警事件 (SECURITY-ALERT)
|
||||
"SEC-ALERT-001", // 可疑访问告警
|
||||
"SEC-ALERT-002", // 异常行为告警
|
||||
}
|
||||
|
||||
// 不变量违反事件到结果码的映射
|
||||
var invariantResultCodes = map[string]string{
|
||||
"INV-PKG-001": "SEC_INV_PKG_001",
|
||||
"INV-PKG-002": "SEC_INV_PKG_002",
|
||||
"INV-PKG-003": "SEC_INV_PKG_003",
|
||||
"INV-SET-001": "SEC_INV_SET_001",
|
||||
"INV-SET-002": "SEC_INV_SET_002",
|
||||
"INV-SET-003": "SEC_INV_SET_003",
|
||||
}
|
||||
|
||||
// 事件描述映射
|
||||
var securityEventDescriptions = map[string]string{
|
||||
"INV-PKG-001": "供应方资质过期,资质验证失败",
|
||||
"INV-PKG-002": "供应方余额为负,余额检查失败",
|
||||
"INV-PKG-003": "售价不得低于保护价,价格校验失败",
|
||||
"INV-SET-001": "结算单状态为processing/completed,不可撤销",
|
||||
"INV-SET-002": "提现金额不得超过可提现余额",
|
||||
"INV-SET-003": "结算单金额与余额流水不平衡",
|
||||
"SEC-BREACH-001": "检测到凭证泄露安全突破",
|
||||
"SEC-BREACH-002": "检测到权限绕过安全突破",
|
||||
"SEC-ALERT-001": "检测到可疑访问行为",
|
||||
"SEC-ALERT-002": "检测到异常行为",
|
||||
}
|
||||
|
||||
// GetSECURITYEvents 返回所有SECURITY事件
|
||||
func GetSECURITYEvents() []string {
|
||||
return securityEvents
|
||||
}
|
||||
|
||||
// GetInvariantViolationEvents 返回所有不变量违反事件
|
||||
func GetInvariantViolationEvents() []string {
|
||||
return []string{
|
||||
"INV-PKG-001",
|
||||
"INV-PKG-002",
|
||||
"INV-PKG-003",
|
||||
"INV-SET-001",
|
||||
"INV-SET-002",
|
||||
"INV-SET-003",
|
||||
}
|
||||
}
|
||||
|
||||
// GetSecurityAlertEvents 返回所有安全告警事件
|
||||
func GetSecurityAlertEvents() []string {
|
||||
return []string{
|
||||
"SEC-ALERT-001",
|
||||
"SEC-ALERT-002",
|
||||
}
|
||||
}
|
||||
|
||||
// GetSecurityBreachEvents 返回所有安全突破事件
|
||||
func GetSecurityBreachEvents() []string {
|
||||
return []string{
|
||||
"SEC-BREACH-001",
|
||||
"SEC-BREACH-002",
|
||||
}
|
||||
}
|
||||
|
||||
// GetEventCategory 返回事件的类别
|
||||
func GetEventCategory(eventName string) string {
|
||||
if isInvariantViolation(eventName) || isSecurityBreach(eventName) || isSecurityAlert(eventName) {
|
||||
return CategorySECURITY
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetEventSubCategory 返回事件的子类别
|
||||
func GetEventSubCategory(eventName string) string {
|
||||
if isInvariantViolation(eventName) {
|
||||
return SubCategoryVIOLATION
|
||||
}
|
||||
if isSecurityBreach(eventName) {
|
||||
return SubCategoryBREACH
|
||||
}
|
||||
if isSecurityAlert(eventName) {
|
||||
return SubCategoryALERT
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetResultCode 返回事件对应的结果码
|
||||
func GetResultCode(eventName string) string {
|
||||
if code, ok := invariantResultCodes[eventName]; ok {
|
||||
return code
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetEventDescription 返回事件的描述
|
||||
func GetEventDescription(eventName string) string {
|
||||
if desc, ok := securityEventDescriptions[eventName]; ok {
|
||||
return desc
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsValidEvent 检查事件名称是否有效
|
||||
func IsValidEvent(eventName string) bool {
|
||||
for _, e := range securityEvents {
|
||||
if e == eventName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isInvariantViolation 检查是否为不变量违反事件
|
||||
func isInvariantViolation(eventName string) bool {
|
||||
for _, e := range getInvariantViolationEvents() {
|
||||
if e == eventName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// getInvariantViolationEvents 返回不变量违反事件列表(内部使用)
|
||||
func getInvariantViolationEvents() []string {
|
||||
return []string{
|
||||
"INV-PKG-001",
|
||||
"INV-PKG-002",
|
||||
"INV-PKG-003",
|
||||
"INV-SET-001",
|
||||
"INV-SET-002",
|
||||
"INV-SET-003",
|
||||
}
|
||||
}
|
||||
|
||||
// isSecurityBreach 检查是否为安全突破事件
|
||||
func isSecurityBreach(eventName string) bool {
|
||||
prefixes := []string{"SEC-BREACH"}
|
||||
for _, prefix := range prefixes {
|
||||
if len(eventName) >= len(prefix) && eventName[:len(prefix)] == prefix {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isSecurityAlert 检查是否为安全告警事件
|
||||
func isSecurityAlert(eventName string) bool {
|
||||
prefixes := []string{"SEC-ALERT"}
|
||||
for _, prefix := range prefixes {
|
||||
if len(eventName) >= len(prefix) && eventName[:len(prefix)] == prefix {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FormatSECURITYEvent 格式化SECURITY事件
|
||||
func FormatSECURITYEvent(eventName string, params map[string]string) string {
|
||||
desc := GetEventDescription(eventName)
|
||||
if desc == "" {
|
||||
return fmt.Sprintf("SECURITY event: %s", eventName)
|
||||
}
|
||||
|
||||
// 如果有额外参数,追加到描述中
|
||||
if len(params) > 0 {
|
||||
return fmt.Sprintf("%s - %v", desc, params)
|
||||
}
|
||||
return desc
|
||||
}
|
||||
131
supply-api/internal/audit/events/security_events_test.go
Normal file
131
supply-api/internal/audit/events/security_events_test.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSECURITYEvents_InvariantViolation(t *testing.T) {
|
||||
// 测试 invariant_violation 事件
|
||||
events := GetSECURITYEvents()
|
||||
|
||||
// INV-PKG-001: 供应方资质过期
|
||||
assert.Contains(t, events, "INV-PKG-001", "Should contain INV-PKG-001")
|
||||
|
||||
// INV-SET-001: processing/completed 不可撤销
|
||||
assert.Contains(t, events, "INV-SET-001", "Should contain INV-SET-001")
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_AllEvents(t *testing.T) {
|
||||
// 测试所有SECURITY事件
|
||||
events := GetSECURITYEvents()
|
||||
|
||||
// 验证不变量违反事件
|
||||
invariantEvents := GetInvariantViolationEvents()
|
||||
for _, event := range invariantEvents {
|
||||
assert.Contains(t, events, event, "SECURITY events should contain %s", event)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetInvariantViolationEvents(t *testing.T) {
|
||||
events := GetInvariantViolationEvents()
|
||||
|
||||
// INV-PKG-001: 供应方资质过期
|
||||
assert.Contains(t, events, "INV-PKG-001")
|
||||
|
||||
// INV-PKG-002: 供应方余额为负
|
||||
assert.Contains(t, events, "INV-PKG-002")
|
||||
|
||||
// INV-PKG-003: 售价不得低于保护价
|
||||
assert.Contains(t, events, "INV-PKG-003")
|
||||
|
||||
// INV-SET-001: processing/completed 不可撤销
|
||||
assert.Contains(t, events, "INV-SET-001")
|
||||
|
||||
// INV-SET-002: 提现金额不得超过可提现余额
|
||||
assert.Contains(t, events, "INV-SET-002")
|
||||
|
||||
// INV-SET-003: 结算单金额与余额流水必须平衡
|
||||
assert.Contains(t, events, "INV-SET-003")
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetSecurityAlertEvents(t *testing.T) {
|
||||
events := GetSecurityAlertEvents()
|
||||
|
||||
// 安全告警事件应该存在
|
||||
assert.NotEmpty(t, events)
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetSecurityBreachEvents(t *testing.T) {
|
||||
events := GetSecurityBreachEvents()
|
||||
|
||||
// 安全突破事件应该存在
|
||||
assert.NotEmpty(t, events)
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetEventCategory(t *testing.T) {
|
||||
// 所有SECURITY事件的类别应该是SECURITY
|
||||
events := GetSECURITYEvents()
|
||||
for _, eventName := range events {
|
||||
category := GetEventCategory(eventName)
|
||||
assert.Equal(t, "SECURITY", category, "Event %s should have category SECURITY", eventName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetResultCode(t *testing.T) {
|
||||
// 测试不变量违反事件的结果码映射
|
||||
testCases := []struct {
|
||||
eventName string
|
||||
expectedCode string
|
||||
}{
|
||||
{"INV-PKG-001", "SEC_INV_PKG_001"},
|
||||
{"INV-PKG-002", "SEC_INV_PKG_002"},
|
||||
{"INV-PKG-003", "SEC_INV_PKG_003"},
|
||||
{"INV-SET-001", "SEC_INV_SET_001"},
|
||||
{"INV-SET-002", "SEC_INV_SET_002"},
|
||||
{"INV-SET-003", "SEC_INV_SET_003"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.eventName, func(t *testing.T) {
|
||||
code := GetResultCode(tc.eventName)
|
||||
assert.Equal(t, tc.expectedCode, code, "Result code mismatch for %s", tc.eventName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetEventDescription(t *testing.T) {
|
||||
// 测试事件描述
|
||||
desc := GetEventDescription("INV-PKG-001")
|
||||
assert.NotEmpty(t, desc)
|
||||
assert.Contains(t, desc, "供应方资质", "Description should contain 供应方资质")
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_IsValidEvent(t *testing.T) {
|
||||
// 测试有效事件验证
|
||||
assert.True(t, IsValidEvent("INV-PKG-001"))
|
||||
assert.True(t, IsValidEvent("INV-SET-001"))
|
||||
assert.False(t, IsValidEvent("INVALID-EVENT"))
|
||||
assert.False(t, IsValidEvent(""))
|
||||
}
|
||||
|
||||
func TestSECURITYEvents_GetEventSubCategory(t *testing.T) {
|
||||
// SECURITY事件的子类别应该是VIOLATION/ALERT/BREACH
|
||||
testCases := []struct {
|
||||
eventName string
|
||||
expectedSubCategory string
|
||||
}{
|
||||
{"INV-PKG-001", "VIOLATION"},
|
||||
{"INV-SET-001", "VIOLATION"},
|
||||
{"SEC-BREACH-001", "BREACH"},
|
||||
{"SEC-ALERT-001", "ALERT"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.eventName, func(t *testing.T) {
|
||||
subCategory := GetEventSubCategory(tc.eventName)
|
||||
assert.Equal(t, tc.expectedSubCategory, subCategory)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user