diff --git a/net/http/src/TRootSniffer.cxx b/net/http/src/TRootSniffer.cxx index b95b63ca9a55b..39d06b330fb6a 100644 --- a/net/http/src/TRootSniffer.cxx +++ b/net/http/src/TRootSniffer.cxx @@ -1289,8 +1289,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 +1303,40 @@ TString TRootSniffer::DecodeUrlOptionValue(const char *value, Bool_t remove_quot res.ReplaceAll("%5D", "]"); res.ReplaceAll("%3D", "="); - 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 + 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; } //////////////////////////////////////////////////////////////////////////////// diff --git a/net/httpsniff/inc/TRootSnifferFull.h b/net/httpsniff/inc/TRootSnifferFull.h index 7af2137646a7c..10f590dd97212 100644 --- a/net/httpsniff/inc/TRootSnifferFull.h +++ b/net/httpsniff/inc/TRootSnifferFull.h @@ -16,6 +16,7 @@ #include class TMemFile; +class TMethodArg; class TRootSnifferFull : public TRootSniffer { protected: @@ -30,6 +31,8 @@ class TRootSnifferFull : public TRootSniffer { void CreateMemFile(); + static TString SanitizeArgument(Bool_t isstr, const char *value, Bool_t onlyquotes = kFALSE); + Bool_t CanDrawClass(TClass *cl) override { return IsDrawableClass(cl); } Bool_t HasStreamerInfo() const override { return kTRUE; } diff --git a/net/httpsniff/src/TRootSnifferFull.cxx b/net/httpsniff/src/TRootSnifferFull.cxx index c9f265fb2f43e..7ae5bb6a7e9ce 100644 --- a/net/httpsniff/src/TRootSnifferFull.cxx +++ b/net/httpsniff/src/TRootSnifferFull.cxx @@ -416,7 +416,7 @@ Bool_t TRootSnifferFull::ProduceImage(Int_t kind, const std::string &path, const if (gDebug > 1) Info("TRootSniffer", "Crate IMAGE from object %s", obj->GetName()); - Int_t width(300), height(200); + Int_t width = 300, height = 200; TString drawopt; if (!options.empty()) { @@ -429,9 +429,7 @@ Bool_t TRootSnifferFull::ProduceImage(Int_t kind, const std::string &path, const Int_t h = url.GetIntValueFromOptions("h"); if (h > 10) height = h; - const char *opt = url.GetValueFromOptions("opt"); - if (opt) - drawopt = opt; + drawopt = DecodeUrlOptionValue(url.GetValueFromOptions("opt"), kTRUE); } Bool_t isbatch = gROOT->IsBatch(); @@ -513,6 +511,42 @@ Bool_t TRootSnifferFull::ProduceXml(const std::string &path, const std::string & return !res.empty(); } +//////////////////////////////////////////////////////////////////////////////// +/// Verify argument value +/// Remove special symbols, escape quotes +/// If not string - only digits, literals, "+", "-", "." are allowed + +TString TRootSnifferFull::SanitizeArgument(Bool_t isstr, const char *value, Bool_t onlyquotes) +{ + Int_t beg = 0, end = strlen(value); + if (end > 0 && isstr && value[beg] == '\"') + beg++; + if (end > 0 && isstr && value[end-1] == '\"') + end--; + TString sanitized; + if (isstr) + sanitized.Append("\""); // string start with quotes + for(Int_t n = beg; n < end; ++n) { + Char_t c = value[n]; + if (!isstr && !onlyquotes) { + // for numeric types make very strict check + if (std::isalnum(c) || std::strchr(".:+-", c)) + sanitized.Append(c); + } else if ((c == '\"') || (c == '\\')) { + // escape quotes inside string + sanitized.Append('\\'); + sanitized.Append(c); + } else if (!std::iscntrl(c)) { + // exclude special chars + sanitized.Append(c); + } + } + if (isstr) + sanitized.Append('\"'); // string end with quotes + return sanitized; +} + + //////////////////////////////////////////////////////////////////////////////// /// Execute command for specified object /// @@ -638,6 +672,10 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string & val = sval.Data(); } + // only value provided from outside should be sanitized + // default argument value or internal pointer will not + Bool_t sanitize = kFALSE; + if ((val != nullptr) && (strcmp(val, "_this_") == 0)) { // special case - object itself is used as argument sval.Form("(%s*)0x%zx", obj_cl->GetName(), (size_t)obj_ptr); @@ -667,7 +705,7 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string & } val = sval.Data(); } else if ((strcmp(val, "_post_object_") == 0) && url.HasOption("_post_class_")) { - TString clname = url.GetValueFromOptions("_post_class_"); + TString clname = DecodeUrlOptionValue(url.GetValueFromOptions("_post_class_"), kTRUE); TClass *arg_cl = gROOT->GetClass(clname, kTRUE, kTRUE); if ((arg_cl != nullptr) && (arg_cl->GetBaseClassOffset(TObject::Class()) == 0) && (post_obj == nullptr)) { post_obj = (TObject *)arg_cl->New(); @@ -692,7 +730,11 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string & } else if (strcmp(val, "_post_length_") == 0) { sval.Form("%ld", (long)fCurrentArg->GetPostDataLength()); val = sval.Data(); + } else { + sanitize = kTRUE; } + } else { + sanitize = val != nullptr; } if (!val) @@ -708,15 +750,11 @@ Bool_t TRootSnifferFull::ProduceExe(const std::string &path, const std::string & if (call_args.Length() > 0) call_args += ", "; - if ((strcmp(arg->GetFullTypeName(), "const char*") == 0) || (strcmp(arg->GetFullTypeName(), "Option_t*") == 0)) { - int len = strlen(val); - if ((strlen(val) < 2) || (*val != '\"') || (val[len - 1] != '\"')) - call_args.Append(TString::Format("\"%s\"", val)); - else - call_args.Append(val); - } else { - call_args.Append(val); - } + Bool_t isstr = (strcmp(arg->GetFullTypeName(), "const char*") == 0) || + (strcmp(arg->GetFullTypeName(), "Option_t*") == 0) || + (strcmp(arg->GetFullTypeName(), "string") == 0); + + call_args.Append(SanitizeArgument(isstr, val, !sanitize)); } TMethodCall *call = nullptr;