-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[http] sanitise URL options before use them #22186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
d4c1aa2
0433163
5b99c6e
fa53f87
772aa0f
ab58efa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,8 @@ | |
| #include <memory> | ||
| #include <vector> | ||
| #include <cstring> | ||
| #include <cctype> | ||
|
|
||
|
|
||
| const char *item_prop_kind = "_kind"; | ||
| const char *item_prop_more = "_more"; | ||
|
|
@@ -1213,9 +1215,41 @@ Bool_t TRootSniffer::ExecuteCmd(const std::string &path, const std::string &opti | |
| return kTRUE; | ||
| } | ||
|
|
||
| TString svalue = DecodeUrlOptionValue(argvalue, kTRUE); | ||
| argname = TString("%") + argname + TString("%"); | ||
| method.ReplaceAll(argname, svalue); | ||
| auto p = method.Index(argname); | ||
| if (p == kNPOS) | ||
| continue; | ||
|
|
||
| method.Remove(p, argname.Length()); | ||
|
|
||
| if ((p > 0) && (p < method.Length()) && (method.Length() > 1) && (method[p-1] == '"') && (method[p] == '"')) { | ||
| // command definition has quotes around argument | ||
| // one can insert value from URL removing quotes | ||
| method.Insert(p, DecodeUrlOptionValue(argvalue, kTRUE)); | ||
| continue; | ||
| } | ||
|
|
||
| // extract argument without removing quotes | ||
| TString svalue = DecodeUrlOptionValue(argvalue, kFALSE); | ||
|
|
||
| if ((svalue.Length() > 1) && (svalue[0] == '"') && (svalue[svalue.Length() - 1] == '"')) { | ||
| // if value itself has quotes, all special symbols already escaped and one can insert it as is | ||
| method.Insert(p, svalue); | ||
| continue; | ||
| } | ||
|
|
||
| Bool_t is_numeric = kTRUE; | ||
| // expect decimal, hex or float values here, E/e also belong to hex | ||
| for(Size_t i = 0; is_numeric && (i < svalue.Length()); ++i) | ||
| is_numeric = std::isxdigit(svalue[i]) || std::strchr(".+-", svalue[i]); | ||
|
|
||
| // always quote content which not numeric | ||
| if (!is_numeric) | ||
| svalue = "\"" + svalue + "\""; | ||
| else if (svalue.IsNull()) | ||
| svalue = "0"; | ||
|
|
||
| method.Insert(p, svalue); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1289,8 +1323,8 @@ Bool_t TRootSniffer::ProduceXml(const std::string &/* path */, const std::string | |
|
|
||
| TString TRootSniffer::DecodeUrlOptionValue(const char *value, Bool_t remove_quotes) | ||
| { | ||
| if (!value || (strlen(value) == 0)) | ||
| return TString(); | ||
| if (!value || !*value) | ||
| return ""; | ||
|
|
||
| TString res = value; | ||
|
|
||
|
|
@@ -1303,13 +1337,40 @@ TString TRootSniffer::DecodeUrlOptionValue(const char *value, Bool_t remove_quot | |
| res.ReplaceAll("%5D", "]"); | ||
| res.ReplaceAll("%3D", "="); | ||
|
linev marked this conversation as resolved.
|
||
|
|
||
| if (remove_quotes && (res.Length() > 1) && ((res[0] == '\'') || (res[0] == '\"')) && | ||
| (res[0] == res[res.Length() - 1])) { | ||
| Char_t quote = 0; | ||
|
|
||
| if ((res.Length() > 1) && ((res[0] == '\'') || (res[0] == '\"')) && (res[0] == res[res.Length() - 1])) | ||
| quote = res[0]; | ||
|
|
||
| // first remove quotes | ||
| if (quote) { | ||
| res.Remove(res.Length() - 1); | ||
| res.Remove(0, 1); | ||
| } | ||
|
|
||
| return res; | ||
| // we expect normal content here, no special symbols, no unescaped quotes | ||
| TString clean; | ||
| for (Ssiz_t i = 0; i < res.Length(); ++i) { | ||
| char c = res[i]; | ||
| if (c == '"' || c == '\\') { | ||
| // escape quotes and slahes | ||
| clean.Append('\\'); | ||
| clean.Append(c); | ||
| } else if (!std::iscntrl(c)) | ||
| // ignore all special symbols | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably I just will use
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what this excludes exactly (does it exclude all ASCII non-printable characters? Does it depend on the locale? The man page makes it look like the case), so I was actually more comfortable with the explicit check.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not like to exclude all non-latin symbols here - as long as they supported on all other places. Main intention of this PR - prevent creation of string literals which can be mis-interpreted by
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are basically already excluding non-latin symbols by doing To be more precise: if Unicode support is desired then we should make sure it works, as it's not really obvious at all that we are handling it correctly throughout the entire pipeline from start to finish (and I don't think we should bother)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In |
||
| clean.Append(c); | ||
| } | ||
|
|
||
| if (quote && !remove_quotes) { | ||
| // return string with quotes - when desired | ||
| res = ""; | ||
| res.Append(quote); | ||
| res.Append(clean); | ||
| res.Append(quote); | ||
| return res; | ||
| } | ||
|
|
||
| return clean; | ||
| } | ||
|
|
||
| //////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. | ||
| # All rights reserved. | ||
| # | ||
| # For the licensing terms see $ROOTSYS/LICENSE. | ||
| # For the list of contributors see $ROOTSYS/README/CREDITS. | ||
|
|
||
| ############################################################################ | ||
| # CMakeLists.txt file for building ROOT net/http package | ||
| # @author Sergey Linev, GSI | ||
| ############################################################################ | ||
|
|
||
| ROOT_ADD_GTEST(testRootSniffer test_sniffer.cxx LIBRARIES RHTTPSniff) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How long can
valuebe? Since we're trying to sanitize the input, shouldn't we also put a reasonable max length?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no such limit and I cannot make here any assumption.
civetwebcuts URL length by 16K.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The backend is not guaranteed to be civetweb in principle, is it?
We should put an artificial limit here so we don't have to worry about huge allocations/reallocations/copies/iterations and we could in principle even preallocate the output string once and do a single pass over the string to validate it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my mind such checks are not part of this PR.
They can be done in
TUrlclass which used to parse all url argumentsThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it fragile to trust the assumption that all strings getting here come from some other place that already partially sanitized them, especially considering it's not even an explicit thing happening at the calling site.
That said, you can at least spare the
strlen(value) == 0and simply checkres.Length()afterwards, to save one string scan.