Harden SENTINEL commands and config rewrite against control-character injection#3847
Conversation
Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. Signed-off-by: Eran Ifrah <eifrah@amazon.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds control-character validation helpers and applies them to SENTINEL CONFIG SET, MONITOR, and SET to reject ASCII control bytes (≤ 0x1F or 0x7F). Updates sentinel config rewrite to use sdscatrepr for several string fields. Adds runtime tests for argument rejection and config rewrite escaping. ChangesControl character validation in SENTINEL
🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/sentinel/tests/03-runtime-reconf.tcl (1)
202-217: ⚡ Quick winAdd one regression case for control chars in
<primaryname>.This test covers option/value tokens well; adding a
<primaryname>control-char case would close the argument-surface coverage.🧪 Suggested addition
test "SENTINEL SET rejects values containing control characters" { @@ # Control char in option name is also rejected assert_error "ERR Invalid argument*" {S 0 SENTINEL SET mymaster "bad\noption" value} + # Control char in primary name is also rejected + assert_error "ERR Invalid argument*" {S 0 SENTINEL SET "mymaster\nbad" quorum 2} }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/sentinel/tests/03-runtime-reconf.tcl` around lines 202 - 217, Add a regression case that verifies control characters in the <primaryname> argument are rejected: inside the test "SENTINEL SET rejects values containing control characters" add an assert_error call using the same pattern as the other checks but targeting the primary name token (e.g. assert_error "ERR Invalid argument*" {S 0 SENTINEL SET "bad\nname" auth-pass "val"}), so the S 0 SENTINEL SET invocation rejects a primaryname containing a newline, carriage return, null, tab, or DEL similarly to the existing auth-user/auth-pass checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/sentinel.c`:
- Around line 4238-4245: The loop that rejects control characters skips the
primary name because it starts at j = 3; update the SENTINEL SET validation to
include c->argv[2] (the <primaryname>) by starting the scan at j = 2 (or
otherwise explicitly check objectGetVal(c->argv[2]) with containsControlChars),
and keep the same error path using addReplyError when control characters are
found so <primaryname> cannot bypass the guard.
---
Nitpick comments:
In `@tests/sentinel/tests/03-runtime-reconf.tcl`:
- Around line 202-217: Add a regression case that verifies control characters in
the <primaryname> argument are rejected: inside the test "SENTINEL SET rejects
values containing control characters" add an assert_error call using the same
pattern as the other checks but targeting the primary name token (e.g.
assert_error "ERR Invalid argument*" {S 0 SENTINEL SET "bad\nname" auth-pass
"val"}), so the S 0 SENTINEL SET invocation rejects a primaryname containing a
newline, carriage return, null, tab, or DEL similarly to the existing
auth-user/auth-pass checks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3e11e46d-c8f8-48af-9cb8-23427bf3a32c
📒 Files selected for processing (2)
src/sentinel.ctests/sentinel/tests/03-runtime-reconf.tcl
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## unstable #3847 +/- ##
=========================================
Coverage 76.65% 76.66%
=========================================
Files 162 162
Lines 80694 80717 +23
=========================================
+ Hits 61857 61881 +24
+ Misses 18837 18836 -1
🚀 New features to boost your workflow:
|
ranshid
left a comment
There was a problem hiding this comment.
Thank you @eifrah-aws !
- I think we are missing some extra sentinal command flows.
sentinelMonitorCommandandsentinelConfigSetCommandboth will require the same handling of containsControlChars.sentinelHandleConfigurationAt startup, loadServerConfig reads each line of the conf file and tokenizes it with sdssplitargs(line, &argc). sdssplitargs honors quoted strings and escape sequences, so since sentinelHandleConfiguration NO content validation — whatever bytes sdssplitargs produced go straight into the in-memory field.createSentinelValkeyInstance- reached from hello-message processing for peer sentinels (so name/runid of a peer comes off the wire from another Sentinel)
- I guess aside for the commands processing, we could mitigate some of the existing and future cases by adding another layer of defence in rewriteConfigSentinelOption
Consolidate Sentinel argument sanitization into a shared helper and apply it to commands that persist user-provided values. This keeps the existing CRLF injection protection while making the validation consistent across SENTINEL SET, SENTINEL CONFIG SET, and SENTINEL MONITOR. The new helper scans all arguments from a given offset and rejects any value containing control characters before command-specific parsing continues. * Sentinel command validation * Config and monitor argument handling Signed-off-by: Eran Ifrah <eifrah@amazon.com>
ranshid
left a comment
There was a problem hiding this comment.
Title/description still say "Validate SENTINEL SET arguments," but the change now also covers SENTINEL MONITOR and SENTINEL CONFIG SET — worth updating so the scope (and changelog) reflects reality. Two inline notes on the validation surface below.
ranshid
left a comment
There was a problem hiding this comment.
Follow-up on the config-write sink: this can be hardened here as defense-in-depth.
Use sdscatrepr() to quote user-controlled string fields when writing the sentinel config file, preventing CRLF injection at the write sink regardless of ingress path (command, conf-load, or peer HELLO). Fields hardened: auth-pass, auth-user, sentinel-user, sentinel-pass, notification-script, client-reconfig-script, known-sentinel runid, and rename-command names. The existing announce-ip field already used sdscatrepr. Round-trips cleanly since sdssplitargs unquotes on load. Signed-off-by: Eran Ifrah <eifrah@amazon.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/sentinel.c`:
- Around line 2059-2060: Replace calls to strlen(...) used as the length
argument to sdscatrepr with sdslen(...) for all SDS-backed fields inside the
sdscatrepr usages; specifically update the sdscatrepr invocations that pass
primary->notification_script, primary->client_reconfig_script,
primary->auth_pass, primary->auth_user, ri->runid, sentinel.sentinel_auth_user
and sentinel.sentinel_auth_pass so they call sdslen(field) instead of
strlen(field). Locate the sdscatprintf/sdscatrepr sequences around the
sdscatrepr calls and only change the length argument to sdslen to avoid
truncation on embedded NULs while keeping the rest of the formatting logic
intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e07c3bf2-3dc6-4296-a0d5-b685b1877985
📒 Files selected for processing (2)
src/sentinel.ctests/sentinel/tests/03-runtime-reconf.tcl
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/sentinel/tests/03-runtime-reconf.tcl
Change runid, auth_pass, auth_user, notification_script, client_reconfig_script, sentinel_auth_pass, and sentinel_auth_user from char* to sds. Replace strlen() with sdslen() in sdscatrepr calls. Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (valkey-io#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
|
Problem when backporting this to 8.1: |
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (valkey-io#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
# Backport sweep for 9.1 Automated cherry-picks from PRs marked "To be backported". ## Applied | Source PR | Title | Detail | |---|---|---| | #3743 | Fix buffered_reply assert in HFE commands with module keyspace notifications | cherry-picked in a prior sweep | | #3766 | Fix flaky block_keyspace_notification test for HGETDEL notify race | cherry-picked in a prior sweep | | #3800 | Fix heap-use-after-free in ACL LOAD when client free is deferred | cherry-picked in a prior sweep | | #3723 | Fix double-finish and RESP reply violation in cluster slot migration | cherry-picked in a prior sweep | | #3872 | Redacting customer information when hide_user_data_from_log is true in rdb.c, networking.c, debug.c and t_hash | cherry-picked in a prior sweep | | #3846 | Fix use-after-free in VM_RegisterClusterMessageReceiver | cherry-picked in a prior sweep | | #3806 | Add ALL_DBS flag to CLUSTER FLUSHSLOT for database-level ACL | cherry-picked in a prior sweep | | #3847 | Harden SENTINEL commands and config rewrite against control-character injection | | | #3801 | Validate every DB clause in COPY against ACL db= permissions | | | #3851 | Replace AUTOMATION_PAT with Valkeyrie Bot GitHub App token | | | #3848 | Fix cluster AUX-field control-character and delimiter injection | | | #3544 | Revert "IO-Threads redesign cleanup work (#3544)" | cherry-picked in a prior sweep | | #3888 | Report exact dbid for COPY in ACL LOG when db= access is denied | conflicts resolved by Claude Code | | #3942 | Fix shard_id format specifier in UPDATE message log | | | #3941 | Avoid random() % 0 undefined behaviour when cluster-node-timeout < 30 | | --- *Generated by valkey-ci-agent using Claude Code.* --------- Signed-off-by: Binbin <binloveplay1314@qq.com> Signed-off-by: Ran Shidlansik <ranshid@amazon.com> Signed-off-by: chx9 <lovelypiska@outlook.com> Signed-off-by: zackcam <zackcam@amazon.com> Signed-off-by: Eran Ifrah <eifrah@amazon.com> Signed-off-by: Jules Lasarte <lasartej@amazon.com> Signed-off-by: jjuleslasarte <jules.lasarte@gmail.com> Signed-off-by: akash kumar <akumdev@amazon.com> Co-authored-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Ran Shidlansik <ranshid@amazon.com> Co-authored-by: lovelypiska <lovelypiska@outlook.com> Co-authored-by: zackcam <zackcam@amazon.com> Co-authored-by: eifrah-aws <eifrah@amazon.com> Co-authored-by: jjuleslasarte <jules.lasarte@gmail.com> Co-authored-by: Jules Lasarte <lasartej@amazon.com> Co-authored-by: Akash Kumar <45854686+akashkgit@users.noreply.github.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
… injection (#3847) Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values. --------- Signed-off-by: Eran Ifrah <eifrah@amazon.com>
Reject any SENTINEL SET argument containing control characters (0x00-0x1F, 0x7F) to prevent config injection via newline-embedded values.