This is an automated email from the ASF dual-hosted git repository.
shreemaan-abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new a3e5f66647 fix(ai-content-moderation): validate deny_code is a valid
HTTP status (#13692)
a3e5f66647 is described below
commit a3e5f66647c2f3f9869dbf3938621d3c52be7156
Author: Shreemaan Abhishek <[email protected]>
AuthorDate: Wed Jul 15 16:49:38 2026 +0800
fix(ai-content-moderation): validate deny_code is a valid HTTP status
(#13692)
---
apisix/plugins/ai-aliyun-content-moderation.lua | 10 +++++++-
apisix/plugins/ai-aws-content-moderation.lua | 10 +++++++-
.../latest/plugins/ai-aliyun-content-moderation.md | 2 +-
.../en/latest/plugins/ai-aws-content-moderation.md | 2 +-
.../latest/plugins/ai-aliyun-content-moderation.md | 2 +-
.../zh/latest/plugins/ai-aws-content-moderation.md | 2 +-
t/plugin/ai-aliyun-content-moderation.t | 26 +++++++++++++++++++++
t/plugin/ai-aws-content-moderation.t | 27 ++++++++++++++++++++++
8 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/apisix/plugins/ai-aliyun-content-moderation.lua
b/apisix/plugins/ai-aliyun-content-moderation.lua
index 8926be4682..6ba2676ef2 100644
--- a/apisix/plugins/ai-aliyun-content-moderation.lua
+++ b/apisix/plugins/ai-aliyun-content-moderation.lua
@@ -95,7 +95,15 @@ local schema = {
risk_level_bar = {type = "string",
enum = {"none", "low", "medium", "high", "max"},
default = "high"},
- deny_code = {type = "number", default = 200},
+ deny_code = {
+ type = "integer",
+ minimum = 200,
+ maximum = 599,
+ default = 200,
+ description = "HTTP status returned on a deny. Defaults to 200 so
the " ..
+ "provider-compatible refusal parses as a normal
completion in " ..
+ "client SDKs; set a 4xx to surface denies as HTTP
errors instead.",
+ },
deny_message = {type = "string"},
timeout = {
type = "integer",
diff --git a/apisix/plugins/ai-aws-content-moderation.lua
b/apisix/plugins/ai-aws-content-moderation.lua
index 93ed368575..d6efd830ba 100644
--- a/apisix/plugins/ai-aws-content-moderation.lua
+++ b/apisix/plugins/ai-aws-content-moderation.lua
@@ -73,7 +73,15 @@ local schema = {
default = 0.5
},
check_request = { type = "boolean", default = true },
- deny_code = { type = "number", default = 200 },
+ deny_code = {
+ type = "integer",
+ minimum = 200,
+ maximum = 599,
+ default = 200,
+ description = "HTTP status returned on a deny. Defaults to 200 so
the " ..
+ "provider-compatible refusal parses as a normal
completion in " ..
+ "client SDKs; set a 4xx to surface denies as HTTP
errors instead.",
+ },
deny_message = { type = "string" },
fail_mode = binding.schema_property("skip"),
},
diff --git a/docs/en/latest/plugins/ai-aliyun-content-moderation.md
b/docs/en/latest/plugins/ai-aliyun-content-moderation.md
index 2ca235ee07..89c0f20c62 100644
--- a/docs/en/latest/plugins/ai-aliyun-content-moderation.md
+++ b/docs/en/latest/plugins/ai-aliyun-content-moderation.md
@@ -65,7 +65,7 @@ The `ai-aliyun-content-moderation` Plugin should be used with
either [`ai-proxy`
| response_check_service | string | False | `"llm_response_moderation"` | |
Aliyun service for response moderation. |
| response_check_length_limit | number | False | `5000` | >= 1 | Response
content length limit. If exceeded, the content is sent to Aliyun in chunks. For
instance, if the response content is 250 characters and
`response_check_length_limit` is set to `100`, the content is sent in 3
requests to Aliyun. |
| risk_level_bar | string | False | `"high"` | `none`, `low`, `medium`,
`high`, `max` | If the evaluated risk level is lower than the `risk_level_bar`,
the request or response will be passed through to Upstream LLM or client
respectively. |
-| deny_code | number | False | `200` | | Rejection HTTP status code. |
+| deny_code | integer | False | `200` | [200, 599] | Rejection HTTP status
code. Defaults to `200` so the provider-compatible refusal parses as a normal
completion in client SDKs; set a 4xx to surface denies as HTTP errors instead. |
| deny_message | string | False | | | Rejection message. |
| timeout | integer | False | `10000` | >= 1 | Timeout in milliseconds. |
| keepalive | boolean | False | `true` | | If `true`, enable HTTP connection
keepalive to Aliyun. |
diff --git a/docs/en/latest/plugins/ai-aws-content-moderation.md
b/docs/en/latest/plugins/ai-aws-content-moderation.md
index 2199d90833..063911ddd6 100644
--- a/docs/en/latest/plugins/ai-aws-content-moderation.md
+++ b/docs/en/latest/plugins/ai-aws-content-moderation.md
@@ -55,7 +55,7 @@ The `ai-aws-content-moderation` Plugin should be used with
either [`ai-proxy`](.
| `moderation_categories` | object | False | | | Key-value pairs of moderation
category and their corresponding threshold. In each pair, the key should be one
of `PROFANITY`, `HATE_SPEECH`, `INSULT`, `HARASSMENT_OR_ABUSE`, `SEXUAL`, or
`VIOLENCE_OR_THREAT`; and the threshold value should be between 0 and 1
(inclusive). |
| `moderation_threshold` | number | False | 0.5 | 0 - 1 | Overall toxicity
threshold. A higher value means more toxic content allowed. This option differs
from the individual category thresholds in `moderation_categories`. For
example, if `moderation_categories` is set with a `PROFANITY` threshold of
`0.5`, and a request has a `PROFANITY` score of `0.1`, the request will not
exceed the category threshold. However, if the request has other categories
like `SEXUAL` or `VIOLENCE_OR_THREAT` [...]
| `check_request` | boolean | False | `true` | | If `true`, moderate the
request content. |
-| `deny_code` | number | False | `200` | | HTTP status code returned when a
request is rejected. |
+| `deny_code` | integer | False | `200` | [200, 599] | HTTP status code
returned when a request is rejected. Defaults to `200` so the
provider-compatible refusal parses as a normal completion in client SDKs; set a
4xx to surface denies as HTTP errors instead. |
| `deny_message` | string | False | | | Message returned when a request is
rejected. If unset, the moderation reason (for example `request body exceeds
toxicity threshold`) is returned. |
| `fail_mode` | string | False | `skip` | `skip`, `warn`, `error` | Behavior
when the request did not pass through `ai-proxy`/`ai-proxy-multi` and therefore
cannot be moderated as an AI request. `skip`: let the request pass through
unchecked; `warn`: pass through and log a warning; `error`: reject the request.
|
diff --git a/docs/zh/latest/plugins/ai-aliyun-content-moderation.md
b/docs/zh/latest/plugins/ai-aliyun-content-moderation.md
index 57d48a9a64..ae420dbc1f 100644
--- a/docs/zh/latest/plugins/ai-aliyun-content-moderation.md
+++ b/docs/zh/latest/plugins/ai-aliyun-content-moderation.md
@@ -65,7 +65,7 @@ import TabItem from '@theme/TabItem';
| response_check_service | string | 否 | `"llm_response_moderation"` | |
用于响应审核的阿里云服务。 |
| response_check_length_limit | number | 否 | `5000` | >= 1 |
响应内容长度上限。如果超过该限制,内容将分块发送到阿里云。例如,如果响应内容为 250 个字符,且 `response_check_length_limit`
设置为 `100`,则内容将分 3 次请求发送到阿里云。 |
| risk_level_bar | string | 否 | `"high"` | `none`、`low`、`medium`、`high`、`max`
| 如果评估的风险等级低于 `risk_level_bar`,请求或响应将分别被放行到上游 LLM 或客户端。 |
-| deny_code | number | 否 | `200` | | 拒绝时的 HTTP 状态码。 |
+| deny_code | integer | 否 | `200` | [200, 599] | 拒绝时的 HTTP 状态码。默认为 `200`,使兼容
provider 的拒绝响应在客户端 SDK 中被解析为正常补全;设置为 4xx 可将拒绝暴露为 HTTP 错误。 |
| deny_message | string | 否 | | | 拒绝时的消息。 |
| timeout | integer | 否 | `10000` | >= 1 | 超时时间(毫秒)。 |
| keepalive | boolean | 否 | `true` | | 如果为 `true`,启用到阿里云的 HTTP 连接保活。 |
diff --git a/docs/zh/latest/plugins/ai-aws-content-moderation.md
b/docs/zh/latest/plugins/ai-aws-content-moderation.md
index c002b3f48d..d98259984e 100644
--- a/docs/zh/latest/plugins/ai-aws-content-moderation.md
+++ b/docs/zh/latest/plugins/ai-aws-content-moderation.md
@@ -57,7 +57,7 @@ import TabItem from '@theme/TabItem';
| `moderation_categories` | object | 否 | | | 审核类别及其对应阈值的键值对。在每个键值对中,键应为
`PROFANITY`、`HATE_SPEECH`、`INSULT`、`HARASSMENT_OR_ABUSE`、`SEXUAL` 或
`VIOLENCE_OR_THREAT` 之一;阈值应在 0 到 1 之间(包含)。 |
| `moderation_threshold` | number | 否 | 0.5 | 0 - 1 |
整体毒性阈值。值越高,允许的有害内容越多。此选项与 `moderation_categories` 中的单独类别阈值不同。例如,如果
`moderation_categories` 中设置了 `PROFANITY` 阈值为 `0.5`,而请求的 `PROFANITY` 分数为
`0.1`,则请求不会超过类别阈值。但如果请求的其他类别(如 `SEXUAL` 或 `VIOLENCE_OR_THREAT`)超过了
`moderation_threshold`,则请求将被拒绝。 |
| `check_request` | boolean | 否 | `true` | | 如果为 `true`,则审核请求内容。 |
-| `deny_code` | number | 否 | `200` | | 请求被拒绝时返回的 HTTP 状态码。 |
+| `deny_code` | integer | 否 | `200` | [200, 599] | 请求被拒绝时返回的 HTTP 状态码。默认为
`200`,使兼容 provider 的拒绝响应在客户端 SDK 中被解析为正常补全;设置为 4xx 可将拒绝暴露为 HTTP 错误。 |
| `deny_message` | string | 否 | | | 请求被拒绝时返回的消息。未设置时,返回审核原因(例如 `request body
exceeds toxicity threshold`)。 |
| `fail_mode` | string | 否 | `skip` | `skip`、`warn`、`error` | 当请求未经过
`ai-proxy`/`ai-proxy-multi`,因而无法作为 AI
请求进行审核时的处理行为。`skip`:放行请求且不做检查;`warn`:放行并记录 warning 日志;`error`:拒绝请求。 |
diff --git a/t/plugin/ai-aliyun-content-moderation.t
b/t/plugin/ai-aliyun-content-moderation.t
index 603e674bc1..ddfd91183e 100644
--- a/t/plugin/ai-aliyun-content-moderation.t
+++ b/t/plugin/ai-aliyun-content-moderation.t
@@ -2281,3 +2281,29 @@ arr_out:arr-out
anth_str:anth sys
anth_blk:b1,b2
bed_blk:bs1,bs2
+
+
+
+=== TEST 73: schema check: deny_code must be within [200, 599]
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin =
require("apisix.plugins.ai-aliyun-content-moderation")
+ local conf = {
+ endpoint = "https://example.com",
+ region_id = "cn-hangzhou",
+ access_key_id = "a",
+ access_key_secret = "s"
+ }
+ for _, code in ipairs({199, 600}) do
+ conf.deny_code = code
+ ngx.say(code, ": ", plugin.check_schema(conf) and "accepted"
or "rejected")
+ end
+ conf.deny_code = 403
+ ngx.say("403: ", plugin.check_schema(conf) and "accepted" or
"rejected")
+ }
+ }
+--- response_body
+199: rejected
+600: rejected
+403: accepted
diff --git a/t/plugin/ai-aws-content-moderation.t
b/t/plugin/ai-aws-content-moderation.t
index 70baeb5017..f9561356a9 100644
--- a/t/plugin/ai-aws-content-moderation.t
+++ b/t/plugin/ai-aws-content-moderation.t
@@ -402,3 +402,30 @@ POST /echo
--- error_code: 500
--- response_body_chomp
no ai instance picked, ai-aws-content-moderation plugin must be used with
ai-proxy or ai-proxy-multi plugin
+
+
+
+=== TEST 17: schema check: deny_code must be within [200, 599]
+--- config
+ location /t {
+ content_by_lua_block {
+ local plugin = require("apisix.plugins.ai-aws-content-moderation")
+ local conf = {
+ comprehend = {
+ access_key_id = "a",
+ secret_access_key = "s",
+ region = "us-east-1"
+ }
+ }
+ for _, code in ipairs({199, 600}) do
+ conf.deny_code = code
+ ngx.say(code, ": ", plugin.check_schema(conf) and "accepted"
or "rejected")
+ end
+ conf.deny_code = 403
+ ngx.say("403: ", plugin.check_schema(conf) and "accepted" or
"rejected")
+ }
+ }
+--- response_body
+199: rejected
+600: rejected
+403: accepted