apache / apache/shenyu

[BUG] AI token limiter rule fields can be null and crash request processing

Open
#6,513 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
8.8k
Forks
3.1k
Avg merge
7d 1h
Merged PRs (30d)
85

Description

### Search before asking

- [x] I had searched in the [issues](https://github.com/apache/shenyu/issues) and found no similar issues.

### Apache ShenYu Component

shenyu-plugin

### What happened

`AiTokenLimiterPluginHandler` parses rule handles directly from JSON and caches them without filling defaults:

```java
final AiTokenLimiterHandle rateLimiterHandle = GsonUtils.getInstance().fromJson(s, AiTokenLimiterHandle.class);
CACHED_HANDLE.get().cachedHandle(CacheKeyUtils.INST.getKey(ruleData), rateLimiterHandle);
```

But the runtime assumes `tokenLimit` and `timeWindowSeconds` are always non-null:

```java
Long tokenLimit = aiTokenLimiterHandle.getTokenLimit();
Long timeWindowSeconds = aiTokenLimiterHandle.getTimeWindowSeconds();
...
return isAllowed(reactiveRedisTemplate, cacheKey, tokenLimit)
```

`isAllowed(...)` unboxes `tokenLimit` during comparison:

```java
if (Long.parseLong(currentTokens.toString()) >= tokenLimit) {
return Mono.just(false);
}
```

and token recording passes `timeWindowSeconds` to `Duration.ofSeconds(...)`:

```java
reactiveRedisTemplate.opsForValue()
.increment(cacheKey, tokens)
.flatMap(currentValue -> reactiveRedisTemplate.expire(cacheKey, Duration.ofSeconds(windowSeconds)))
.subscribe();
```

The admin metadata for these rule fields marks them as not required and does not provide default values:

```sql
INSERT INTO plugin_handle ... 'timeWindowSeconds' ... '{"required":"0","rule":""}'
INSERT INTO plugin_handle ... 'tokenLimit' ... '{"required":"0","rule":""}'
```

So a synced/saved rule handle missing either value can cause runtime failures instead of using the documented/default limiter settings.

### Expected behavior

AI token limiter rule handling should validate required fields or apply defaults consistently for rule handles, not only for the selector default-rule path. Missing `tokenLimit` or `timeWindowSeconds` should not crash request processing.

### How to reproduce

1. Enable the `ai-token-limiter` plugin with Redis config.
2. Create or sync a rule handle that omits `tokenLimit`, for example:

```json
{"aiTokenLimitType":"uri","timeWindowSeconds":60,"keyName":"default"}
```

3. Send a request matching the rule.
4. `isAllowed(...)` compares the current token count with a null `tokenLimit` and the request fails.

A handle that omits `timeWindowSeconds` can also fail when response token usage is recorded through `Duration.ofSeconds(windowSeconds)`.

### Debug logs

_No response_

### Environment

Current `master` branch.

### Are you willing to submit a PR?

- [ ] Yes I am willing to submit a PR!

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with AiTokenLimiterPluginHandler and trace how rule handles reach isAllowed(...) and the token-recording expire call after Gson parsing and caching. Compare this path with the selector default-rule handling, and inspect the plugin_handle metadata for tokenLimit and timeWindowSeconds. Done means a synced or saved handle missing either field no longer crashes request processing and the handling is consistent with the documented defaults or validation.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, redis, sql
Domain
api, backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.