Skip to content

fix(redis): reject invalid key types before generating update commands - #2437

Merged
openai0229 merged 12 commits into
OtterMind:mainfrom
Aias00:fix/redis-update-null-type-npe-2436
Sep 11, 2026
Merged

openai0229 merged 12 commits into
OtterMind:mainfrom
Aias00:fix/redis-update-null-type-npe-2436

Conversation

@Aias00

@Aias00 Aias00 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Related issue

Closes #2436

Summary

Redis key updates with a missing or unsupported type can throw a NullPointerException or fall back to string commands, deleting the old key and writing a replacement. Validate every present old/new key before generating commands and reject null, blank, none, or unknown type codes with BusinessException.

A null key object remains a valid create/delete signal; two null objects remain a no-op. Supported types are string, list, set, zset, hash, and stream.

Affected surfaces

  • Frontend / Web
  • Backend / API / Storage
  • Database plugin / Driver
  • JCEF / Desktop packaging
  • CI / Build / Release
  • Documentation only

Verification

  • Commands and results:
    • Exact main/PR class compilation with Java 17 and JUnit Platform: main 8/8, PR 14/14 RedisScriptExecutorUpdateTest cases passed.
    • An instrumented JDBC connection captured 20 updates per version: all 14 invalid PR inputs threw BusinessException before prepareStatement; six valid command sequences matched main.
    • git diff --check main...HEAD: passed.
    • mvn -B -f chat2db-community-server/pom.xml -pl :chat2db-community-redis -am -Dmaven.test.skip=false -DskipTests=false '-Dsurefire.includes=**/*Test.java' -Dmaven.test.failure.ignore=false package: passed on the synchronized branch; 317 tests, 0 failures, 0 errors, 0 skipped. Redis module: 66 tests. All 8 reactor modules built successfully.
  • Manual verification: Replayed the captured commands into an isolated Redis 7.4.2 container and read back data (40 cases across main/PR). All 14 invalid PR cases preserved data; 12 valid readbacks across both versions passed for create, delete, update, rename/TTL, type conversion, and no-op. This verified generated Redis commands, not the complete Web/JDBC transport. No real Web or desktop interaction test was run.
  • UI evidence: N/A

Risk and compatibility

  • Public API or stored data: Malformed type inputs are now rejected before any Redis write. Request shape and valid key data are unchanged.
  • Database or driver compatibility: Supported Redis type codes and create/delete object-null semantics are preserved; Redis 7.4.2 command behavior was verified.
  • Network, privacy, or security: No new network operations or credentials; invalid inputs no longer reach delete, rename, type-specific writes, or expiry commands.
  • Community / Local / Pro boundary: N/A; no runtime-mode or product-boundary changes.
  • Backward compatibility: Clients submitting missing, none, blank, or unknown type codes now receive an explicit error instead of a null dereference or string fallback.

Reviewer map

  • Start here: RedisScriptExecutor.update and validateKeyType; regressions in RedisScriptExecutorUpdateTest.
  • Failure condition: A present RedisKey has an unsupported type. Validation runs before command generation for both old and new keys.
  • Rollback or disable path: Revert this PR; no storage migration is involved.

Contributor declaration

  • I linked the Issue that defines this change.
  • I tested the affected behavior and reported the actual results above.
  • I did not include credentials, private data, or generated build output.
  • I disclosed substantial AI assistance below, or this PR contains no substantial AI-generated code.

AI assistance: The original patch and follow-up fixes were authored with Claude Code. Codex re-reviewed the final implementation, ran the tests and Redis command verification above, and synchronized the latest main.

Copilot AI review requested due to automatic review settings August 3, 2026 13:26
@Aias00
Aias00 requested a review from openai0229 as a code owner August 3, 2026 13:26
@openai0229 openai0229 moved this to In Review in Chat2DB Community Aug 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a Redis plugin bug where RedisScriptExecutor.update could throw a NullPointerException when comparing Redis key types that may be null (e.g., when a key does not exist and getKeyType returns null). The fix makes the type comparison null-safe to prevent crashes during key updates.

Changes:

  • Replace oldKey.getType().equals(newKey.getType()) with Objects.equals(oldKey.getType(), newKey.getType()) to avoid NPEs when either type is null.
  • Add the required java.util.Objects import.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@openai0229 openai0229 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only moves the null dereference. When the new type is null and the old type is non-null, typeChanged becomes true and RedisDataType.fromCode(newKey.getType()).getScript() still dereferences null. When both types are null, the else branch does the same through typeSource. Please validate both old/new type contracts before either branch (including unknown codes), define whether invalid updates should be rejected or treated as no-op, and add focused tests for one-null, both-null, and unknown-type cases.

Aias00 added a commit to Aias00/Chat2DB that referenced this pull request Aug 4, 2026
…nd#2437)

Reviewer feedback: Objects.equals only prevented the .equals() NPE but
RedisDataType.fromCode(newKey.getType()) still NPEs when newKey.getType()
is null (key doesn't exist). Add null guards for BOTH old and new types
before the typeChanged check; if either is null, fall through to the
rename path instead of entering the type-change branch.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: liuhy <liuhongyu@apache.org>
@Aias00

Aias00 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Good catch — thank you. The previous Objects.equals only prevented the .equals() NPE but RedisDataType.fromCode(null) downstream still NPEs. Added null guards for both oldKey.getType() and newKey.getType() before the typeChanged check. If either is null (key doesn't exist), typeChanged is false and the code falls through to the rename path, which doesn't dereference getType(). Pushed in the same branch.

@openai0229

Copy link
Copy Markdown
Contributor

Thanks for the follow-up. The current guard still does not make this update path safe.

RedisDataType.fromCode(null) does not throw an NPE; it returns NONE, and NONE.getScript() falls back to StringTypeScript. By treating a null old or new type as "not changed", the code continues into the rename/update branch with an invalid type:

  • old.type=list, new.type=null can still run ListTypeScript and may emit DEL for an empty replacement.
  • old.type=null, new.type=list selects StringTypeScript, so the list is not created or converted correctly.
  • A renamed missing key still emits RENAME and fails with no such key.
  • none and unknown type codes also silently fall back to string behavior.

The normal UI only submits string, list, set, zset, hash, or stream; when key detail returns type=none, it removes the vanished row instead of opening the editor. Null/unknown types therefore look like malformed or stale direct API input and should be rejected explicitly before generating any Redis command.

Please validate both old and new types against the supported set before DEL, RENAME, type-specific writes, or EXPIRE, and add focused tests for old-null, new-null, both-null, none, unknown codes, and rename of a missing key. The tests should also verify that invalid input emits no Redis commands.

Aias00 added a commit to Aias00/Chat2DB that referenced this pull request Aug 6, 2026
Address review on OtterMind#2437: the previous null-guard only prevented the .equals()
NPE, but RedisDataType.fromCode(null/none/unknown) returns NONE and falls back
to StringTypeScript, so malformed/stale input still ran type-specific writes,
DEL, RENAME (failing with "no such key"), or EXPIRE.

Validate both old and new key types against {string,list,set,zset,hash,stream}
up front and reject (BusinessException) before any script is generated, so no
Redis command can be emitted for invalid input.

Tests cover old-null, new-null, both-null, none, unknown code, and rename of a
missing key — each asserts the update is rejected.

Co-Authored-By: Claude <noreply@anthropic.com>
@Aias00

Aias00 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review — addressed in 8de3e62b.

  • update() now validates both oldKey and newKey types against {string, list, set, zset, hash, stream} up front and rejects (BusinessException) before any script is generated. null, none, and unknown codes are all rejected, so no DEL/RENAME/type-specific write/EXPIRE can be emitted for invalid input.
  • RedisDataType.fromCode returning NONE (for none and unknown codes) is now treated as invalid rather than falling back to StringTypeScript.
  • Tests added in RedisScriptExecutorUpdateTest: old-null, new-null, both-null, none, unknown code, and rename of a missing key — each asserts the update is rejected (no command emitted). Existing both-null and type-change-abort cases still pass.

Full redis module suite: 59 tests, 0 failures.

Aias00 and others added 4 commits August 21, 2026 02:30
…tterMind#2436)

oldKey.getType().equals(newKey.getType()) NPEs when either type is null
(getKeyType returns null for a non-existent key). Use Objects.equals.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: liuhy <liuhongyu@apache.org>
…nd#2437)

Reviewer feedback: Objects.equals only prevented the .equals() NPE but
RedisDataType.fromCode(newKey.getType()) still NPEs when newKey.getType()
is null (key doesn't exist). Add null guards for BOTH old and new types
before the typeChanged check; if either is null, fall through to the
rename path instead of entering the type-change branch.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: liuhy <liuhongyu@apache.org>
Address review on OtterMind#2437: the previous null-guard only prevented the .equals()
NPE, but RedisDataType.fromCode(null/none/unknown) returns NONE and falls back
to StringTypeScript, so malformed/stale input still ran type-specific writes,
DEL, RENAME (failing with "no such key"), or EXPIRE.

Validate both old and new key types against {string,list,set,zset,hash,stream}
up front and reject (BusinessException) before any script is generated, so no
Redis command can be emitted for invalid input.

Tests cover old-null, new-null, both-null, none, unknown code, and rename of a
missing key — each asserts the update is rejected.

Co-Authored-By: Claude <noreply@anthropic.com>
Constraint: Preserve the existing both-null no-op while treating single-null update inputs as malformed
Rejected: Leaving key==null as a pass-through | It still permits stale direct API input to reach rename/update paths
Confidence: high
Scope-risk: narrow
Tested: mvn -B -f chat2db-community-server/pom.xml -pl :chat2db-community-redis -am -Dmaven.test.skip=false -DskipTests=false -Dtest=RedisScriptExecutorUpdateTest -Dsurefire.failIfNoSpecifiedTests=false -Dmaven.test.failure.ignore=false test
Tested: git diff --check
@Aias00
Aias00 force-pushed the fix/redis-update-null-type-npe-2436 branch from 3da229e to e29b12b Compare August 21, 2026 09:32
@Aias00

Aias00 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@openai0229 The requested Redis null-type handling is addressed on current head 697ebb3cf: old/new null, none, and unknown type codes are validated before script selection, with focused regression tests covering those cases. All current-head checks are successful. Please re-review when available.

@Aias00
Aias00 requested a review from openai0229 September 1, 2026 22:40
@openai0229 openai0229 changed the title fix(redis): guard RedisScriptExecutor.update against null key types fix(redis): reject invalid key types before generating update commands Sep 11, 2026

@openai0229 openai0229 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed synchronized head 545fe7c. The previous requested changes are addressed: both present key types are validated before command generation, while null key objects retain create/delete and no-op semantics. No blocking findings.

The synchronized Redis reactor passed 316 tests (66 in the Redis module), with zero failures or skips, and all 8 modules packaged successfully. The three PR files are unchanged from the previously reviewed version, where 14 invalid inputs emitted no commands and captured commands were replayed/read back in isolated Redis 7.4.2 (40 cases).

@openai0229
openai0229 merged commit 237498e into OtterMind:main Sep 11, 2026
16 of 18 checks passed
@openai0229 openai0229 moved this from In Review to Done in Chat2DB Community Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

bug(redis): RedisScriptExecutor.update NPEs when key type is null

3 participants