Copilot commented on code in PR #11629:
URL: https://github.com/apache/apisix/pull/11629#discussion_r2844262098


##########
t/plugin/opa3.t:
##########
@@ -0,0 +1,106 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+use t::APISIX 'no_plan';
+
+repeat_each(1);
+no_long_string();
+no_root_location();
+
+add_block_preprocessor(sub {
+    my ($block) = @_;
+
+    if (!defined $block->request) {
+        $block->set_value("request", "GET /t");
+    }
+});
+
+run_tests();
+
+__DATA__
+
+=== TEST 1: setup route with plugin
+--- config
+    location /t {
+        content_by_lua_block {
+            local t = require("lib.test_admin").test
+            local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                        "methods": ["POST"],
+                        "plugins": {
+                            "opa": {
+                                "host": "http://127.0.0.1:8181";,
+                                "policy": "with_body",
+                                "with_body": true
+                            }
+                        },
+                        "upstream": {
+                            "nodes": {
+                                "127.0.0.1:1980": 1
+                            },
+                            "type": "roundrobin"
+                        },
+                        "uris": ["/hello", "/test"]
+                }]]
+                )
+
+            if code >= 300 then
+                ngx.status = code
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: hit route (with empty request)
+--- request
+POST /hello
+--- error_code: 403
+
+
+
+=== TEST 3: hit route (with json request)
+--- request
+POST /hello
+{
+    "hello": "world"
+}
+--- response_body
+hello world
+--- error_log_matches
+"\"request\":{\"body\":{\"hello\":\"world\"}"
+
+
+

Review Comment:
   `error_log_matches` makes the test depend on log output that isn't directly 
tied to the assertion (the allow/deny behavior already proves the body is being 
used by OPA). This is likely to be flaky across log-level/config changes. 
Prefer asserting via response behavior only (e.g., rely on 200 vs 403), or use 
an `echo`-style policy to return the evaluated input and assert `request.body` 
from the response body instead of logs.
   ```suggestion
   
   ```



##########
docs/en/latest/plugins/opa.md:
##########
@@ -87,6 +89,7 @@ Each of these keys are explained below:
 - `type` indicates the request type (`http` or `stream`).
 - `request` is used when the `type` is `http` and contains the basic request 
information (URL, headers etc).
 - `var` contains the basic information about the requested connection (IP, 
port, request timestamp etc).
+- `body` contains the http-body of the request

Review Comment:
   Documentation mismatch: the example shows `"body": {}` under `request`, but 
the text below describes `body` as if it were a top-level key. Also, the 
implementation can omit `request.body` when empty, and can send a string for 
non-JSON bodies. Please clarify that this is `request.body` and document the 
possible types/absence so policy authors know what to expect.
   ```suggestion
   - `request.body` contains the HTTP request body. This field may be omitted 
when the body is empty. When present, it is:
     - a JSON value (object, array, or primitive) if the request body is parsed 
as JSON, or
     - a string for non‑JSON bodies or bodies that cannot be parsed as JSON.
   ```



##########
apisix/plugins/opa/helper.lua:
##########
@@ -45,8 +61,18 @@ local function build_http_request(conf, ctx)
         headers = core.request.headers(ctx),
         query   = core.request.get_uri_args(ctx),
     }
-end
 
+    if conf.with_body then
+        local body, err = get_body_for_request()
+        if err then
+            core.log.error(err)
+        else
+            http.body = body
+        end

Review Comment:
   When `with_body` is enabled and reading the request body fails (e.g., HTTP/2 
or HTTP/3 request without `Content-Length`, or temp-file read errors), the code 
currently just logs and continues without `request.body`. This can cause OPA 
policies that rely on the body to evaluate against incomplete input 
(potentially allowing unintended requests). Consider failing closed when 
`with_body` is true by propagating the error up so the plugin can reject the 
request (e.g., 400/403/503) instead of silently omitting the body.
   ```suggestion
               -- fail closed when with_body is enabled and the request body
               -- cannot be read, so that OPA does not evaluate against
               -- incomplete input
               core.log.error(err)
               return nil, err
           end
   
           http.body = body
   ```



##########
ci/pod/docker-compose.plugin.yml:
##########
@@ -212,11 +212,14 @@ services:
     restart: unless-stopped
     ports:
       - 8181:8181
-    command: run -s /example.rego /echo.rego /data.json /with_route.rego
+    command: run -s --log-level debug /example.rego /echo.rego /data.json 
/with_route.rego /with_body.rego

Review Comment:
   `--log-level debug` was added to the OPA container command. This can 
significantly increase CI log volume/noise and may slow down runs. Unless debug 
logging is required for tests, consider reverting to the previous default log 
level (or making it conditional) and keeping the compose file minimal.
   ```suggestion
       command: run -s /example.rego /echo.rego /data.json /with_route.rego 
/with_body.rego
   ```



##########
docs/zh/latest/plugins/opa.md:
##########
@@ -2,8 +2,6 @@
 title: opa
 keywords:
   - Apache APISIX
-  - API 网关
-  - Plugin
   - Open Policy Agent
   - opa

Review Comment:
   The `keywords` front-matter removed the common `API 网关` and `Plugin` 
entries. Most zh plugin docs keep these keywords (e.g., 
`docs/zh/latest/plugins/key-auth.md:3-7`). If this removal isn't intentional, 
please restore them to stay consistent and preserve search/SEO metadata.
   ```suggestion
     - opa
     - API 网关
     - Plugin
   ```



##########
docs/zh/latest/plugins/opa.md:
##########
@@ -82,16 +81,17 @@ description: 本篇文档介绍了 Apache APISIX 通过 opa 插件与 Open Polic
 }
 ```
 
-上述代码具体释义如下:
+以下是各个键的说明:
 
-- `type` 代表请求类型(如 `http` 或 `stream`);
-- `request` 则需要在 `type` 为 `http` 时使用,包含基本的请求信息(如 URL、头信息等);
-- `var` 包含关于请求连接的基本信息(如 IP、端口、请求时间戳等);
-- `route`、`service` 和 `consumer` 包含的数据与 APISIX 中存储的数据相同,只有当这些对象上配置了 `opa` 
插件时才会发送。
+- `type` 表示请求类型(`http` 或 `stream`).
+- `request` 在 `type` 为 `http` 时使用,包含基本请求信息(URL、头信息等).
+- `var` 包含请求连接的基本信息(IP、端口、请求时间戳等)。
+- `body` 包含请求的 HTTP 主体。

Review Comment:
   文档表述有歧义:示例里 `body` 位于 `request` 对象下,但后面的说明将 `body` 当作顶层字段。并且实现里 
`request.body` 可能在空请求体时缺失,且非 JSON 请求体会以字符串形式发送。建议明确写为 
`request.body`,并补充其可能的类型/缺失情况,方便编写策略。
   ```suggestion
   - `request` 在 `type` 为 `http` 时使用,包含基本请求信息(URL、头信息等)。
   - `var` 包含请求连接的基本信息(IP、端口、请求时间戳等)。
   - `request.body` 包含请求的 HTTP 主体:当请求体为空时该字段可能不存在;当请求体为 JSON 时,该字段为解析后的 JSON 
值;当请求体为非 JSON 时,该字段为原始字符串。
   ```



##########
apisix/plugins/opa/helper.lua:
##########
@@ -34,9 +34,25 @@ local function build_var(conf, ctx)
     }
 end
 
+local function get_body_for_request()
+    local original_body, err = core.request.get_body()
+    if err then
+        return nil, "failed to get request body: " .. err
+    end
+    if original_body == nil then
+        return nil
+    end
+    -- decode to prevent double encoded json objects
+    local body, err = core.json.decode(original_body)
+    if err then
+        -- if its not json, the body can just be added

Review Comment:
   For non-JSON bodies, `body` is forwarded as a raw Lua string. If the payload 
contains binary or invalid UTF-8, `core.json.encode` (cjson.safe) can fail to 
encode the OPA input, which will break the OPA decision call. Consider encoding 
non-JSON bodies into a JSON-safe representation (e.g., base64 + a marker/type 
field) or explicitly dropping/denying on non-UTF8 bodies when `with_body` is 
enabled.
   ```suggestion
       local body, decode_err = core.json.decode(original_body)
       if decode_err then
           -- if it's not json, ensure the body is JSON-safe (e.g., valid UTF-8)
           local _, encode_err = core.json.encode(original_body)
           if encode_err then
               return nil, "request body is not JSON-encodable: " .. encode_err
           end
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to