Files
185d2ad7d9 Allow some tags only on top level to fix --tags filtering (#3171)
The problem: when running `./runtest --large-memory --tags large-memory`
the `--tags large-memory` filter would look for tests with
`large-memory` tag, it found the nested tag in the test context but
`start_server` didn't have `large-memory`, so the filtering logic had
issues. And that's why we did not notice how we introduced this failure:

```
[err]: CVE-2025-32023: Sparse HLL XZERO overflow triggers crash in tests/unit/hyperloglog.tcl
Expected an error matching '*INVALIDOBJ*' but got '1' (context: type eval line 24 cmd {assert_error {*INVALIDOBJ*} {r pfadd hll_overflow foo}} proc ::test)
```

Tags `large-memory`, `needs:other-server`, `compatible-redis`, and
`network` now restricted to top-level `start_server`. Prevents `--tags`
filtering issues when these tags appear in nested contexts. Added
validation that errors on nested usage. Refactored tests that fail new
validation. Also fixed all `lsearch` calls to use `-exact` flag to
preventing bugs like "tls" matching "tls:skip".

---------

Signed-off-by: Daniil Kashapov <[email protected]>
Co-authored-by: Viktor Söderqvist <[email protected]>
2026-02-09 12:01:30 +01:00

169 lines
6.8 KiB
Tcl

set testmodule [file normalize tests/modules/usercall.so]
set test_script_set "#!lua
redis.call('set','x',1)
return 1"
set test_script_get "#!lua
redis.call('get','x')
return 1"
start_server {tags {"modules usercall network"}} {
r module load $testmodule
# baseline test that module isn't doing anything weird
test {test module check regular valkey command without user/acl} {
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
assert_equal [r usercall.call_without_user set x 5] OK
assert_equal [r usercall.reset_user] OK
}
# call with user with acl set on it, but without testing the acl
test {test module check regular valkey command with user} {
assert_equal [r set x 5] OK
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
# off and sanitize-payload because module user / default value
assert_equal [r usercall.get_acl] "off sanitize-payload ~* &* alldbs +@all -set"
# doesn't fail for regular commands as just testing acl here
assert_equal [r usercall.call_with_user_flag {} set x 10] OK
assert_equal [r get x] 10
assert_equal [r usercall.reset_user] OK
}
# call with user with acl set on it, but with testing the acl in rm_call (for cmd itself)
test {test module check regular valkey command with user and acl} {
assert_equal [r set x 5] OK
r ACL LOG RESET
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
# off and sanitize-payload because module user / default value
assert_equal [r usercall.get_acl] "off sanitize-payload ~* &* alldbs +@all -set"
# fails here as testing acl in rm call
assert_error {*NOPERM User module_user has no permissions*} {r usercall.call_with_user_flag C set x 10}
assert_equal [r usercall.call_with_user_flag C get x] 5
# verify that new log entry added
set entry [lindex [r ACL LOG] 0]
assert_equal [dict get $entry username] {module_user}
assert_equal [dict get $entry context] {module}
assert_equal [dict get $entry object] {set}
assert_equal [dict get $entry reason] {command}
assert_match {*cmd=usercall.call_with_user_flag*} [dict get $entry client-info]
assert_equal [r usercall.reset_user] OK
}
# call with user with acl set on it, but with testing the acl in rm_call (for cmd itself)
test {test module check regular valkey command with user and acl from blocked background thread} {
assert_equal [r set x 5] OK
r ACL LOG RESET
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
# fails here as testing acl in rm call from a background thread
assert_error {*NOPERM User module_user has no permissions*} {r usercall.call_with_user_bg C set x 10}
assert_equal [r usercall.call_with_user_bg C get x] 5
# verify that new log entry added
set entry [lindex [r ACL LOG] 0]
assert_equal [dict get $entry username] {module_user}
assert_equal [dict get $entry context] {module}
assert_equal [dict get $entry object] {set}
assert_equal [dict get $entry reason] {command}
assert_match {*cmd=NULL*} [dict get $entry client-info]
assert_equal [r usercall.reset_user] OK
}
# baseline script test, call without user on script
test {test module check eval script without user} {
set sha_set [r script load $test_script_set]
set sha_get [r script load $test_script_get]
assert_equal [r usercall.call_without_user evalsha $sha_set 0] 1
assert_equal [r usercall.call_without_user evalsha $sha_get 0] 1
}
# baseline script test, call without user on script
test {test module check eval script with user being set, but not acl testing} {
set sha_set [r script load $test_script_set]
set sha_get [r script load $test_script_get]
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
# off and sanitize-payload because module user / default value
assert_equal [r usercall.get_acl] "off sanitize-payload ~* &* alldbs +@all -set"
# passes as not checking ACL
assert_equal [r usercall.call_with_user_flag {} evalsha $sha_set 0] 1
assert_equal [r usercall.call_with_user_flag {} evalsha $sha_get 0] 1
}
# call with user on script (without rm_call acl check) to ensure user carries through to script execution
# we already tested the check in rm_call above, here we are checking the script itself will enforce ACL
test {test module check eval script with user and acl} {
set sha_set [r script load $test_script_set]
set sha_get [r script load $test_script_get]
r ACL LOG RESET
assert_equal [r usercall.reset_user] OK
assert_equal [r usercall.add_to_acl "~* &* +@all -set"] OK
# fails here in script, as rm_call will permit the eval call
catch {r usercall.call_with_user_flag C evalsha $sha_set 0} e
assert_match {*ERR ACL failure in script*} $e
assert_equal [r usercall.call_with_user_flag C evalsha $sha_get 0] 1
# verify that new log entry added
set entry [lindex [r ACL LOG] 0]
assert_equal [dict get $entry username] {module_user}
assert_equal [dict get $entry context] {lua}
assert_equal [dict get $entry object] {set}
assert_equal [dict get $entry reason] {command}
assert_match {*cmd=usercall.call_with_user_flag*} [dict get $entry client-info]
}
start_server {tags {"wait aof external:skip"}} {
set slave [srv 0 client]
set slave_host [srv 0 host]
set slave_port [srv 0 port]
set slave_pid [srv 0 pid]
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
$master config set appendonly yes
$master config set appendfsync everysec
$slave config set appendonly yes
$slave config set appendfsync everysec
test {Setup slave} {
$slave slaveof $master_host $master_port
wait_for_condition 50 100 {
[s 0 master_link_status] eq {up}
} else {
fail "Replication not started."
}
}
test {test module replicate only to replicas and WAITAOF} {
$master set x 1
assert_equal [$master waitaof 1 1 10000] {1 1}
$master usercall.call_with_user_flag A! config set loglevel notice
# Make sure WAITAOF doesn't hang
assert_equal [$master waitaof 1 1 10000] {1 1}
}
}
}