diff options
Diffstat (limited to 'test')
24 files changed, 844 insertions, 140 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 67ff98050..5a4e73f77 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,56 +1,115 @@ -include(CTest) - -IF(BUILD_TESTING MATCHES "ON") - SET(TESTSRC rspamd_mem_pool_test.c - rspamd_statfile_test.c - rspamd_url_test.c - rspamd_dns_test.c - rspamd_dkim_test.c - rspamd_rrd_test.c - rspamd_radix_test.c - rspamd_shingles_test.c - rspamd_upstream_test.c - rspamd_lua_pcall_vs_resume_test.c - rspamd_lua_test.c - rspamd_cryptobox_test.c - rspamd_heap_test.c - rspamd_test_suite.c) - - ADD_EXECUTABLE(rspamd-test ${TESTSRC}) - SET_TARGET_PROPERTIES(rspamd-test PROPERTIES COMPILE_FLAGS "-DRSPAMD_TEST") - ADD_DEPENDENCIES(rspamd-test rspamd-server) - SET_TARGET_PROPERTIES(rspamd-test PROPERTIES LINKER_LANGUAGE CXX) - TARGET_LINK_LIBRARIES(rspamd-test rspamd-server) - ADD_TEST(NAME rspamd-test COMMAND rspamd-test "-p" "/rspamd/lua") - - SET(CXXTESTSSRC rspamd_cxx_unit.cxx) - - ADD_EXECUTABLE(rspamd-test-cxx ${CXXTESTSSRC}) - SET_TARGET_PROPERTIES(rspamd-test-cxx PROPERTIES LINKER_LANGUAGE CXX) - ADD_DEPENDENCIES(rspamd-test-cxx rspamd-server) - TARGET_LINK_LIBRARIES(rspamd-test-cxx PRIVATE rspamd-server) - SET_TARGET_PROPERTIES(rspamd-test-cxx PROPERTIES LINKER_LANGUAGE CXX) - ADD_TEST(NAME rspamd-test-cxx COMMAND rspamd-test-cxx) - - IF(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") - # Also add dependencies for convenience - FILE(GLOB_RECURSE LUA_TESTS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/lua/*.*") - ADD_CUSTOM_TARGET(units-dir COMMAND - ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/lua/unit" - ) - ADD_DEPENDENCIES(rspamd-test units-dir) - FOREACH(_LF IN LISTS LUA_TESTS) - GET_FILENAME_COMPONENT(_NM "${_LF}" NAME) - IF("${_LF}" MATCHES "^.*/unit/.*$") - SET(_DS "${CMAKE_CURRENT_BINARY_DIR}/lua/unit/${_NM}") - ELSE() - SET(_DS "${CMAKE_CURRENT_BINARY_DIR}/lua/${_NM}") - ENDIF() - ADD_CUSTOM_TARGET("${_NM}" COMMAND - ${CMAKE_COMMAND} -E copy_if_different ${_LF} ${_DS} - SOURCES "${_LF}" - ) - ADD_DEPENDENCIES(rspamd-test "${_NM}") - ENDFOREACH() - ENDIF() -ENDIF()
\ No newline at end of file + +# Define C test sources +set(RSPAMD_TEST_SOURCES + rspamd_mem_pool_test.c + rspamd_statfile_test.c + rspamd_url_test.c + rspamd_dns_test.c + rspamd_dkim_test.c + rspamd_rrd_test.c + rspamd_radix_test.c + rspamd_shingles_test.c + rspamd_upstream_test.c + rspamd_lua_pcall_vs_resume_test.c + rspamd_lua_test.c + rspamd_cryptobox_test.c + rspamd_heap_test.c + rspamd_test_suite.c +) + +# Main C test executable +add_executable(rspamd-test ${RSPAMD_TEST_SOURCES}) +target_compile_definitions(rspamd-test PRIVATE RSPAMD_TEST) +target_link_libraries(rspamd-test PRIVATE rspamd-server) +set_target_properties(rspamd-test PROPERTIES + LINKER_LANGUAGE CXX +) + +# Add the test +add_test( + NAME rspamd-test + COMMAND rspamd-test -p /rspamd/lua +) + +# C++ unit tests +set(CXX_TEST_SOURCES rspamd_cxx_unit.cxx) + +# C++ test executable +add_executable(rspamd-test-cxx ${CXX_TEST_SOURCES}) +target_link_libraries(rspamd-test-cxx PRIVATE rspamd-server) +set_target_properties(rspamd-test-cxx PROPERTIES + LINKER_LANGUAGE CXX +) + +# Add the C++ test +add_test( + NAME rspamd-test-cxx + COMMAND rspamd-test-cxx +) + +# Copy test Lua scripts for out-of-source builds +if (NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}") + # Find Lua test files + file(GLOB_RECURSE LUA_TESTS CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/lua/*.*") + + # Create target to ensure unit directory exists + add_custom_target(units-dir + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/lua/unit" + ) + + # Make the main test depend on the directory creation + add_dependencies(rspamd-test units-dir) + + # Create file-specific targets for each Lua test file + foreach (lua_file IN LISTS LUA_TESTS) + # Extract just the filename + get_filename_component(file_name "${lua_file}" NAME) + + # Determine destination path + if ("${lua_file}" MATCHES "^.*/unit/.*$") + set(dest_file "${CMAKE_CURRENT_BINARY_DIR}/lua/unit/${file_name}") + else () + set(dest_file "${CMAKE_CURRENT_BINARY_DIR}/lua/${file_name}") + endif () + + # Create custom target to copy the file + add_custom_target("${file_name}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${lua_file}" "${dest_file}" + SOURCES "${lua_file}" + COMMENT "Copying ${file_name} to test directory" + ) + + # Make the main test depend on this file copy + add_dependencies(rspamd-test "${file_name}") + endforeach () +endif () + +# Add test properties +set_tests_properties(rspamd-test PROPERTIES + ENVIRONMENT "RSPAMD_INSTALLROOT=${CMAKE_INSTALL_PREFIX}" +) + +# Custom target for running all tests directly (convenience target) +add_custom_target(run-tests + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + DEPENDS rspamd-test rspamd-test-cxx + COMMENT "Running all Rspamd tests" +) + +# Custom target for running C tests only +add_custom_target(run-c-tests + COMMAND ${CMAKE_COMMAND} -E echo "Running C unit tests..." + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rspamd-test -p /rspamd/lua + DEPENDS rspamd-test + COMMENT "Running Rspamd C tests" +) + +# Custom target for running C++ tests only +add_custom_target(run-cxx-tests + COMMAND ${CMAKE_COMMAND} -E echo "Running C++ unit tests..." + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rspamd-test-cxx + DEPENDS rspamd-test-cxx + COMMENT "Running Rspamd C++ tests" +)
\ No newline at end of file diff --git a/test/functional/cases/001_merged/102_multimap.robot b/test/functional/cases/001_merged/102_multimap.robot index 50d1af6b6..a16d0e5c4 100644 --- a/test/functional/cases/001_merged/102_multimap.robot +++ b/test/functional/cases/001_merged/102_multimap.robot @@ -418,6 +418,16 @@ MAP - EXTERNAL MISS ... Settings={symbols_enabled = [EXTERNAL_MULTIMAP]} Do Not Expect Symbol EXTERNAL_MULTIMAP +MAP - EXTERNAL CDB + Scan File ${MESSAGE} IP=127.0.0.1 Hostname=example.com + ... Settings={symbols_enabled = [EXTERNAL_MULTIMAP_CDB]} + Expect Symbol EXTERNAL_MULTIMAP_CDB + +MAP - EXTERNAL CDB MISS + Scan File ${MESSAGE} IP=127.0.0.1 Hostname=example.com.bg + ... Settings={symbols_enabled = [EXTERNAL_MULTIMAP_CDB]} + Do Not Expect Symbol EXTERNAL_MULTIMAP_CDB + MAP - DYNAMIC SYMBOLS - SYM1 Scan File ${MESSAGE} IP=127.0.0.1 Hostname=foo ... Settings={symbols_enabled = [DYN_TEST1,DYN_TEST2,DYN_MULTIMAP]} diff --git a/test/functional/cases/001_merged/350_magic.robot b/test/functional/cases/001_merged/350_magic.robot index 66a18f2af..b2746ce3c 100644 --- a/test/functional/cases/001_merged/350_magic.robot +++ b/test/functional/cases/001_merged/350_magic.robot @@ -65,3 +65,4 @@ Magic detections bundle 1 ... MAGIC_SYM_ICS_55 ... MAGIC_SYM_VCF_56 ... MAGIC_SYM_CSV_57 + ... MAGIC_SYM_HEIC_58 diff --git a/test/functional/cases/120_fuzzy/lib.robot b/test/functional/cases/120_fuzzy/lib.robot index fda0af54a..a57ecc742 100644 --- a/test/functional/cases/120_fuzzy/lib.robot +++ b/test/functional/cases/120_fuzzy/lib.robot @@ -16,6 +16,7 @@ ${RSPAMD_FUZZY_ENCRYPTED_ONLY} false ${RSPAMD_FUZZY_ENCRYPTION_KEY} null ${RSPAMD_FUZZY_INCLUDE} ${RSPAMD_TESTDIR}/configs/empty.conf ${RSPAMD_FUZZY_KEY} null +${RSPAMD_FUZZY_SERVER_MODE} servers ${RSPAMD_FUZZY_SHINGLES_KEY} null ${RSPAMD_SCOPE} Suite ${SETTINGS_FUZZY_CHECK} ${EMPTY} @@ -109,6 +110,7 @@ Fuzzy Setup Encrypted Set Suite Variable ${RSPAMD_FUZZY_ENCRYPTION_KEY} ${RSPAMD_KEY_PUB1} Set Suite Variable ${RSPAMD_FUZZY_CLIENT_ENCRYPTION_KEY} ${RSPAMD_KEY_PUB1} Set Suite Variable ${RSPAMD_FUZZY_INCLUDE} ${RSPAMD_TESTDIR}/configs/fuzzy-encryption-key.conf + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers Rspamd Redis Setup Fuzzy Setup Encrypted Dyn1 @@ -142,6 +144,8 @@ Fuzzy Setup Encrypted Keyed Fuzzy Setup Plain [Arguments] ${algorithm} Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} ${algorithm} + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers + Set Suite Variable ${SETTINGS_FUZZY_CHECK} servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}"; Rspamd Redis Setup Fuzzy Setup Keyed @@ -149,6 +153,7 @@ Fuzzy Setup Keyed Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} ${algorithm} Set Suite Variable ${RSPAMD_FUZZY_KEY} mYN888sydwLTfE32g2hN Set Suite Variable ${RSPAMD_FUZZY_SHINGLES_KEY} hXUCgul9yYY3Zlk1QIT2 + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} servers Rspamd Redis Setup Fuzzy Setup Plain Fasthash @@ -218,3 +223,21 @@ Fuzzy Multimessage Overwrite Test FOR ${i} IN @{MESSAGES} Fuzzy Overwrite Test ${i} END + +Fuzzy Setup Split Servers + Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} split + Set Suite Variable ${SETTINGS_FUZZY_CHECK} read_servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}"; write_servers = "${RSPAMD_LOCAL_ADDR}:${RSPAMD_PORT_FUZZY}"; + Rspamd Redis Setup + +Fuzzy Setup Read Only + Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} read_only + Set Suite Variable ${SETTINGS_FUZZY_CHECK} read_only = true; + Rspamd Redis Setup + +Fuzzy Setup Write Only + Set Suite Variable ${RSPAMD_FUZZY_ALGORITHM} siphash + Set Suite Variable ${RSPAMD_FUZZY_SERVER_MODE} write_only + Set Suite Variable ${SETTINGS_FUZZY_CHECK} mode = "write_only"; + Rspamd Redis Setup diff --git a/test/functional/cases/120_fuzzy/read-only.robot b/test/functional/cases/120_fuzzy/read-only.robot new file mode 100644 index 000000000..0c3be7fec --- /dev/null +++ b/test/functional/cases/120_fuzzy/read-only.robot @@ -0,0 +1,14 @@ +*** Settings *** +Suite Setup Fuzzy Setup Read Only +Suite Teardown Rspamd Redis Teardown +Resource lib.robot + +*** Test Cases *** +Fuzzy Add + Fuzzy Multimessage Add Test + +Fuzzy Fuzzy + Fuzzy Multimessage Fuzzy Test + +Fuzzy Miss + Fuzzy Multimessage Miss Test diff --git a/test/functional/cases/120_fuzzy/split-servers.robot b/test/functional/cases/120_fuzzy/split-servers.robot new file mode 100644 index 000000000..41acb23ff --- /dev/null +++ b/test/functional/cases/120_fuzzy/split-servers.robot @@ -0,0 +1,18 @@ +*** Settings *** +Suite Setup Fuzzy Setup Split Servers +Suite Teardown Rspamd Redis Teardown +Resource lib.robot +Variables ${RSPAMD_TESTDIR}/lib/vars.py + +*** Variables *** +${CONFIG} ${RSPAMD_TESTDIR}/configs/fuzzy-split-servers.conf + +*** Test Cases *** +Fuzzy Add + Fuzzy Multimessage Add Test + +Fuzzy Fuzzy + Fuzzy Multimessage Fuzzy Test + +Fuzzy Miss + Fuzzy Multimessage Miss Test diff --git a/test/functional/cases/120_fuzzy/write-only.robot b/test/functional/cases/120_fuzzy/write-only.robot new file mode 100644 index 000000000..ee017a1d3 --- /dev/null +++ b/test/functional/cases/120_fuzzy/write-only.robot @@ -0,0 +1,14 @@ +*** Settings *** +Suite Setup Fuzzy Setup Write Only +Suite Teardown Rspamd Redis Teardown +Resource lib.robot + +*** Test Cases *** +Fuzzy Add + Fuzzy Multimessage Add Test + +Fuzzy Fuzzy + Fuzzy Multimessage Fuzzy Test + +Fuzzy Miss + Fuzzy Multimessage Miss Test diff --git a/test/functional/cases/400_known_senders.robot b/test/functional/cases/400_known_senders.robot index d827acc0e..a7cde59cb 100644 --- a/test/functional/cases/400_known_senders.robot +++ b/test/functional/cases/400_known_senders.robot @@ -43,33 +43,37 @@ INCOMING MAIL SENDER IS UNKNOWN ... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} Do Not Expect Symbol ${SYMBOL_GLOBAL} Do Not Expect Symbol ${SYMBOL_LOCAL} - + INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE UNKNOWN Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... User=xxx@abrakadabra.com + ... From=xxx@abrakadabra.com ... Settings=${SETTINGS_REPLIES} Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... Settings=${SETTINGS_REPLIES} + ... Rcpt=xxx@abrakadabra.com ... Settings=${SETTINGS_REPLIES} + ... From=user@emailbl.com Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 ... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} Expect Symbol ${SYMBOL_GLOBAL} Do Not Expect Symbol ${SYMBOL_LOCAL} INCOMING MAIL SENDER IS KNOWN RECIPIENTS ARE KNOWN Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 User=user@emailbl.com From=user@emailbl.com ... Settings=${SETTINGS_REPLIES} Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com ... Settings=${SETTINGS_REPLIES} Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com ... Settings=${SETTINGS_REPLIES} Scan File ${RSPAMD_TESTDIR}/messages/inc_mail_known_sender.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 User=user@emailbl.com Rcpt=user@emailbl.com ... Settings={symbols_enabled [${SYMBOL_GLOBAL}, ${SYMBOL_LOCAL}]} Expect Symbol ${SYMBOL_GLOBAL} Expect Symbol ${SYMBOL_LOCAL} - diff --git a/test/functional/cases/410_replies.robot b/test/functional/cases/410_replies.robot index 23ad9df35..b6710149c 100644 --- a/test/functional/cases/410_replies.robot +++ b/test/functional/cases/410_replies.robot @@ -15,33 +15,36 @@ ${RSPAMD_SCOPE} Suite *** Test Cases *** Reply to 1 sender 1 recipients Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... User=xxx@abrakadabra.com + ... From=xxx@abrakadabra.com ... Settings=${SETTINGS_REPLIES} + ... Rcpt=user@emailbl.com Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_1.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... Rcpt=xxx@abrakadabra.com ... Settings=${SETTINGS_REPLIES} + ... From=user@emailbl.com Expect Symbol ${SYMBOL} -Reply to 1 sender 2 recipients first is set second is not +Reply to 1 sender 2 recipients but SMTP recipient matches Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_2_first.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... User=xxxx@emailbl.com ... Settings=${SETTINGS_REPLIES} Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2.eml - ... IP=8.8.8.8 User=user@emailbl.com + ... IP=8.8.8.8 + ... Rcpt=xxxx@emailbl.com ... Settings=${SETTINGS_REPLIES} Expect Symbol ${SYMBOL} -Reply to 1 sender 2 recipients 1 rcpt is same - Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2_s.eml - ... IP=8.8.8.8 User=user@emailbl.com +Reply to 1 sender 2 recipients but SMTP recipient NOT matches + Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_1_2_first.eml + ... IP=8.8.8.8 + ... User=user@emailbl.com ... Settings=${SETTINGS_REPLIES} - Expect Symbol ${SYMBOL} - -Reply to another sender 2 recipients - Scan File ${RSPAMD_TESTDIR}/messages/set_replyto_2_2.eml - ... IP=8.8.8.8 User=another@emailbl.com + Scan File ${RSPAMD_TESTDIR}/messages/replyto_1_2.eml + ... IP=8.8.8.8 User=user@emailbl.com + ... Rcpt=another@emailbl.com ... Settings=${SETTINGS_REPLIES} - Scan File ${RSPAMD_TESTDIR}/messages/replyto_2_2.eml - ... IP=8.8.8.8 User=another@emailbl.com - ... Settings=${SETTINGS_REPLIES} - Expect Symbol ${SYMBOL} + Do Not Expect Symbol ${SYMBOL} diff --git a/test/functional/cases/410_logging/000_console/000_systemd_logger.robot b/test/functional/cases/411_logging/000_console/000_systemd_logger.robot index 88178461b..88178461b 100644 --- a/test/functional/cases/410_logging/000_console/000_systemd_logger.robot +++ b/test/functional/cases/411_logging/000_console/000_systemd_logger.robot diff --git a/test/functional/cases/410_logging/000_console/001_timestamps.robot b/test/functional/cases/411_logging/000_console/001_timestamps.robot index bd8e2c349..bd8e2c349 100644 --- a/test/functional/cases/410_logging/000_console/001_timestamps.robot +++ b/test/functional/cases/411_logging/000_console/001_timestamps.robot diff --git a/test/functional/cases/410_logging/001_file/000_json.robot b/test/functional/cases/411_logging/001_file/000_json.robot index a2f04e85c..a2f04e85c 100644 --- a/test/functional/cases/410_logging/001_file/000_json.robot +++ b/test/functional/cases/411_logging/001_file/000_json.robot diff --git a/test/functional/cases/550_milter_headers.robot b/test/functional/cases/550_milter_headers.robot index 80471b83c..c09659714 100644 --- a/test/functional/cases/550_milter_headers.robot +++ b/test/functional/cases/550_milter_headers.robot @@ -37,3 +37,14 @@ CHECK HEADERS WITHOUT TEST SYMBOL # Check X-Spam-Level header Do Not Expect Added Header X-Spam-Level Expect Removed Header X-Spam-Level + +CHECK HEADERS WITH OVERRIDE SETTINGS + # id_milter_headers_override setting enables only authentication-results and x-spam-level routines + Scan File ${MESSAGE} Settings-Id=id_milter_headers_override + # Test the milter_headers override behavior + # Check that Authentication-Results and X-Spam-Level headers are present (exact values are not important) + Expect Header Is Present Authentication-Results + Expect Header Is Present X-Spam-Level + # Verify other headers are not added since only authentication-results and x-spam-level routines run + Do Not Expect Added Header X-Virus + Do Not Expect Added Header My-Spamd-Bar diff --git a/test/functional/configs/fuzzy-split-servers.conf b/test/functional/configs/fuzzy-split-servers.conf new file mode 100644 index 000000000..e7c3bb922 --- /dev/null +++ b/test/functional/configs/fuzzy-split-servers.conf @@ -0,0 +1,97 @@ +redis { + servers = "{= env.REDIS_ADDR =}:{= env.REDIS_PORT =}"; +} +lua = "{= env.TESTDIR =}/lua/test_coverage.lua"; +options = { + filters = "fuzzy_check"; + pidfile = "{= env.TMPDIR =}/rspamd.pid"; + control_socket = "{= env.TMPDIR =}/rspamd.sock mode=0600"; + url_tld = "{= env.TESTDIR =}/../lua/unit/test_tld.dat"; + dns { + retransmits = 10; + timeout = 2s; + } +} +logging = { + type = "file", + level = "debug" + filename = "{= env.TMPDIR =}/rspamd.log" +} +metric = { + name = "default", + actions = { + reject = 100500, + } + unknown_weight = 1 + symbol { + weight = 10.0; + name = "{= env.FLAG1_SYMBOL =}"; + } + symbol { + weight = -1.0; + name = "{= env.FLAG2_SYMBOL =}"; + } +} + +worker { + type = normal + bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_NORMAL =}"; + count = 1 + task_timeout = 60s; +} + +worker { + type = controller + bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_CONTROLLER =}"; + count = 1 + secure_ip = ["{= env.LOCAL_ADDR =}"]; + stats_path = "{= env.TMPDIR =}/stats.ucl"; +} + +worker { + count = 1; + backend = "{= env.FUZZY_BACKEND =}"; + bind_socket = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}"; + type = "fuzzy"; + hashfile = "{= env.TMPDIR =}/fuzzy.db"; + allow_update = ["{= env.LOCAL_ADDR =}"]; + encrypted_only = {= env.FUZZY_ENCRYPTED_ONLY =}; + keypair { + privkey = "{= env.KEY_PVT1 =}"; + pubkey = "{= env.KEY_PUB1 =}"; + } + dynamic_keys_map = "{= env.TESTDIR =}/configs/maps/fuzzy_keymap.map"; +} + +fuzzy_check { + min_bytes = 100; + timeout = 1s; + retransmits = 10; + + rule { + min_bytes = 0; + min_length = 0; + algorithm = "{= env.FUZZY_ALGORITHM =}"; + read_servers = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}"; + write_servers = "{= env.LOCAL_ADDR =}:{= env.PORT_FUZZY =}"; + symbol = "R_TEST_FUZZY"; + max_score = 10.0; + mime_types = ["application/*"]; + read_only = false; + skip_unknown = true; + skip_hashes = "{= env.TMPDIR =}/skip_hash.map"; + fuzzy_key = {= env.FUZZY_KEY =}; + fuzzy_shingles_key = {= env.FUZZY_SHINGLES_KEY =}; +.include "{= env.FUZZY_INCLUDE =}"; + fuzzy_map = { + R_TEST_FUZZY_DENIED { + max_score = 10.0; + flag = {= env.FLAG1_NUMBER =}; + } + R_TEST_FUZZY_WHITE { + max_score = 1.0; + flag = {= env.FLAG2_NUMBER =}; + } + } + } +} diff --git a/test/functional/configs/merged-override.conf b/test/functional/configs/merged-override.conf index 344e30786..e302e88fc 100644 --- a/test/functional/configs/merged-override.conf +++ b/test/functional/configs/merged-override.conf @@ -254,6 +254,14 @@ multimap { } } + EXTERNAL_MULTIMAP_CDB { + type = "hostname"; + map = { + external = true; + cdb = "{= env.TESTDIR =}/configs/maps/domains.cdb"; + } + } + DYN_MULTIMAP { type = "hostname"; map = "{= env.TESTDIR =}/configs/maps/dynamic_symbols.map"; diff --git a/test/functional/configs/milter_headers.conf b/test/functional/configs/milter_headers.conf index 947bc28dd..502747605 100644 --- a/test/functional/configs/milter_headers.conf +++ b/test/functional/configs/milter_headers.conf @@ -22,3 +22,15 @@ milter_headers { } } + +settings { + id_milter_headers_override { + apply { + plugins { + milter_headers { + routines = [ authentication-results, x-spam-level ]; + } + } + } + } +} diff --git a/test/functional/lib/rspamd.robot b/test/functional/lib/rspamd.robot index 9c30a97db..5d23e3ceb 100644 --- a/test/functional/lib/rspamd.robot +++ b/test/functional/lib/rspamd.robot @@ -120,6 +120,15 @@ Expect Added Header Should Be Equal ${SCAN_RESULT}[milter][add_headers][${header_name}][value] ${header_value} Should Be Equal as Numbers ${SCAN_RESULT}[milter][add_headers][${header_name}][order] ${pos} +Expect Header Is Present + [Arguments] ${header_name} + Dictionary Should Contain Key ${SCAN_RESULT} milter + ... msg=milter block was not present in protocol response + Dictionary Should Contain Key ${SCAN_RESULT}[milter] add_headers + ... msg=add_headers block was not present in protocol response + Dictionary Should Contain Key ${SCAN_RESULT}[milter][add_headers] ${header_name} + ... msg=${header_name} was not added + Expect Email [Arguments] ${email} List Should Contain Value ${SCAN_RESULT}[emails] ${email} diff --git a/test/functional/messages/gargantua.eml b/test/functional/messages/gargantua.eml index c6edc50f3..acb3d367f 100644 --- a/test/functional/messages/gargantua.eml +++ b/test/functional/messages/gargantua.eml @@ -23502,4 +23502,58 @@ X-Real-Type: csv ImZvbyIsImJhciIsImJheiIKIjEiLCIyIiwiMyIK +--XXX +Content-Type: image/heic +Content-Transfer-Encoding: base64 +X-Real-Type: heic + +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 +0YCJBxFcC867msO9wip2vP786vlLeC/fqKwSng== + --XXX-- diff --git a/test/lua/unit/logger.lua b/test/lua/unit/logger.lua index dc0120709..c28d8bb09 100644 --- a/test/lua/unit/logger.lua +++ b/test/lua/unit/logger.lua @@ -3,17 +3,17 @@ context("Logger unit tests", function() local log = require "rspamd_logger" local cases = { - {'string', 'string'}, - {'%1', 'string', 'string'}, - {'%1', '1.1', 1.1}, - {'%1', '1', 1}, - {'%1', 'true', true}, - {'%1', '{[1] = 1, [2] = test}', {1, 'test'}}, - {'%1', '{[1] = 1, [2] = 2.1, [k2] = test}', {1, 2.1, k2='test'}}, - {'%s', 'true', true}, + { 'string', 'string' }, + { '%1', 'string', 'string' }, + { '%1', '1.1', 1.1 }, + { '%1', '1', 1 }, + { '%1', 'true', true }, + { '%1', '{[1] = 1, [2] = test}', { 1, 'test' } }, + { '%1', '{[1] = 1, [2] = 2.1, [k2] = test}', { 1, 2.1, k2 = 'test' } }, + { '%s', 'true', true }, } - for _,c in ipairs(cases) do + for _, c in ipairs(cases) do local s if c[3] then s = log.slog(c[1], c[3]) @@ -21,7 +21,82 @@ context("Logger unit tests", function() s = log.slog(c[1]) end assert_equal(s, c[2], string.format("'%s' doesn't match with '%s'", - c[2], s)) + c[2], s)) + end + end) + + test("Logger graceful error handling", function() + local log = require "rspamd_logger" + + -- Test missing arguments + local missing_arg_cases = { + { '%1', '<MISSING ARGUMENT>' }, + { '%0', '<MISSING ARGUMENT>' }, -- %0 is invalid since Lua args are 1-indexed + { '%2', '<MISSING ARGUMENT>', 'arg1' }, + { '%1 %2', 'arg1 <MISSING ARGUMENT>', 'arg1' }, + { 'prefix %1 %3 suffix', 'prefix arg1 <MISSING ARGUMENT> suffix', 'arg1' }, + } + + for _, c in ipairs(missing_arg_cases) do + local s + if c[3] then + s = log.slog(c[1], c[3]) + else + s = log.slog(c[1]) + end + assert_equal(s, c[2], string.format("Missing arg test: '%s' doesn't match with '%s'", + c[2], s)) + end + + -- Test extra arguments + local extra_arg_cases = { + { '%1', 'arg1 <EXTRA 1 ARGUMENTS>', 'arg1', 'extra1' }, + { '%1', 'arg1 <EXTRA 2 ARGUMENTS>', 'arg1', 'extra1', 'extra2' }, + { '%s', 'arg1 <EXTRA 1 ARGUMENTS>', 'arg1', 'extra1' }, + { 'prefix %1 suffix', 'prefix arg1 suffix <EXTRA 1 ARGUMENTS>', 'arg1', 'extra1' }, + } + + for _, c in ipairs(extra_arg_cases) do + local s + if c[4] and c[5] then + s = log.slog(c[1], c[3], c[4], c[5]) + elseif c[4] then + s = log.slog(c[1], c[3], c[4]) + else + s = log.slog(c[1], c[3]) + end + assert_equal(s, c[2], string.format("Extra arg test: '%s' doesn't match with '%s'", + c[2], s)) + end + + -- Test literal percent sequences (should pass through as-is) + local literal_cases = { + { '%-1', '%-1' }, + { '%abc', '%abc' }, -- Should pass through as literal since it's not a valid number + { '%', '%' }, -- Single percent should pass through + } + + for _, c in ipairs(literal_cases) do + local s = log.slog(c[1]) + assert_equal(s, c[2], string.format("Literal test: '%s' doesn't match with '%s'", + c[2], s)) + end + + -- Test mixed scenarios + local mixed_cases = { + { '%1 %3', 'arg1 <MISSING ARGUMENT> <EXTRA 1 ARGUMENTS>', 'arg1', 'extra1' }, + { '%2 %4', 'extra1 <MISSING ARGUMENT> <EXTRA 1 ARGUMENTS>', 'arg1', 'extra1' }, + } + + for _, c in ipairs(mixed_cases) do + local s + if c[4] then + s = log.slog(c[1], c[3], c[4]) + else + s = log.slog(c[1], c[3]) + end + assert_equal(s, c[2], string.format("Mixed test: '%s' doesn't match with '%s'", + c[2], s)) end end) end)
\ No newline at end of file diff --git a/test/lua/unit/rspamd_resolver.lua b/test/lua/unit/rspamd_resolver.lua index e987ff00b..2fdec2c4b 100644 --- a/test/lua/unit/rspamd_resolver.lua +++ b/test/lua/unit/rspamd_resolver.lua @@ -6,24 +6,58 @@ context("Check punycoding UTF-8 URL", function() local resolver = rspamd_resolver.init(rspamd_util.create_event_base(), rspamd_config) - local cases = { - -- https://unicode.org/reports/tr46/#Deviations - ['faß.de'] = 'fass.de', -- IDNA2008 result: xn--fa-hia.de - ['βόλος.com'] = 'xn--nxasmq6b.com', -- IDNA2008 result: xn--nxasmm1c.com - ['نامهای.com'] = 'xn--mgba3gch31f.com', -- IDNA2008 result: xn--mgba3gch31f060k.com - ['ශ්රී.com'] = 'xn--10cl1a0b.com', -- IDNA2008 result: xn--10cl1a0b660p.com - - -- https://unicode.org/reports/tr46/#Table_Example_Processing - ['日本語。JP'] = 'xn--wgv71a119e.jp', -- Fullwidth characters are remapped, including 。 - --['u¨.com'] = 'xn--tda.com', -- Normalize changes u + umlaut to ü - ['☕.us'] = 'xn--53h.us', -- Post-Unicode 3.2 characters are allowed - - -- Other + -- Helper function to detect IDNA behavior by testing a known conversion + local function detect_idna_behavior() + -- Use faß.de as a test case - different results in IDNA2003 vs IDNA2008 + local test_result = resolver:idna_convert_utf8('faß.de') + if test_result == 'fass.de' then + return 'transitional' -- IDNA2003/transitional behavior + elseif test_result == 'xn--fa-hia.de' then + return 'nontransitional' -- IDNA2008/nontransitional behavior + else + return 'unknown' + end + end + + local idna_behavior = detect_idna_behavior() + + -- Define test cases with both expected results + local cases_transitional = { + -- IDNA2003/transitional results (ICU < 76 default) + ['faß.de'] = 'fass.de', + ['βόλος.com'] = 'xn--nxasmq6b.com', + ['نامهای.com'] = 'xn--mgba3gch31f.com', + ['ශ්රී.com'] = 'xn--10cl1a0b.com', + ['日本語。JP'] = 'xn--wgv71a119e.jp', + ['☕.us'] = 'xn--53h.us', + ['example.рф'] = 'example.xn--p1ai', + } + + local cases_nontransitional = { + -- IDNA2008/nontransitional results (ICU >= 76 default) + ['faß.de'] = 'xn--fa-hia.de', + ['βόλος.com'] = 'xn--nxasmm1c.com', + ['نامهای.com'] = 'xn--mgba3gch31f060k.com', + ['ශ්රී.com'] = 'xn--10cl1a0b660p.com', + ['日本語。JP'] = 'xn--wgv71a119e.jp', + ['☕.us'] = 'xn--53h.us', ['example.рф'] = 'example.xn--p1ai', } + -- Choose appropriate test cases based on detected behavior + local cases + if idna_behavior == 'transitional' then + cases = cases_transitional + print("Detected IDNA transitional behavior (ICU < 76 or configured for IDNA2003)") + elseif idna_behavior == 'nontransitional' then + cases = cases_nontransitional + print("Detected IDNA nontransitional behavior (ICU >= 76 default)") + else + error("Could not detect IDNA behavior - unexpected result for test case") + end + for k, v in pairs(cases) do - test(string.format("punycode %s -> %s", k, v), function() + test(string.format("punycode %s -> %s (%s)", k, v, idna_behavior), function() local res = resolver:idna_convert_utf8(k) assert_equal(res, v) end) diff --git a/test/rspamd_cryptobox_test.c b/test/rspamd_cryptobox_test.c index 03b833404..82225d071 100644 --- a/test/rspamd_cryptobox_test.c +++ b/test/rspamd_cryptobox_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2024 Vsevolod Stakhov + * Copyright 2025 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -153,7 +153,6 @@ create_constrained_split(struct rspamd_cryptobox_segment *seg, int mseg, void rspamd_cryptobox_test_func(void) { - void *map; unsigned char *begin, *end; rspamd_nm_t key; rspamd_nonce_t nonce; @@ -161,9 +160,8 @@ void rspamd_cryptobox_test_func(void) struct rspamd_cryptobox_segment *seg; double t1, t2; int i, cnt, ms; - gboolean checked_openssl = FALSE; - map = create_mapping(mapping_size, &begin, &end); + create_mapping(mapping_size, &begin, &end); ottery_rand_bytes(key, sizeof(key)); ottery_rand_bytes(nonce, sizeof(nonce)); diff --git a/test/rspamd_cxx_unit_cryptobox.hxx b/test/rspamd_cxx_unit_cryptobox.hxx index 7d9c76b4e..4624e2f93 100644 --- a/test/rspamd_cxx_unit_cryptobox.hxx +++ b/test/rspamd_cxx_unit_cryptobox.hxx @@ -1,5 +1,5 @@ /* - * Copyright 2024 Vsevolod Stakhov + * Copyright 2025 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -245,6 +245,225 @@ TEST_SUITE("rspamd_cryptobox") auto out_arr = std::vector(std::begin(out), std::end(out)); CHECK(out_arr == expected_arr); } + + // Test vectors for XChaCha20-Poly1305 compatibility with Go implementation + // These test cases use the same inputs as the Go version to verify compatibility + + TEST_CASE("rspamd xchacha20poly1305 compatibility all_zeros_64_bytes") + { + // Test case: all_zeros_64_bytes + // Key: 32 zero bytes + // Nonce: 24 zero bytes + // Plaintext: 64 zero bytes + + rspamd_nm_t key; + memset(key, 0, sizeof(key)); + + rspamd_nonce_t nonce; + memset(nonce, 0, sizeof(nonce)); + + unsigned char plaintext[64]; + memset(plaintext, 0, sizeof(plaintext)); + + // Expected values from C implementation + unsigned char expected_cipher[64] = { + 0x78, 0x9e, 0x96, 0x89, 0xe5, 0x20, 0x8d, 0x7f, 0xd9, 0xe1, 0xf3, 0xc5, 0xb5, 0x34, 0x1f, 0x48, + 0xef, 0x18, 0xa1, 0x3e, 0x41, 0x89, 0x98, 0xad, 0xda, 0xdd, 0x97, 0xa3, 0x69, 0x3a, 0x98, 0x7f, + 0x8e, 0x82, 0xec, 0xd5, 0xc1, 0x43, 0x3b, 0xfe, 0xd1, 0xaf, 0x49, 0x75, 0x0c, 0x0f, 0x1f, 0xf2, + 0x9c, 0x41, 0x74, 0xa0, 0x5b, 0x11, 0x9a, 0xa3, 0xa9, 0xe8, 0x33, 0x38, 0x12, 0xe0, 0xc0, 0xfe}; + + rspamd_mac_t expected_mac = { + 0x9c, 0x22, 0xbd, 0x8b, 0x7d, 0x68, 0x00, 0xca, 0x3f, 0x9d, 0xf1, 0xc0, 0x3e, 0x31, 0x3e, 0x68}; + + // Test encryption using Rspamd's nm (shared key) encryption + unsigned char ciphertext[64]; + memcpy(ciphertext, plaintext, sizeof(plaintext)); + + rspamd_mac_t mac; + + rspamd_cryptobox_encrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + + CHECK(memcmp(ciphertext, expected_cipher, sizeof(expected_cipher)) == 0); + CHECK(memcmp(mac, expected_mac, sizeof(expected_mac)) == 0); + + // Test decryption + gboolean decrypt_ok = rspamd_cryptobox_decrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + CHECK(decrypt_ok == TRUE); + CHECK(memcmp(ciphertext, plaintext, sizeof(plaintext)) == 0); + } + + TEST_CASE("rspamd xchacha20poly1305 compatibility all_zeros_128_bytes") + { + // Test case: all_zeros_128_bytes + // Key: 32 zero bytes + // Nonce: 24 zero bytes + // Plaintext: 128 zero bytes + + rspamd_nm_t key; + memset(key, 0, sizeof(key)); + + rspamd_nonce_t nonce; + memset(nonce, 0, sizeof(nonce)); + + unsigned char plaintext[128]; + memset(plaintext, 0, sizeof(plaintext)); + + unsigned char expected_cipher[128] = { + 0x78, 0x9e, 0x96, 0x89, 0xe5, 0x20, 0x8d, 0x7f, 0xd9, 0xe1, 0xf3, 0xc5, 0xb5, 0x34, 0x1f, 0x48, + 0xef, 0x18, 0xa1, 0x3e, 0x41, 0x89, 0x98, 0xad, 0xda, 0xdd, 0x97, 0xa3, 0x69, 0x3a, 0x98, 0x7f, + 0x8e, 0x82, 0xec, 0xd5, 0xc1, 0x43, 0x3b, 0xfe, 0xd1, 0xaf, 0x49, 0x75, 0x0c, 0x0f, 0x1f, 0xf2, + 0x9c, 0x41, 0x74, 0xa0, 0x5b, 0x11, 0x9a, 0xa3, 0xa9, 0xe8, 0x33, 0x38, 0x12, 0xe0, 0xc0, 0xfe, + 0xa4, 0x9e, 0x1e, 0xe0, 0x13, 0x4a, 0x70, 0xa9, 0xd4, 0x9c, 0x24, 0xe0, 0xcb, 0xd8, 0xfc, 0x3b, + 0xa2, 0x7e, 0x97, 0xc3, 0x32, 0x2a, 0xd4, 0x87, 0xf7, 0x78, 0xf8, 0xdc, 0x6a, 0x12, 0x2f, 0xa5, + 0x9c, 0xbe, 0x33, 0xe7, 0x78, 0xea, 0x2e, 0x50, 0xbb, 0x59, 0x09, 0xc9, 0x97, 0x1c, 0x4f, 0xec, + 0x2f, 0x93, 0x52, 0x3f, 0x77, 0x89, 0x2d, 0x17, 0xca, 0xa5, 0x81, 0x67, 0xde, 0xc4, 0xd6, 0xc7}; + + rspamd_mac_t expected_mac = { + 0xcf, 0xe1, 0x4a, 0xc3, 0x39, 0x35, 0xd3, 0x63, 0x1a, 0x06, 0xbf, 0x55, 0x88, 0xf4, 0x12, 0xfa}; + + unsigned char ciphertext[128]; + memcpy(ciphertext, plaintext, sizeof(plaintext)); + + rspamd_mac_t mac; + rspamd_cryptobox_encrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + + CHECK(memcmp(ciphertext, expected_cipher, sizeof(expected_cipher)) == 0); + CHECK(memcmp(mac, expected_mac, sizeof(expected_mac)) == 0); + + // Test decryption + gboolean decrypt_ok = rspamd_cryptobox_decrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + CHECK(decrypt_ok == TRUE); + CHECK(memcmp(ciphertext, plaintext, sizeof(plaintext)) == 0); + } + + TEST_CASE("rspamd xchacha20poly1305 compatibility test_pattern_64_bytes") + { + // Test case: test_pattern_64_bytes + // Key: 0x01 repeated 32 times + // Nonce: 0x01, 0x02, 0x03, ... 0x18 (24 bytes) + // Plaintext: 0x00, 0x01, 0x02, ... 0x41 (66 bytes) + + rspamd_nm_t key; + memset(key, 0x01, sizeof(key)); + + rspamd_nonce_t nonce = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, + 0x21, 0x22, 0x23, 0x24}; + + unsigned char plaintext[66]; + for (int i = 0; i < 66; i++) { + plaintext[i] = i; + } + + // Expected values from C implementation + unsigned char expected_cipher[66] = { + 0xe6, 0x0e, 0xf7, 0x6d, 0x7f, 0x04, 0x37, 0x81, 0x9f, 0x60, 0x03, 0x28, 0x60, 0xb1, 0x2b, 0xaa, + 0xae, 0x2b, 0x13, 0xef, 0x6d, 0xd3, 0x18, 0xf1, 0x3b, 0xc6, 0x06, 0xfb, 0x65, 0x9a, 0x53, 0x3b, + 0x23, 0xe6, 0x99, 0x0c, 0x65, 0x2f, 0xbf, 0x56, 0xcb, 0x7c, 0x18, 0x53, 0xa8, 0xbc, 0x11, 0xc4, + 0x0b, 0x35, 0xc9, 0x40, 0x9a, 0xc2, 0xe1, 0x7f, 0x1a, 0x72, 0xaa, 0xb3, 0x8b, 0x4e, 0x21, 0x32, + 0x87, 0xf7}; + + rspamd_mac_t expected_mac = { + 0xf2, 0xa7, 0xbd, 0xae, 0x53, 0x68, 0xfe, 0xd8, 0x4c, 0x92, 0xe8, 0x52, 0x35, 0x4d, 0x78, 0x7c}; + + unsigned char ciphertext[66]; + memcpy(ciphertext, plaintext, sizeof(plaintext)); + + rspamd_mac_t mac; + + rspamd_cryptobox_encrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + + CHECK(memcmp(ciphertext, expected_cipher, sizeof(expected_cipher)) == 0); + CHECK(memcmp(mac, expected_mac, sizeof(expected_mac)) == 0); + + // Test decryption + gboolean decrypt_ok = rspamd_cryptobox_decrypt_nm_inplace(ciphertext, sizeof(ciphertext), nonce, key, mac); + CHECK(decrypt_ok == TRUE); + CHECK(memcmp(ciphertext, plaintext, sizeof(plaintext)) == 0); + } + + TEST_CASE("rspamd mac key derivation compatibility all_zeros") + { + // Test MAC key derivation process + // Key: 32 zero bytes + // Nonce: 24 zero bytes + + rspamd_nm_t key; + memset(key, 0, sizeof(key)); + + rspamd_nonce_t nonce; + memset(nonce, 0, sizeof(nonce)); + + // Expected values from C implementation + unsigned char expected_subkey[64] = { + 0xbc, 0xd0, 0x2a, 0x18, 0xbf, 0x3f, 0x01, 0xd1, 0x92, 0x92, 0xde, 0x30, 0xa7, 0xa8, 0xfd, 0xac, + 0xa4, 0xb6, 0x5e, 0x50, 0xa6, 0x00, 0x2c, 0xc7, 0x2c, 0xd6, 0xd2, 0xf7, 0xc9, 0x1a, 0xc3, 0xd5, + 0x72, 0x8f, 0x83, 0xe0, 0xaa, 0xd2, 0xbf, 0xcf, 0x9a, 0xbd, 0x2d, 0x2d, 0xb5, 0x8f, 0xae, 0xdd, + 0x65, 0x01, 0x5d, 0xd8, 0x3f, 0xc0, 0x9b, 0x13, 0x1e, 0x27, 0x10, 0x43, 0x01, 0x9e, 0x8e, 0x0f}; + + unsigned char expected_mac_key[32] = { + 0xbc, 0xd0, 0x2a, 0x18, 0xbf, 0x3f, 0x01, 0xd1, 0x92, 0x92, 0xde, 0x30, 0xa7, 0xa8, 0xfd, 0xac, + 0xa4, 0xb6, 0x5e, 0x50, 0xa6, 0x00, 0x2c, 0xc7, 0x2c, 0xd6, 0xd2, 0xf7, 0xc9, 0x1a, 0xc3, 0xd5}; + + // Generate subkey using XChaCha20 (first 64 bytes of keystream) + // This simulates the MAC key derivation process used in secretbox + unsigned char subkey[64]; + memset(subkey, 0, sizeof(subkey)); + + // Use libsodium's ChaCha20 directly to generate the subkey + // This matches what happens inside the secretbox implementation + crypto_stream_xchacha20(subkey, sizeof(subkey), nonce, key); + + // MAC key is first 32 bytes of subkey + unsigned char mac_key[32]; + memcpy(mac_key, subkey, 32); + + CHECK(memcmp(subkey, expected_subkey, sizeof(expected_subkey)) == 0); + CHECK(memcmp(mac_key, expected_mac_key, sizeof(expected_mac_key)) == 0); + } + + TEST_CASE("rspamd mac key derivation compatibility test_pattern") + { + // Test MAC key derivation process + // Key: 0x01 repeated 32 times + // Nonce: 0x01, 0x02, 0x03, ... 0x18 (24 bytes) + + rspamd_nm_t key; + memset(key, 0x01, sizeof(key)); + + rspamd_nonce_t nonce = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, + 0x21, 0x22, 0x23, 0x24}; + + // Expected values from C implementation + unsigned char expected_subkey[64] = { + 0x47, 0xa6, 0xe3, 0xb5, 0x0f, 0xd4, 0x7f, 0x08, 0xb5, 0x35, 0x80, 0xfc, 0x93, 0x66, 0x1a, 0x7f, + 0x9c, 0xf5, 0x8c, 0x93, 0xae, 0x4e, 0x3f, 0xcf, 0x86, 0xb7, 0xdf, 0x34, 0x48, 0x73, 0x33, 0xdb, + 0x71, 0x31, 0x0f, 0xe1, 0xcc, 0xd9, 0x0c, 0x0a, 0x1a, 0x19, 0x54, 0x30, 0xdf, 0xe3, 0xda, 0xee, + 0x70, 0x29, 0xd9, 0xae, 0xf6, 0x4d, 0x78, 0xe3, 0xe8, 0x43, 0x98, 0xea, 0xaa, 0xd8, 0x85, 0x79}; + + unsigned char expected_mac_key[32] = { + 0x47, 0xa6, 0xe3, 0xb5, 0x0f, 0xd4, 0x7f, 0x08, 0xb5, 0x35, 0x80, 0xfc, 0x93, 0x66, 0x1a, 0x7f, + 0x9c, 0xf5, 0x8c, 0x93, 0xae, 0x4e, 0x3f, 0xcf, 0x86, 0xb7, 0xdf, 0x34, 0x48, 0x73, 0x33, 0xdb}; + + // Generate subkey using XChaCha20 (first 64 bytes of keystream) + // This simulates the MAC key derivation process used in secretbox + unsigned char subkey[64]; + memset(subkey, 0, sizeof(subkey)); + + // Use libsodium's ChaCha20 directly to generate the subkey + // This matches what happens inside the secretbox implementation + crypto_stream_xchacha20(subkey, sizeof(subkey), nonce, key); + + // MAC key is first 32 bytes of subkey + unsigned char mac_key[32]; + memcpy(mac_key, subkey, 32); + + CHECK(memcmp(subkey, expected_subkey, sizeof(expected_subkey)) == 0); + CHECK(memcmp(mac_key, expected_mac_key, sizeof(expected_mac_key)) == 0); + } } #endif diff --git a/test/rspamd_dns_test.c b/test/rspamd_dns_test.c index d041351df..ccdc47820 100644 --- a/test/rspamd_dns_test.c +++ b/test/rspamd_dns_test.c @@ -1,4 +1,20 @@ +/* + * Copyright 2025 Vsevolod Stakhov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #include "config.h" #include "tests.h" #include "dns.h" @@ -51,6 +67,9 @@ test_dns_cb(struct rdns_reply *reply, gpointer arg) case RDNS_REQUEST_MX: msg_debug("got mx %s:%d", cur->content.mx.name, cur->content.mx.priority); break; + default: + msg_debug("got unknown type %d", cur->type); + break; } cur = cur->next; } diff --git a/test/rspamd_shingles_test.c b/test/rspamd_shingles_test.c index d1a10de84..5b88f4b2d 100644 --- a/test/rspamd_shingles_test.c +++ b/test/rspamd_shingles_test.c @@ -17,6 +17,7 @@ #include "rspamd.h" #include "shingles.h" #include "ottery.h" +#include "libserver/word.h" #include <math.h> static const char * @@ -52,63 +53,76 @@ generate_random_string(char *begin, size_t len) } } -static GArray * +static rspamd_words_t * generate_fuzzy_words(gsize cnt, gsize max_len) { - GArray *res; + rspamd_words_t *res; gsize i, wlen; - rspamd_ftok_t w; + rspamd_word_t word; char *t; - res = g_array_sized_new(FALSE, FALSE, sizeof(rspamd_ftok_t), cnt); + res = g_malloc(sizeof(*res)); + kv_init(*res); for (i = 0; i < cnt; i++) { wlen = ottery_rand_range(max_len) + 1; /* wlen = max_len; */ - w.len = wlen; t = g_malloc(wlen); generate_random_string(t, wlen); - w.begin = t; - g_array_append_val(res, w); + + memset(&word, 0, sizeof(word)); + word.stemmed.begin = t; + word.stemmed.len = wlen; + word.original.begin = t; + word.original.len = wlen; + word.flags = 0; /* No flags set, so it won't be skipped */ + + kv_push(rspamd_word_t, *res, word); } return res; } static void -permute_vector(GArray *in, double prob) +permute_vector(rspamd_words_t *in, double prob) { gsize i, total = 0; - rspamd_ftok_t *w; + rspamd_word_t *w; - for (i = 0; i < in->len; i++) { + for (i = 0; i < kv_size(*in); i++) { if (ottery_rand_unsigned() <= G_MAXUINT * prob) { - w = &g_array_index(in, rspamd_ftok_t, i); - generate_random_string((char *) w->begin, w->len); + w = &kv_A(*in, i); + generate_random_string((char *) w->stemmed.begin, w->stemmed.len); + /* Also update original since they point to same memory */ + w->original.begin = w->stemmed.begin; + w->original.len = w->stemmed.len; total++; } } - msg_debug("generated %z permutations of %ud words", total, in->len); + msg_debug("generated %z permutations of %ud words", total, (unsigned int) kv_size(*in)); } static void -free_fuzzy_words(GArray *ar) +free_fuzzy_words(rspamd_words_t *ar) { gsize i; - rspamd_ftok_t *w; + rspamd_word_t *w; - for (i = 0; i < ar->len; i++) { - w = &g_array_index(ar, rspamd_ftok_t, i); - g_free((gpointer) w->begin); + for (i = 0; i < kv_size(*ar); i++) { + w = &kv_A(*ar, i); + g_free((gpointer) w->stemmed.begin); } + + kv_destroy(*ar); + g_free(ar); } static void test_case(gsize cnt, gsize max_len, double perm_factor, enum rspamd_shingle_alg alg) { - GArray *input; + rspamd_words_t *input; struct rspamd_shingle *sgl, *sgl_permuted; double res; unsigned char key[16]; @@ -281,51 +295,59 @@ void rspamd_shingles_test_func(void) enum rspamd_shingle_alg alg = RSPAMD_SHINGLES_OLD; struct rspamd_shingle *sgl; unsigned char key[16]; - GArray *input; - rspamd_ftok_t tok; + rspamd_words_t input; + rspamd_word_t word; int i; memset(key, 0, sizeof(key)); - input = g_array_sized_new(FALSE, FALSE, sizeof(rspamd_ftok_t), 5); + kv_init(input); for (i = 0; i < 5; i++) { char *b = g_alloca(8); memset(b, 0, 8); memcpy(b + 1, "test", 4); b[0] = 'a' + i; - tok.begin = b; - tok.len = 5 + ((i + 1) % 4); - g_array_append_val(input, tok); + + memset(&word, 0, sizeof(word)); + word.stemmed.begin = b; + word.stemmed.len = 5 + ((i + 1) % 4); + word.original.begin = b; + word.original.len = word.stemmed.len; + word.flags = 0; /* No flags set, so it won't be skipped */ + + kv_push(rspamd_word_t, input, word); } - sgl = rspamd_shingles_from_text(input, key, NULL, + sgl = rspamd_shingles_from_text(&input, key, NULL, rspamd_shingles_default_filter, NULL, RSPAMD_SHINGLES_OLD); for (i = 0; i < RSPAMD_SHINGLE_SIZE; i++) { g_assert(sgl->hashes[i] == expected_old[i]); } g_free(sgl); - sgl = rspamd_shingles_from_text(input, key, NULL, + sgl = rspamd_shingles_from_text(&input, key, NULL, rspamd_shingles_default_filter, NULL, RSPAMD_SHINGLES_XXHASH); for (i = 0; i < RSPAMD_SHINGLE_SIZE; i++) { g_assert(sgl->hashes[i] == expected_xxhash[i]); } g_free(sgl); - sgl = rspamd_shingles_from_text(input, key, NULL, + sgl = rspamd_shingles_from_text(&input, key, NULL, rspamd_shingles_default_filter, NULL, RSPAMD_SHINGLES_MUMHASH); for (i = 0; i < RSPAMD_SHINGLE_SIZE; i++) { g_assert(sgl->hashes[i] == expected_mumhash[i]); } g_free(sgl); - sgl = rspamd_shingles_from_text(input, key, NULL, + sgl = rspamd_shingles_from_text(&input, key, NULL, rspamd_shingles_default_filter, NULL, RSPAMD_SHINGLES_FAST); for (i = 0; i < RSPAMD_SHINGLE_SIZE; i++) { g_assert(sgl->hashes[i] == expected_fasthash[i]); } g_free(sgl); + kv_destroy(input); + for (alg = RSPAMD_SHINGLES_OLD; alg <= RSPAMD_SHINGLES_FAST; alg++) { test_case(200, 10, 0.1, alg); test_case(500, 20, 0.01, alg); |