mirror of
https://github.com/alibaba/higress.git
synced 2026-06-01 08:37:26 +08:00
fix(ai-security-guard): 移除Suggestion=block的兜底逻辑,改为基于阈值判断 || fix(ai-security-guard): Remove the cover-up logic of Suggestion=block and change it to based on threshold judgment (#3731)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
41
.github/workflows/sync.yml
vendored
Normal file
41
.github/workflows/sync.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: Sync Upstream
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# 每天 UTC 时间 00:00 运行 (北京时间 08:00)
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
workflow_dispatch: # 允许手动触发
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # 获取所有历史记录,防止报错
|
||||||
|
|
||||||
|
- name: Setup Git
|
||||||
|
run: |
|
||||||
|
git config user.name 'github-actions[bot]'
|
||||||
|
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||||
|
|
||||||
|
- name: Add Upstream Remote
|
||||||
|
# 将 <原始仓库URL> 替换为你要同步的原始项目地址
|
||||||
|
run: git remote add upstream https://github.com/alibaba/higress.git
|
||||||
|
|
||||||
|
- name: Fetch Upstream
|
||||||
|
run: git fetch upstream
|
||||||
|
|
||||||
|
- name: Merge Upstream Changes
|
||||||
|
# 将 upstream/main 替换为 upstream/master 或其他分支名
|
||||||
|
run: |
|
||||||
|
git checkout main
|
||||||
|
git merge upstream/main --no-edit
|
||||||
|
# 如果合并冲突,可以使用 git merge --abort 中止,或者手动处理
|
||||||
|
|
||||||
|
- name: Push to Origin
|
||||||
|
# 使用 secrets.GITHUB_TOKEN 自动推送,无需配置 PAT
|
||||||
|
run: git push origin main
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -807,11 +807,6 @@ func evaluateRiskMultiModal(data Data, config AISecurityConfig, consumer string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Data.Suggestion=block fallback
|
|
||||||
if data.Suggestion == "block" {
|
|
||||||
return RiskBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasMask {
|
if hasMask {
|
||||||
return RiskMask
|
return RiskMask
|
||||||
}
|
}
|
||||||
@@ -821,9 +816,6 @@ func evaluateRiskMultiModal(data Data, config AISecurityConfig, consumer string)
|
|||||||
// detailTriggersBlock returns whether this single detail should trigger blocking,
|
// detailTriggersBlock returns whether this single detail should trigger blocking,
|
||||||
// given the resolved dimension action and threshold evaluation result.
|
// given the resolved dimension action and threshold evaluation result.
|
||||||
func detailTriggersBlock(detail Detail, dimAction string, exceeds bool) bool {
|
func detailTriggersBlock(detail Detail, dimAction string, exceeds bool) bool {
|
||||||
if detail.Suggestion == "block" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if dimAction == "block" {
|
if dimAction == "block" {
|
||||||
return exceeds
|
return exceeds
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,14 +258,12 @@ var knownDetailTypes = []string{
|
|||||||
//
|
//
|
||||||
// Sub-property 4b: For any Detail where the resolved dimAction is "block" and the
|
// Sub-property 4b: For any Detail where the resolved dimAction is "block" and the
|
||||||
// detail's level exceeds the configured threshold, evaluateRiskMultiModal SHALL return RiskBlock.
|
// detail's level exceeds the configured threshold, evaluateRiskMultiModal SHALL return RiskBlock.
|
||||||
func TestProperty4a_SuggestionBlockAlwaysProducesRiskBlock(t *testing.T) {
|
func TestProperty4a_SuggestionBlockRespectsThreshold(t *testing.T) {
|
||||||
f := func(seed uint64) bool {
|
f := func(seed uint64) bool {
|
||||||
r := rand.New(rand.NewSource(int64(seed)))
|
r := rand.New(rand.NewSource(int64(seed)))
|
||||||
|
|
||||||
// Pick a random detail type
|
|
||||||
detailType := knownDetailTypes[r.Intn(len(knownDetailTypes))]
|
detailType := knownDetailTypes[r.Intn(len(knownDetailTypes))]
|
||||||
|
|
||||||
// Pick a random level based on type
|
|
||||||
var level string
|
var level string
|
||||||
if detailType == SensitiveDataType {
|
if detailType == SensitiveDataType {
|
||||||
level = validSensitiveLevels[r.Intn(len(validSensitiveLevels))]
|
level = validSensitiveLevels[r.Intn(len(validSensitiveLevels))]
|
||||||
@@ -273,40 +271,34 @@ func TestProperty4a_SuggestionBlockAlwaysProducesRiskBlock(t *testing.T) {
|
|||||||
level = validGeneralRiskLevels[r.Intn(len(validGeneralRiskLevels))]
|
level = validGeneralRiskLevels[r.Intn(len(validGeneralRiskLevels))]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Random config: pick random dimAction (block or mask) and random thresholds
|
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
|
// Set all thresholds to max so no detail exceeds threshold
|
||||||
// Randomly assign dimension actions
|
config.ContentModerationLevelBar = MaxRisk
|
||||||
actions := []string{"block", "mask"}
|
config.PromptAttackLevelBar = MaxRisk
|
||||||
config.ContentModerationAction = actions[r.Intn(2)]
|
config.SensitiveDataLevelBar = S4Sensitive
|
||||||
config.PromptAttackAction = actions[r.Intn(2)]
|
config.MaliciousUrlLevelBar = MaxRisk
|
||||||
config.SensitiveDataAction = actions[r.Intn(2)]
|
config.ModelHallucinationLevelBar = MaxRisk
|
||||||
config.MaliciousUrlAction = actions[r.Intn(2)]
|
config.CustomLabelLevelBar = MaxRisk
|
||||||
config.ModelHallucinationAction = actions[r.Intn(2)]
|
|
||||||
config.CustomLabelAction = actions[r.Intn(2)]
|
|
||||||
|
|
||||||
// Random thresholds
|
|
||||||
config.ContentModerationLevelBar = validGeneralRiskLevels[1+r.Intn(len(validGeneralRiskLevels)-1)]
|
|
||||||
config.PromptAttackLevelBar = validGeneralRiskLevels[1+r.Intn(len(validGeneralRiskLevels)-1)]
|
|
||||||
config.SensitiveDataLevelBar = validSensitiveLevels[r.Intn(len(validSensitiveLevels))]
|
|
||||||
config.MaliciousUrlLevelBar = validGeneralRiskLevels[1+r.Intn(len(validGeneralRiskLevels)-1)]
|
|
||||||
config.ModelHallucinationLevelBar = validGeneralRiskLevels[1+r.Intn(len(validGeneralRiskLevels)-1)]
|
|
||||||
config.CustomLabelLevelBar = validGeneralRiskLevels[1+r.Intn(len(validGeneralRiskLevels)-1)]
|
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
RiskLevel: "none", // Avoid top-level gate interference
|
RiskLevel: "none",
|
||||||
Detail: []Detail{
|
Detail: []Detail{
|
||||||
{
|
{
|
||||||
Type: detailType,
|
Type: detailType,
|
||||||
Suggestion: "block", // Always block suggestion
|
Suggestion: "block",
|
||||||
Level: level,
|
Level: level,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
if result != RiskBlock {
|
exceeds := detailExceedsThreshold(data.Detail[0], config, "")
|
||||||
t.Errorf("expected RiskBlock for Suggestion=block, type=%s, level=%s, got %d", detailType, level, result)
|
if exceeds && result != RiskBlock {
|
||||||
|
t.Errorf("expected RiskBlock when threshold exceeded for type=%s, level=%s", detailType, level)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !exceeds && result == RiskBlock {
|
||||||
|
t.Errorf("expected non-block when threshold not exceeded for type=%s, level=%s", detailType, level)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -315,7 +307,6 @@ func TestProperty4a_SuggestionBlockAlwaysProducesRiskBlock(t *testing.T) {
|
|||||||
cfg := &quick.Config{MaxCount: 200}
|
cfg := &quick.Config{MaxCount: 200}
|
||||||
if err := quick.Check(f, cfg); err != nil {
|
if err := quick.Check(f, cfg); err != nil {
|
||||||
t.Errorf("Property 4a failed: %v", err)
|
t.Errorf("Property 4a failed: %v", err)
|
||||||
fmt.Printf("Property 4a counterexample: %v\n", err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,14 +574,11 @@ func TestProperty5b_TopLevelAttackLevelGateProducesRiskBlock(t *testing.T) {
|
|||||||
//
|
//
|
||||||
// For any set of Details that do not individually trigger block, when Data.Suggestion=block,
|
// For any set of Details that do not individually trigger block, when Data.Suggestion=block,
|
||||||
// evaluateRiskMultiModal SHALL return RiskBlock.
|
// evaluateRiskMultiModal SHALL return RiskBlock.
|
||||||
func TestProperty6_DataSuggestionBlockFallbackProducesRiskBlock(t *testing.T) {
|
func TestProperty6_DataSuggestionBlockIgnoredWhenThresholdNotExceeded(t *testing.T) {
|
||||||
f := func(seed uint64) bool {
|
f := func(seed uint64) bool {
|
||||||
r := rand.New(rand.NewSource(int64(seed)))
|
r := rand.New(rand.NewSource(int64(seed)))
|
||||||
|
|
||||||
// Generate 0-4 random non-blocking details.
|
numDetails := r.Intn(5)
|
||||||
// Strategy: use Suggestion="pass" or "watch" with levels below their thresholds
|
|
||||||
// so that no detail individually triggers block.
|
|
||||||
numDetails := r.Intn(5) // 0-4 details
|
|
||||||
nonBlockSuggestions := []string{"pass", "watch"}
|
nonBlockSuggestions := []string{"pass", "watch"}
|
||||||
details := make([]Detail, numDetails)
|
details := make([]Detail, numDetails)
|
||||||
|
|
||||||
@@ -598,8 +586,6 @@ func TestProperty6_DataSuggestionBlockFallbackProducesRiskBlock(t *testing.T) {
|
|||||||
detailType := knownDetailTypes[r.Intn(len(knownDetailTypes))]
|
detailType := knownDetailTypes[r.Intn(len(knownDetailTypes))]
|
||||||
suggestion := nonBlockSuggestions[r.Intn(len(nonBlockSuggestions))]
|
suggestion := nonBlockSuggestions[r.Intn(len(nonBlockSuggestions))]
|
||||||
|
|
||||||
// Use "none" level (0) which is always below any meaningful threshold
|
|
||||||
// since all thresholds are set to max.
|
|
||||||
var level string
|
var level string
|
||||||
if detailType == SensitiveDataType {
|
if detailType == SensitiveDataType {
|
||||||
level = "S0"
|
level = "S0"
|
||||||
@@ -615,7 +601,6 @@ func TestProperty6_DataSuggestionBlockFallbackProducesRiskBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
// Set all thresholds to max so no detail exceeds threshold
|
|
||||||
config.ContentModerationLevelBar = MaxRisk
|
config.ContentModerationLevelBar = MaxRisk
|
||||||
config.PromptAttackLevelBar = MaxRisk
|
config.PromptAttackLevelBar = MaxRisk
|
||||||
config.SensitiveDataLevelBar = S4Sensitive
|
config.SensitiveDataLevelBar = S4Sensitive
|
||||||
@@ -625,16 +610,15 @@ func TestProperty6_DataSuggestionBlockFallbackProducesRiskBlock(t *testing.T) {
|
|||||||
config.RiskAction = "block"
|
config.RiskAction = "block"
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
RiskLevel: "none", // Avoid top-level RiskLevel gate
|
RiskLevel: "none",
|
||||||
AttackLevel: "", // Avoid top-level AttackLevel gate
|
AttackLevel: "",
|
||||||
Suggestion: "block", // The fallback that should trigger RiskBlock
|
Suggestion: "block",
|
||||||
Detail: details,
|
Detail: details,
|
||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
if result != RiskBlock {
|
if result != RiskPass {
|
||||||
t.Errorf("expected RiskBlock for Data.Suggestion=block with %d non-blocking details, got %d",
|
t.Errorf("expected RiskPass when no detail exceeds threshold (data.Suggestion=block should be ignored), got %d", result)
|
||||||
numDetails, result)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func TestTC_EVAL_001(t *testing.T) {
|
|||||||
require.Equal(t, RiskMask, result)
|
require.Equal(t, RiskMask, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_002 同上但 Suggestion=block => RiskBlock
|
// TestTC_EVAL_002 Suggestion=block but level below threshold => RiskPass
|
||||||
func TestTC_EVAL_002(t *testing.T) {
|
func TestTC_EVAL_002(t *testing.T) {
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
config.SensitiveDataAction = "mask"
|
config.SensitiveDataAction = "mask"
|
||||||
@@ -77,7 +77,7 @@ func TestTC_EVAL_002(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
require.Equal(t, RiskBlock, result)
|
require.Equal(t, RiskPass, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_003 promptAttackAction=block 且该维度超阈值 => RiskBlock
|
// TestTC_EVAL_003 promptAttackAction=block 且该维度超阈值 => RiskBlock
|
||||||
@@ -323,7 +323,7 @@ func TestTC_EVAL_013(t *testing.T) {
|
|||||||
require.Equal(t, RiskMask, result)
|
require.Equal(t, RiskMask, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_014 未知维度 Detail.Type=maliciousFile 且 Suggestion=block => RiskBlock
|
// TestTC_EVAL_014 未知维度 Detail.Type=maliciousFile 无阈值配置 => RiskPass
|
||||||
func TestTC_EVAL_014(t *testing.T) {
|
func TestTC_EVAL_014(t *testing.T) {
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
|
|
||||||
@@ -339,16 +339,16 @@ func TestTC_EVAL_014(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
require.Equal(t, RiskBlock, result)
|
require.Equal(t, RiskPass, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_015 Detail 不触发拦截,但 Data.Suggestion=block => RiskBlock
|
// TestTC_EVAL_015 Detail level below threshold, data.Suggestion=block ignored => RiskPass
|
||||||
func TestTC_EVAL_015(t *testing.T) {
|
func TestTC_EVAL_015(t *testing.T) {
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
RiskLevel: "none",
|
RiskLevel: "none",
|
||||||
Suggestion: "block", // 兜底
|
Suggestion: "block",
|
||||||
Detail: []Detail{
|
Detail: []Detail{
|
||||||
{
|
{
|
||||||
Suggestion: "pass",
|
Suggestion: "pass",
|
||||||
@@ -359,7 +359,7 @@ func TestTC_EVAL_015(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
require.Equal(t, RiskBlock, result)
|
require.Equal(t, RiskPass, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_016 Data.Suggestion=mask 但无 sensitiveData 脱敏明细 => 不返回 RiskMask
|
// TestTC_EVAL_016 Data.Suggestion=mask 但无 sensitiveData 脱敏明细 => 不返回 RiskMask
|
||||||
@@ -531,7 +531,7 @@ func TestTC_EVAL_018(t *testing.T) {
|
|||||||
result := EvaluateRisk(MultiModalGuardForBase64, data, config, "")
|
result := EvaluateRisk(MultiModalGuardForBase64, data, config, "")
|
||||||
require.Equal(t, RiskMask, result)
|
require.Equal(t, RiskMask, result)
|
||||||
|
|
||||||
// block 场景
|
// block scenario: level=high but threshold=max => RiskPass
|
||||||
data2 := Data{
|
data2 := Data{
|
||||||
RiskLevel: "none",
|
RiskLevel: "none",
|
||||||
Detail: []Detail{
|
Detail: []Detail{
|
||||||
@@ -543,21 +543,21 @@ func TestTC_EVAL_018(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
result2 := EvaluateRisk(MultiModalGuardForBase64, data2, config, "")
|
result2 := EvaluateRisk(MultiModalGuardForBase64, data2, config, "")
|
||||||
require.Equal(t, RiskBlock, result2)
|
require.Equal(t, RiskPass, result2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_019 空 Detail 列表 + Data.Suggestion=block => RiskBlock
|
// TestTC_EVAL_019 空 Detail 列表 + Data.Suggestion=block => RiskPass (threshold not exceeded)
|
||||||
func TestTC_EVAL_019(t *testing.T) {
|
func TestTC_EVAL_019(t *testing.T) {
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
RiskLevel: "none",
|
RiskLevel: "none",
|
||||||
Suggestion: "block",
|
Suggestion: "block",
|
||||||
Detail: []Detail{}, // 空 Detail 列表
|
Detail: []Detail{},
|
||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
require.Equal(t, RiskBlock, result)
|
require.Equal(t, RiskPass, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_020 空 Detail 列表 + 无 Data.Suggestion => RiskPass
|
// TestTC_EVAL_020 空 Detail 列表 + 无 Data.Suggestion => RiskPass
|
||||||
@@ -787,15 +787,16 @@ func TestTC_EVAL_027(t *testing.T) {
|
|||||||
require.Equal(t, RiskBlock, result2)
|
require.Equal(t, RiskBlock, result2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_EVAL_028 Data.Suggestion=block 兜底 + 有 mask 候选 => RiskBlock
|
// TestTC_EVAL_028 Data.Suggestion=block does NOT override threshold checks.
|
||||||
// block 兜底优先于 mask 候选
|
// Detail level S1 < threshold S4, so the request should pass even though
|
||||||
|
// the top-level Suggestion is "block".
|
||||||
func TestTC_EVAL_028(t *testing.T) {
|
func TestTC_EVAL_028(t *testing.T) {
|
||||||
config := baseConfig()
|
config := baseConfig()
|
||||||
config.SensitiveDataAction = "mask"
|
config.SensitiveDataAction = "mask"
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
RiskLevel: "none",
|
RiskLevel: "none",
|
||||||
Suggestion: "block", // 兜底 block
|
Suggestion: "block",
|
||||||
Detail: []Detail{
|
Detail: []Detail{
|
||||||
{
|
{
|
||||||
Suggestion: "mask",
|
Suggestion: "mask",
|
||||||
@@ -807,7 +808,7 @@ func TestTC_EVAL_028(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
result := EvaluateRisk(MultiModalGuard, data, config, "")
|
||||||
require.Equal(t, RiskBlock, result)
|
require.Equal(t, RiskPass, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestTC_DESENS_005 Detail.Result 为空数组 => 返回空字符串
|
// TestTC_DESENS_005 Detail.Result 为空数组 => 返回空字符串
|
||||||
|
|||||||
@@ -1467,7 +1467,7 @@ func TestIsRiskLevelAcceptable(t *testing.T) {
|
|||||||
{Suggestion: "block", Type: "contentModeration", Level: "high"},
|
{Suggestion: "block", Type: "contentModeration", Level: "high"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.False(t, cfg.IsRiskLevelAcceptable(cfg.MultiModalGuard, data, config, ""))
|
require.True(t, cfg.IsRiskLevelAcceptable(cfg.MultiModalGuard, data, config, ""))
|
||||||
})
|
})
|
||||||
|
|
||||||
// 用例 3: riskAction=mask, 无风险 → 应返回 true
|
// 用例 3: riskAction=mask, 无风险 → 应返回 true
|
||||||
@@ -2549,7 +2549,7 @@ func TestBuildDenyResponseBody(t *testing.T) {
|
|||||||
require.Equal(t, "high", result.BlockedDetails[0].Level)
|
require.Equal(t, "high", result.BlockedDetails[0].Level)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("blockedDetails includes explicit block suggestion below threshold", func(t *testing.T) {
|
t.Run("blockedDetails empty when suggestion=block but below threshold", func(t *testing.T) {
|
||||||
resp := cfg.Response{
|
resp := cfg.Response{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
RequestId: "req-suggestion-block",
|
RequestId: "req-suggestion-block",
|
||||||
@@ -2566,9 +2566,7 @@ func TestBuildDenyResponseBody(t *testing.T) {
|
|||||||
|
|
||||||
var result cfg.DenyResponseBody
|
var result cfg.DenyResponseBody
|
||||||
require.NoError(t, json.Unmarshal(body, &result))
|
require.NoError(t, json.Unmarshal(body, &result))
|
||||||
require.Len(t, result.BlockedDetails, 1)
|
require.Len(t, result.BlockedDetails, 0)
|
||||||
require.Equal(t, cfg.SensitiveDataType, result.BlockedDetails[0].Type)
|
|
||||||
require.Equal(t, "S3", result.BlockedDetails[0].Level)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("blockedDetails includes customLabel when threshold exceeded", func(t *testing.T) {
|
t.Run("blockedDetails includes customLabel when threshold exceeded", func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user