HY-love-sleep opened a new pull request, #7153:
URL: https://github.com/apache/shenyu/pull/7153

   ### Motivation
   
   A gateway that fronts an LLM should be able to stop a prompt that carries 
forbidden content
   before the model ever sees it, and this is a compliance requirement for a 
lot of deployments.
   ShenYu has no content-based filter today: the `waf` plugin only matches 
request conditions
   (uri / header / param) and never looks at the body, and the logging plugins 
desensitize log
   records only.
   
   This PR adds a sensitive word filter for the request body.
   
   ### What is added
   
   | | |
   | --- | --- |
   | `shenyu-plugin-ai/shenyu-plugin-ai-sensitive-word` | the plugin, the 
Aho-Corasick automaton and the data handler |
   | `shenyu-spring-boot-starter-plugin-ai-sensitive-word` | the starter |
   | `shenyu-common` | `SensitiveWordHandle` (rule level) and 
`PluginEnum.SENSITIVE_WORD` |
   | `shenyu-plugin-ai/pom.xml`, starter pom, `shenyu-bootstrap/pom.xml` | 
module registration |
   
   ### Design
   
   1. **The dictionary lives in redis.** The rule carries a `redisKey` (default
      `shenyu:sensitive:words`) and the plugin reads the set with `SMEMBERS`, 
so the dictionary is
      maintained by operations without redeploying shenyu, and every rule can 
point at its own set.
   2. **Aho-Corasick.** The dictionary is compiled into an automaton, so a body 
is scanned in a
      single pass and every matching word is reported, nested and overlapping 
ones included (both
      `中国` and `中国银行` for `中国银行`).
   3. **The automaton is cached per redis key**, not per plugin: rules with 
different dictionaries
      never share an automaton. A cached dictionary is read from redis again 
after
      `refreshIntervalSeconds` (default 300), and it is dropped immediately 
when the rule is
      configured again, so a dictionary change does not need a gateway restart.
   4. **The event loop is never blocked.** The body is read with the shared
      `ServerWebExchangeUtils#rewriteRequestBody`, and the automaton is 
compiled on a bounded elastic
      thread, never inside the request thread.
   5. **Fail open.** If redis is unreachable the request is passed through and 
the failure is logged.
      A dictionary that cannot be read must not take the traffic down.
   6. **Request body only.** Only the request body is inspected, so the plugin 
is not limited to AI
      routes; response-side (including SSE streaming) detection is a possible 
follow-up.
   
   ### Configuration
   
   Plugin level, the redis client used to read the dictionaries:
   
   ```json
   {"url": "127.0.0.1:6379", "password": "", "database": 0, "mode": 
"standalone"}
   ```
   
   Rule level:
   
   | field | type | default | description |
   | --- | --- | --- | --- |
   | `redisKey` | string | `shenyu:sensitive:words` | the redis set holding the 
dictionary of this rule |
   | `refreshIntervalSeconds` | long | `300` | how long a compiled dictionary 
is reused, `0` reads it on every request |
   
   ### Why not extend the `waf` plugin
   
   `WafHandle` only carries `permission` and `statusCode`, and `waf` runs at 
order 50, before the
   body is available: its contract is "reject on the matched conditions", not 
"inspect the content".
   A dictionary, a refresh policy and a matched-word report do not fit that 
handle, and the body has
   to be read at a later order anyway.
   
   ### Testing
   
   ```
   ./mvnw -pl shenyu-common -Dtest=SensitiveWordHandleTest test
   ./mvnw -pl 
shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-sensitive-word,<starter> -am 
test
   ./mvnw -pl shenyu-bootstrap -am -DskipTests package
   ```
   
   All green: checkstyle 0 violations, RAT ok, and 34 tests
   (`AhoCorasickTest` 14, `SensitiveWordPluginTest` 7, 
`SensitiveWordPluginDataHandlerTest` 9,
   `SensitiveWordHandleTest` 2, starter 2). The automaton tests cover nested, 
overlapping and suffix
   words, blank entries, empty dictionaries and long texts; the plugin tests 
cover the reject path
   (response body contains the matched words) and both fail-open paths.
   
   Manual check:
   
   ```
   redis-cli SADD shenyu:sensitive:words "bad word"
   curl -X POST http://localhost:9195/ai/chat -d 'a bad word here'
   ```
   
   ### Not included in this PR (happy to add according to your preference)
   
   - **the `db/init` and `db/upgrade` scripts that register the plugin** (the 
`plugin` row and the
     `resource` rows for the console). I would like to agree on the plugin 
name, its order, the module
     placement and the rule handle fields first — if the design is fine I will 
add them right away,
     in this PR or as a follow-up;
   - **the console rule form** (`shenyu-dashboard` project);
   - **the dictionary itself is deliberately not bundled.** A word list depends 
on the country, the
     business and the applicable compliance rules, so it must not be shipped 
inside a gateway:
     deployments provide their own redis set (see the module README).
   


-- 
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