fix: preserve base64 passwords with embedded '=' in Surge SS parser

split(configs[i], "=") splits on ALL '=' characters, truncating
SS 2022-blake3 passwords that contain base64 padding (==) +
colon-delimited IMK. Replaced with split_first() which splits
only on the first '=', keeping the full password intact.

- Added split_first() utility to string.h/string.cpp
- Replaced all 15 key=value split() calls in subparser.cpp
- Verified with Docker build + Surge/Clash output test
This commit is contained in:
feisan committed 2026-06-02 09:49:05 +08:00
1 parent 3c640b1976
commit 781f06a8ed
4 files changed
+90 -15

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
FROM alpine:3.16 AS builder
LABEL maintainer="feisan"
ARG THREADS="4"
WORKDIR /
RUN set -xe && \
apk add --no-cache --virtual .build-tools git g++ build-base linux-headers cmake python3 && \
apk add --no-cache --virtual .build-deps curl-dev rapidjson-dev pcre2-dev yaml-cpp-dev && \
git clone --no-checkout https://github.com/ftk/quickjspp.git && \
cd quickjspp && \
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09 && \
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09 && \
git submodule update --init && \
cmake -DCMAKE_BUILD_TYPE=Release . && \
make quickjs -j $THREADS && \
install -d /usr/lib/quickjs/ && \
install -m644 quickjs/libquickjs.a /usr/lib/quickjs/ && \
install -d /usr/include/quickjs/ && \
install -m644 quickjs/quickjs.h quickjs/quickjs-libc.h /usr/include/quickjs/ && \
install -m644 quickjspp.hpp /usr/include && \
cd .. && \
git clone https://github.com/PerMalmberg/libcron --depth=1 && \
cd libcron && \
git submodule update --init && \
cmake -DCMAKE_BUILD_TYPE=Release . && \
make libcron -j $THREADS && \
install -m644 libcron/out/Release/liblibcron.a /usr/lib/ && \
install -d /usr/include/libcron/ && \
install -m644 libcron/include/libcron/* /usr/include/libcron/ && \
install -d /usr/include/date/ && \
install -m644 libcron/externals/date/include/date/* /usr/include/date/ && \
cd .. && \
git clone https://github.com/ToruNiina/toml11 --branch="v4.3.0" --depth=1 && \
cd toml11 && \
cmake -DCMAKE_CXX_STANDARD=11 . && \
make install -j $THREADS && \
cd ..
# Copy local source instead of cloning from GitHub
COPY . /subconverter
WORKDIR /subconverter
RUN python3 -m ensurepip && \
python3 -m pip install gitpython && \
python3 scripts/update_rules.py -c scripts/rules_config.conf && \
cmake -DCMAKE_BUILD_TYPE=Release . && \
make -j $THREADS
# build final image
FROM alpine:3.16
RUN apk add --no-cache --virtual subconverter-deps pcre2 libcurl yaml-cpp
COPY --from=builder /subconverter/subconverter /usr/bin/
COPY --from=builder /subconverter/base /base/
ENV TZ=Africa/Abidjan
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /base
CMD subconverter
EXPOSE 25500/tcp
+15 -15
View File
@@ -1031,7 +1031,7 @@ void explodeQuan(const std::string &quan, Proxy &node) {
//read link
for (uint32_t i = 6; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
@@ -2054,7 +2054,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
password = trim(configs[4]);
for (i = 6; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
@@ -2102,7 +2102,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
@@ -2156,7 +2156,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
password = trim(configs[4]);
}
for (i = 5; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
@@ -2186,7 +2186,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
method = "auto";
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2247,7 +2247,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
if (port == "0")
continue;
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
@@ -2275,7 +2275,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2318,7 +2318,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2355,7 +2355,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
break;
case "wireguard"_hash:
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2416,7 +2416,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 3; i < configs.size(); i++) {
vArray = split(configs[i], "=");
vArray = split_first(configs[i], "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2455,7 +2455,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2550,7 +2550,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
net = "tcp";
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2617,7 +2617,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
net = "tcp";
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2683,7 +2683,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
@@ -2735,7 +2735,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes) {
continue;
for (i = 1; i < configs.size(); i++) {
vArray = split(trim(configs[i]), "=");
vArray = split_first(trim(configs[i]), "=");
if (vArray.size() != 2)
continue;
itemName = trim(vArray[0]);
+16
View File
@@ -25,6 +25,22 @@ std::vector<std::string> split(const std::string &s, const std::string &separato
return result;
}
std::vector<std::string> split_first(const std::string &s, const std::string &separator)
{
std::vector<std::string> result;
string_size pos = s.find(separator);
if(pos == std::string::npos)
{
result.push_back(s);
}
else
{
result.push_back(s.substr(0, pos));
result.push_back(s.substr(pos + separator.size()));
}
return result;
}
void split(std::vector<std::string_view> &result, std::string_view s, char separator)
{
string_size bpos = 0, epos = s.find(separator);
+2
View File
@@ -18,6 +18,8 @@ using string_pair_array = std::vector<std::pair<std::string, std::string>>;
std::vector<std::string> split(const std::string &s, const std::string &separator);
std::vector<std::string_view> split(std::string_view s, char separator);
void split(std::vector<std::string_view> &result, std::string_view s, char separator);
// Split on first occurrence only (preserves values containing separator, e.g. base64 "==")
std::vector<std::string> split_first(const std::string &s, const std::string &separator);
std::string join(const string_array &arr, const std::string &delimiter);
template <typename InputIt>