CVE-2024-29882: Insufficient Information (NVD-CWE-noinfo)
value: SRS is a simple, high-efficiency, real-time video server. SRS's `/api/v1/vhosts/vid-<id>?callback=<payload>` endpoint didn't filter the callback function name which led to injecting malicious javascript payloads and executing XSS ( Cross-Site Scripting). This vulnerability is fixed in 5.0.210 and 6.0.121.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-03-28T14:15Z
Repository: https://github.com/ossrs/srs
Language: C++
Files changed: trunk/configure, trunk/doc/CHANGELOG.md, trunk/src/core/srs_core_version5.hpp, trunk/src/core/srs_core_version6.hpp, trunk/src/kernel/srs_kernel_error.hpp, trunk/src/protocol/srs_protocol_http_conn.cpp, trunk/src/utest/srs_utest_protocol3.cpp, trunk/src/utest/srs_utest_protocol3.hpp
Commit date: 2024-03-26 19:30:52 +0800
Commit message: Merge pull request from GHSA-gv9r-qcjc-5hj7

* Filter JSONP callback function name. v5.0.210,v6.0.121

* Add utest.

* Refine utest
Diff: diff --git a/trunk/configure b/trunk/configure
index ca4f28a11..355d48432 100755
--- a/trunk/configure
+++ b/trunk/configure
@@ -462,11 +462,11 @@ fi
 # utest, the unit-test cases of srs, base on gtest1.6
 if [[ $SRS_UTEST == YES ]]; then
     MODULE_FILES=("srs_utest" "srs_utest_amf0" "srs_utest_kernel" "srs_utest_core"
         "srs_utest_config" "srs_utest_rtmp" "srs_utest_http" "srs_utest_avc" "srs_utest_reload"
         "srs_utest_mp4" "srs_utest_service" "srs_utest_app" "srs_utest_rtc" "srs_utest_config2"
-        "srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2")
+        "srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3")
     if [[ $SRS_SRT == YES ]]; then
         MODULE_FILES+=("srs_utest_srt")
     fi
     if [[ $SRS_GB28181 == YES ]]; then
         MODULE_FILES+=("srs_utest_gb28181")
diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md
index b3d0d3566..116255185 100644
--- a/trunk/doc/CHANGELOG.md
+++ b/trunk/doc/CHANGELOG.md
@@ -5,10 +5,11 @@
 The changelog for SRS.
 
 <a name="v6-changes"></a>
 
 ## SRS 6.0 Changelog
+* v6.0, 2024-03-26, Filter JSONP callback function name. v6.0.121
 * v6.0, 2024-03-26, Merge [#3995](https://github.com/ossrs/srs/pull/3995): Build: Refine workflow for cygwin and remove scorecard. v6.0.120 (#3995)
 * v6.0, 2024-03-26, Merge [#4005](https://github.com/ossrs/srs/pull/4005): Build: Fix module failed for  main_ingest_hls and mp4_parser. v6.0.119 (#4005)
 * v6.0, 2024-03-24, Merge [#3989](https://github.com/ossrs/srs/pull/3989): ST: Research adds examples that demos pthread and helloworld. v6.0.118 (#3989)
 * v6.0, 2024-03-19, Merge [#3958](https://github.com/ossrs/srs/pull/3958): Add a TCP proxy for debugging. v6.0.117 (#3958)
 * v6.0, 2024-03-20, Merge [#3964](https://github.com/ossrs/srs/pull/3964): WebRTC: Add support for A/V only WHEP/WHEP player. v6.0.116 (#3964)
@@ -129,10 +130,11 @@ The changelog for SRS.
 * v6.0, 2022-11-22, Init SRS 6. v6.0.0
 
 <a name="v5-changes"></a>
 
 ## SRS 5.0 Changelog
+* v5.0, 2024-03-26, Filter JSONP callback function name. v5.0.210
 * v5.0, 2024-03-19, Merge [#3990](https://github.com/ossrs/srs/pull/3990): System: Disable feature that obtains versions and check features status. v5.0.209 (#3990)
 * v5.0, 2024-02-06, Merge [#3920](https://github.com/ossrs/srs/pull/3920): WHIP: Fix bug for converting WHIP to RTMP/HLS. v5.0.208 (#3920)
 * v5.0, 2024-02-05, Merge [#3925](https://github.com/ossrs/srs/pull/3925): RTC: Fix video and audio track pt_ is not change in player before publisher. v5.0.207 (#3925)
 * v5.0, 2024-02-05, Merge [#3923](https://github.com/ossrs/srs/pull/3923): Configure: print enabled/disable sanitizer. v5.0.206 (#3923)
 * v5.0, 2023-12-30, Merge [#3916](https://github.com/ossrs/srs/pull/3916): Enhancing the compatibility of options.sh. v5.0.204 (#3916)
diff --git a/trunk/src/core/srs_core_version5.hpp b/trunk/src/core/srs_core_version5.hpp
index 957b7e7bc..bbd2af29c 100644
--- a/trunk/src/core/srs_core_version5.hpp
+++ b/trunk/src/core/srs_core_version5.hpp
@@ -7,8 +7,8 @@
 #ifndef SRS_CORE_VERSION5_HPP
 #define SRS_CORE_VERSION5_HPP
 
 #define VERSION_MAJOR       5
 #define VERSION_MINOR       0
-#define VERSION_REVISION    209
+#define VERSION_REVISION    210
 
 #endif
diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp
index 7566b8cf7..e19b872bf 100644
--- a/trunk/src/core/srs_core_version6.hpp
+++ b/trunk/src/core/srs_core_version6.hpp
@@ -7,8 +7,8 @@
 #ifndef SRS_CORE_VERSION6_HPP
 #define SRS_CORE_VERSION6_HPP
 
 #define VERSION_MAJOR       6
 #define VERSION_MINOR       0
-#define VERSION_REVISION    120
+#define VERSION_REVISION    121
 
 #endif
diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp
index 85329e166..f24895b46 100644
--- a/trunk/src/kernel/srs_kernel_error.hpp
+++ b/trunk/src/kernel/srs_kernel_error.hpp
@@ -330,11 +330,12 @@
     XX(ERROR_HEVC_NALU_UEV                 , 4054, "HevcNaluUev", "Failed to read UEV for HEVC NALU") \
     XX(ERROR_HEVC_NALU_SEV                 , 4055, "HevcNaluSev", "Failed to read SEV for HEVC NALU") \
     XX(ERROR_STREAM_CASTER_HEVC_VPS        , 4054, "CasterTsHevcVps", "Invalid ts HEVC VPS for stream caster") \
     XX(ERROR_STREAM_CASTER_HEVC_SPS        , 4055, "CasterTsHevcSps", "Invalid ts HEVC SPS for stream caster") \
     XX(ERROR_STREAM_CASTER_HEVC_PPS        , 4056, "CasterTsHevcPps", "Invalid ts HEVC PPS for stream caster") \
-    XX(ERROR_STREAM_CASTER_HEVC_FORMAT     , 4057, "CasterTsHevcFormat", "Invalid ts HEVC Format for stream caster")
+    XX(ERROR_STREAM_CASTER_HEVC_FORMAT     , 4057, "CasterTsHevcFormat", "Invalid ts HEVC Format for stream caster") \
+    XX(ERROR_HTTP_JSONP                    , 4058, "HttpJsonp", "Invalid callback for JSONP")
 
 
 /**************************************************/
 /* RTC protocol error. */
 #define SRS_ERRNO_MAP_RTC(XX) \
diff --git a/trunk/src/protocol/srs_protocol_http_conn.cpp b/trunk/src/protocol/srs_protocol_http_conn.cpp
index 7b91b8afb..3f7651392 100644
--- a/trunk/src/protocol/srs_protocol_http_conn.cpp
+++ b/trunk/src/protocol/srs_protocol_http_conn.cpp
@@ -330,10 +330,24 @@ void SrsHttpMessage::set_header(SrsHttpHeader* header, bool keep_alive)
             _body->close();
         }
     }
 }
 
+// For callback function name, only allow [a-zA-Z0-9_-.] characters.
+bool srs_is_valid_jsonp_callback(std::string callback)
+{
+    for (int i = 0; i < (int)callback.length(); i++) {
+        char ch = callback.at(i);
+        bool is_alpha_beta = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
+        bool is_number = (ch >= '0' && ch <= '9');
+        if (!is_alpha_beta && !is_number && ch != '.' && ch != '_' && ch != '-') {
+            return false;
+        }
+    }
+    return true;
+}
+
 srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp)
 {
     srs_error_t err = srs_success;
     
     _url = url;
@@ -371,16 +385,20 @@ srs_error_t SrsHttpMessage::set_url(string url, bool allow_jsonp)
     // parse query string.
     srs_parse_query_string(_uri->get_query(), _query);
     
     // parse jsonp request message.
     if (allow_jsonp) {
-        if (!query_get("callback").empty()) {
-            jsonp = true;
-        }
+        string callback= query_get("callback");
+        jsonp = !callback.empty();
+
         if (jsonp) {
             jsonp_method = query_get("method");
         }
+
+        if (!srs_is_valid_jsonp_callback(callback)) {
+            return srs_error_new(ERROR_HTTP_JSONP, "invalid callback=%s", callback.c_str());
+        }
     }
     
     return err;
 }
 
diff --git a/trunk/src/utest/srs_utest_protocol3.cpp b/trunk/src/utest/srs_utest_protocol3.cpp
new file mode 100644
index 000000000..a86e1aa9f
--- /dev/null
+++ b/trunk/src/utest/srs_utest_protocol3.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright (c) 2013-2024 The SRS Authors
+//
+// SPDX-License-Identifier: MIT
+//
+#include <srs_utest_protocol3.hpp>
+
+using namespace std;
+
+#include <srs_kernel_error.hpp>
+#include <srs_core_autofree.hpp>
+#include <srs_protocol_utility.hpp>
+#include <srs_protocol_rtmp_msg_array.hpp>
+#include <srs_protocol_rtmp_stack.hpp>
+#include <srs_kernel_utility.hpp>
+#include <srs_app_st.hpp>
+#include <srs_protocol_amf0.hpp>
+#include <srs_protocol_rtmp_stack.hpp>
+#include <srs_protocol_http_conn.hpp>
+#include <srs_protocol_protobuf.hpp>
+#include <srs_kernel_buffer.hpp>
+
+extern bool srs_is_valid_jsonp_callback(std::string callback);
+
+VOID TEST(ProtocolHttpTest, JsonpCallbackName)
+{
+    EXPECT_TRUE(srs_is_valid_jsonp_callback(""));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("callback"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback-1234567890"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback_1234567890"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback.1234567890"));
+    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890-_."));
+    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback()//"));
+    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback!"));
+    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback;"));
+}
+
diff --git a/trunk/src/utest/srs_utest_protocol3.hpp b/trunk/src/utest/srs_utest_protocol3.hpp
new file mode 100644
index 000000000..d0fa6f8a5
--- /dev/null
+++ b/trunk/src/utest/srs_utest_protocol3.hpp
@@ -0,0 +1,16 @@
+//
+// Copyright (c) 2013-2024 The SRS Authors
+//
+// SPDX-License-Identifier: MIT
+//
+
+#ifndef SRS_UTEST_PROTOCOL3_HPP
+#define SRS_UTEST_PROTOCOL3_HPP
+
+/*
+#include <srs_utest_protocol3.hpp>
+*/
+#include <srs_utest_protocol.hpp>
+
+#endif
+

Vulnerable code:         "srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2")
#define VERSION_REVISION    209
#define VERSION_REVISION    120
    XX(ERROR_STREAM_CASTER_HEVC_FORMAT     , 4057, "CasterTsHevcFormat", "Invalid ts HEVC Format for stream caster")
        if (!query_get("callback").empty()) {
            jsonp = true;
        }
Fixed code:         "srs_utest_protocol" "srs_utest_protocol2" "srs_utest_kernel2" "srs_utest_protocol3")
* v6.0, 2024-03-26, Filter JSONP callback function name. v6.0.121
* v5.0, 2024-03-26, Filter JSONP callback function name. v5.0.210
#define VERSION_REVISION    210
#define VERSION_REVISION    121
    XX(ERROR_STREAM_CASTER_HEVC_FORMAT     , 4057, "CasterTsHevcFormat", "Invalid ts HEVC Format for stream caster") \
    XX(ERROR_HTTP_JSONP                    , 4058, "HttpJsonp", "Invalid callback for JSONP")
// For callback function name, only allow [a-zA-Z0-9_-.] characters.
bool srs_is_valid_jsonp_callback(std::string callback)
{
    for (int i = 0; i < (int)callback.length(); i++) {
        char ch = callback.at(i);
        bool is_alpha_beta = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
        bool is_number = (ch >= '0' && ch <= '9');
        if (!is_alpha_beta && !is_number && ch != '.' && ch != '_' && ch != '-') {
            return false;
        }
    }
    return true;
}

        string callback= query_get("callback");
        jsonp = !callback.empty();


        if (!srs_is_valid_jsonp_callback(callback)) {
            return srs_error_new(ERROR_HTTP_JSONP, "invalid callback=%s", callback.c_str());
        }
//
// Copyright (c) 2013-2024 The SRS Authors
//
// SPDX-License-Identifier: MIT
//
#include <srs_utest_protocol3.hpp>

using namespace std;

#include <srs_kernel_error.hpp>
#include <srs_core_autofree.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_protocol_rtmp_msg_array.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_app_st.hpp>
#include <srs_protocol_amf0.hpp>
#include <srs_protocol_rtmp_stack.hpp>
#include <srs_protocol_http_conn.hpp>
#include <srs_protocol_protobuf.hpp>
#include <srs_kernel_buffer.hpp>

extern bool srs_is_valid_jsonp_callback(std::string callback);

VOID TEST(ProtocolHttpTest, JsonpCallbackName)
{
    EXPECT_TRUE(srs_is_valid_jsonp_callback(""));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("callback"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback-1234567890"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback_1234567890"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback.1234567890"));
    EXPECT_TRUE(srs_is_valid_jsonp_callback("Callback1234567890-_."));
    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback()//"));
    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback!"));
    EXPECT_FALSE(srs_is_valid_jsonp_callback("callback;"));
}

//
// Copyright (c) 2013-2024 The SRS Authors
//
// SPDX-License-Identifier: MIT
//

#ifndef SRS_UTEST_PROTOCOL3_HPP
#define SRS_UTEST_PROTOCOL3_HPP

/*
#include <srs_utest_protocol3.hpp>
*/
#include <srs_utest_protocol.hpp>

#endif