Perform UrlDecodeUni & UrlDecode transformations in-place

- Use std::string in UrlEncode transformation, instead of manually
  memory management. This avoids an additional copy after completing
  encoding by just swapping the encoded value and the input.
- Removed inplace helper function from the class, as it's only
  referenced by the implementation.
This commit is contained in:
Eduardo Arias 2024-08-19 09:12:12 -07:00
parent 8bf4d96e6b
commit 17a2cbd164
5 changed files with 45 additions and 99 deletions

View File

@ -27,25 +27,16 @@ UrlDecode::UrlDecode(const std::string &action)
} }
bool UrlDecode::transform(std::string &value, const Transaction *trans) const { bool UrlDecode::transform(std::string &value, const Transaction *trans) const {
unsigned char *val(NULL);
int invalid_count = 0; int invalid_count = 0;
int _changed; int changed;
const auto new_len = utils::urldecode_nonstrict_inplace(
(unsigned char*)value.data(),
value.length(),
&invalid_count,
&changed);
val = (unsigned char *) malloc(sizeof(char) * value.size() + 1); value.resize(new_len);
memcpy(val, value.c_str(), value.size() + 1); return changed != 0;
val[value.size()] = '\0';
int size = utils::urldecode_nonstrict_inplace(val, value.size(),
&invalid_count, &_changed);
std::string out;
out.append((const char *)val, size);
free(val);
const auto changed = out != value;
value = out;
return changed;
} }

View File

@ -22,41 +22,19 @@
namespace modsecurity::actions::transformations { namespace modsecurity::actions::transformations {
bool UrlDecodeUni::transform(std::string &value, const Transaction *t) const {
std::string ret;
unsigned char *input;
input = reinterpret_cast<unsigned char *>
(malloc(sizeof(char) * value.length()+1));
if (input == NULL) {
return "";
}
memcpy(input, value.c_str(), value.length()+1);
size_t i = inplace(input, value.length(), t);
ret.assign(reinterpret_cast<char *>(input), i);
free(input);
const auto changed = ret != value;
value = ret;
return changed;
}
/** /**
* *
* IMP1 Assumes NUL-terminated * IMP1 Assumes NUL-terminated
*/ */
int UrlDecodeUni::inplace(unsigned char *input, uint64_t input_len, static inline bool inplace(std::string &value,
const Transaction *t) { const Transaction *t) {
unsigned char *d = input; bool changed = false;
int64_t i, count, fact, j, xv; auto d = reinterpret_cast<unsigned char*>(value.data());
int Code, hmap = -1; const unsigned char *input = d;
const auto input_len = value.length();
if (input == NULL) return -1; std::string::size_type i, count, fact, j, xv;
int Code, hmap = -1;
i = count = 0; i = count = 0;
while (i < input_len) { while (i < input_len) {
@ -116,19 +94,17 @@ int UrlDecodeUni::inplace(unsigned char *input, uint64_t input_len,
} }
} }
d++; d++;
count++;
i += 6; i += 6;
changed = true;
} else { } else {
/* Invalid data, skip %u. */ /* Invalid data, skip %u. */
*d++ = input[i++]; *d++ = input[i++];
*d++ = input[i++]; *d++ = input[i++];
count += 2;
} }
} else { } else {
/* Not enough bytes (4 data bytes), skip %u. */ /* Not enough bytes (4 data bytes), skip %u. */
*d++ = input[i++]; *d++ = input[i++];
*d++ = input[i++]; *d++ = input[i++];
count += 2;
} }
} else { } else {
/* Standard URL encoding. */ /* Standard URL encoding. */
@ -143,8 +119,8 @@ int UrlDecodeUni::inplace(unsigned char *input, uint64_t input_len,
if (VALID_HEX(c1) && VALID_HEX(c2)) { if (VALID_HEX(c1) && VALID_HEX(c2)) {
*d++ = utils::string::x2c(&input[i + 1]); *d++ = utils::string::x2c(&input[i + 1]);
count++;
i += 3; i += 3;
changed = true;
} else { } else {
/* Not a valid encoding, skip this % */ /* Not a valid encoding, skip this % */
*d++ = input[i++]; *d++ = input[i++];
@ -153,25 +129,31 @@ int UrlDecodeUni::inplace(unsigned char *input, uint64_t input_len,
} else { } else {
/* Not enough bytes available, skip this % */ /* Not enough bytes available, skip this % */
*d++ = input[i++]; *d++ = input[i++];
count++;
} }
} }
} else { } else {
/* Character is not a percent sign. */ /* Character is not a percent sign. */
if (input[i] == '+') { if (input[i] == '+') {
*d++ = ' '; *d++ = ' ';
changed = true;
} else { } else {
*d++ = input[i]; *d++ = input[i];
} }
count++;
i++; i++;
} }
} }
*d = '\0'; *d = '\0';
return count; value.resize(d - input);
return changed;
}
bool UrlDecodeUni::transform(std::string &value, const Transaction *trans) const {
return inplace(value, trans);
} }

View File

@ -26,8 +26,6 @@ class UrlDecodeUni : public Transformation {
: Transformation(action) { } : Transformation(action) { }
bool transform(std::string &value, const Transaction *trans) const override; bool transform(std::string &value, const Transaction *trans) const override;
static int inplace(unsigned char *input, uint64_t input_len,
const Transaction *transaction);
}; };
} // namespace modsecurity::actions::transformations } // namespace modsecurity::actions::transformations

View File

@ -26,65 +26,43 @@ UrlEncode::UrlEncode(const std::string &action)
} }
std::string UrlEncode::url_enc(const char *input, static inline bool url_enc(std::string &value) {
unsigned int input_len, int *changed) { const auto len = value.size() * 3 + 1;
char *rval, *d; std::string ret(len, {});
unsigned int i, len;
int count = 0;
*changed = 0; bool changed = false;
len = input_len * 3 + 1;
d = rval = reinterpret_cast<char *>(malloc(len));
if (rval == NULL) {
return {};
}
/* ENH Only encode the characters that really need to be encoded. */ /* ENH Only encode the characters that really need to be encoded. */
for (i = 0; i < input_len; i++) { char *d = ret.data();
unsigned char c = input[i]; for (const auto c : value) {
if (c == ' ') { if (c == ' ') {
*d++ = '+'; *d++ = '+';
*changed = 1; changed = true;
count++;
} else { } else {
if ( (c == 42) || ((c >= 48) && (c <= 57)) if ( (c == 42) || ((c >= 48) && (c <= 57))
|| ((c >= 65) && (c <= 90)) || ((c >= 65) && (c <= 90))
|| ((c >= 97) && (c <= 122))) { || ((c >= 97) && (c <= 122))) {
*d++ = c; *d++ = c;
count++; }
} else { else
{
*d++ = '%'; *d++ = '%';
count++; d = (char *)utils::string::c2x(c, (unsigned char *)d);
utils::string::c2x(c, (unsigned char *)d); changed = true;
d += 2;
count++;
count++;
*changed = 1;
} }
} }
} }
*d = '\0'; ret.resize(d - ret.c_str());
std::swap(value, ret);
std::string ret("");
ret.append(rval, count);
free(rval);
return ret;
}
bool UrlEncode::transform(std::string &value, const Transaction *trans) const {
int _changed;
std::string ret = url_enc(value.c_str(), value.size(), &_changed);
const auto changed = ret != value;
value = ret;
return changed; return changed;
} }
bool UrlEncode::transform(std::string &value, const Transaction *trans) const {
return url_enc(value);
}
} // namespace modsecurity::actions::transformations } // namespace modsecurity::actions::transformations

View File

@ -25,9 +25,6 @@ class UrlEncode : public Transformation {
explicit UrlEncode(const std::string &action); explicit UrlEncode(const std::string &action);
bool transform(std::string &value, const Transaction *trans) const override; bool transform(std::string &value, const Transaction *trans) const override;
static std::string url_enc(const char *input,
unsigned int input_len, int *changed);
}; };
} // namespace modsecurity::actions::transformations } // namespace modsecurity::actions::transformations