diff --git a/dali/base/dadfs.cpp b/dali/base/dadfs.cpp index 19f10e16efd..6e2c6b5f113 100644 --- a/dali/base/dadfs.cpp +++ b/dali/base/dadfs.cpp @@ -9934,6 +9934,7 @@ class CInitGroups StringArray clusternames; unsigned defaultTimeout; bool machinesLoaded; + bool writeLock; GroupType getGroupType(const char *type) { @@ -10009,6 +10010,8 @@ class CInitGroups void addClusterGroup(const char *name, IPropertyTree *newClusterGroup, bool realCluster) { + if (!writeLock) + throw makeStringException(0, "CInitGroups::addClusterGroup called in read-only mode"); VStringBuffer prop("Group[@name=\"%s\"]", name); IPropertyTree *root = groupsconnlock.conn->queryRoot(); IPropertyTree *old = root->queryPropTree(prop.str()); @@ -10366,11 +10369,12 @@ class CInitGroups return root->queryPropTree(xpath.str()); } public: - CInitGroups(unsigned _defaultTimeout) - : groupsconnlock("constructGroup",SDS_GROUPSTORE_ROOT,true,false,false,_defaultTimeout) + CInitGroups(unsigned _defaultTimeout, bool _writeLock) + : groupsconnlock("constructGroup",SDS_GROUPSTORE_ROOT,_writeLock,false,false,_defaultTimeout) { defaultTimeout = _defaultTimeout; machinesLoaded = false; + writeLock = _writeLock; } IPropertyTree *queryCluster(const IPropertyTree *env, const char *_clusterName, const char *type, const char *msg, StringBuffer &messages) @@ -10707,13 +10711,13 @@ class CInitGroups void initClusterGroups(bool force, StringBuffer &response, IPropertyTree *oldEnvironment, unsigned timems) { - CInitGroups init(timems); + CInitGroups init(timems, true); init.constructGroups(force, response, oldEnvironment); } void initClusterAndStoragePlaneGroups(bool force, IPropertyTree *oldEnvironment, unsigned timems) { - CInitGroups init(timems); + CInitGroups init(timems, true); StringBuffer response; init.constructGroups(force, response, oldEnvironment); @@ -10728,19 +10732,19 @@ void initClusterAndStoragePlaneGroups(bool force, IPropertyTree *oldEnvironment, bool resetClusterGroup(const char *clusterName, const char *type, bool spares, StringBuffer &response, unsigned timems) { - CInitGroups init(timems); + CInitGroups init(timems, true); return init.resetClusterGroup(clusterName, type, spares, response); } bool addClusterSpares(const char *clusterName, const char *type, const std::vector &hosts, StringBuffer &response, unsigned timems) { - CInitGroups init(timems); + CInitGroups init(timems, true); return init.addSpares(clusterName, type, hosts, response); } bool removeClusterSpares(const char *clusterName, const char *type, const std::vector &hosts, StringBuffer &response, unsigned timems) { - CInitGroups init(timems); + CInitGroups init(timems, true); return init.removeSpares(clusterName, type, hosts, response); } @@ -10764,7 +10768,7 @@ static IGroup *getClusterNodeGroup(const char *clusterName, const char *type, bo * to DFS and elsewhere. */ Owned nodeGroup = queryNamedGroupStore().lookup(nodeGroupName); - CInitGroups init(timems); + CInitGroups init(timems, false); Owned expandedClusterGroup = init.getGroupFromCluster(type, cluster, true); if (!expandedClusterGroup) throwStringExceptionV(0, "Failed to get group for '%s' cluster '%s'", type, clusterName); diff --git a/esp/bindings/http/platform/httpservice.cpp b/esp/bindings/http/platform/httpservice.cpp index 4e45abaa7aa..748155df69e 100644 --- a/esp/bindings/http/platform/httpservice.cpp +++ b/esp/bindings/http/platform/httpservice.cpp @@ -153,6 +153,33 @@ static bool authenticateOptionalFailed(IEspContext& ctx, IEspHttpBinding* bindin return false; } +static bool checkHttpPathStaysWithinBounds(const char *path) +{ + if (isEmptyString(path)) + return true; + //The path that follows /esp/files should be relative, not absolute - reject immediately if it is. + if (isAbsolutePath(path)) + return false; + int depth = 0; + StringArray nodes; + nodes.appendList(path, "/"); + ForEachItemIn(i, nodes) + { + const char *node = nodes.item(i); + if (!*node || streq(node, ".")) //empty or "." doesn't advance + continue; + if (!streq(node, "..")) + depth++; + else + { + depth--; + if (depth<0) //only really care that the relative http path doesn't position itself above its own root node + return false; + } + } + return true; +} + EspHttpBinding* CEspHttpServer::getBinding() { EspHttpBinding* thebinding=NULL; @@ -417,7 +444,16 @@ int CEspHttpServer::processRequest() m_response->redirect(*m_request.get(),url); } else + { + if (strieq(methodName.str(), "files_") && !checkHttpPathStaysWithinBounds(pathEx)) + { + AERRLOG("Get File %s: attempted access outside of %sfiles/", pathEx.str(), getCFD()); + m_response->setStatus(HTTP_STATUS_NOT_FOUND); + m_response->send(); + return 0; + } thebinding->onGet(m_request.get(), m_response.get()); + } } else unsupported(); @@ -733,33 +769,6 @@ static void httpGetDirectory(CHttpRequest* request, CHttpResponse* response, con response->send(); } -static bool checkHttpPathStaysWithinBounds(const char *path) -{ - if (!path || !*path) - return true; - //The path that follows /esp/files should be relative, not absolute - reject immediately if it is. - if (isAbsolutePath(path)) - return false; - int depth = 0; - StringArray nodes; - nodes.appendList(path, "/"); - ForEachItemIn(i, nodes) - { - const char *node = nodes.item(i); - if (!*node || streq(node, ".")) //empty or "." doesn't advance - continue; - if (!streq(node, "..")) - depth++; - else - { - depth--; - if (depth<0) //only really care that the relative http path doesn't position itself above its own root node - return false; - } - } - return true; -} - int CEspHttpServer::onGetFile(CHttpRequest* request, CHttpResponse* response, const char *urlpath) { if (!request || !response || !urlpath) @@ -810,6 +819,14 @@ int CEspHttpServer::onGetXslt(CHttpRequest* request, CHttpResponse* response, co if (!request || !response || !path) return -1; + if (!checkHttpPathStaysWithinBounds(path)) + { + AERRLOG("Get File %s: attempted access outside of %sxslt/", path, getCFD()); + response->setStatus(HTTP_STATUS_NOT_FOUND); + response->send(); + return 0; + } + StringBuffer mimetype, etag, lastModified; MemoryBuffer content; bool modified = true; diff --git a/esp/src/src-react/components/WorkunitDetails.tsx b/esp/src/src-react/components/WorkunitDetails.tsx index d4b779309f2..df196748aeb 100644 --- a/esp/src/src-react/components/WorkunitDetails.tsx +++ b/esp/src/src-react/components/WorkunitDetails.tsx @@ -105,9 +105,6 @@ export const WorkunitDetails: React.FunctionComponent = ({ }, { id: "eclsummary", label: nlsHPCC.ECL - }, { - id: "eclsummaryold", - label: "L" + nlsHPCC.ECL }, { id: "xml", label: nlsHPCC.XML diff --git a/esp/src/src-react/components/forms/landing-zone/BlobImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/BlobImportForm.tsx index 0ca2e32c585..2ce53e8f583 100644 --- a/esp/src/src-react/components/forms/landing-zone/BlobImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/BlobImportForm.tsx @@ -85,9 +85,8 @@ export const BlobImportForm: React.FunctionComponent = ({ request = data; if (!isContainer) { request["sourceIP"] = file.SourceIP; - } else { - request["sourcePlane"] = file.SourcePlane; } + request["sourcePlane"] = file.SourcePlane; request["sourcePath"] = file.SourceFile; request["fullPath"] = file.SourceFile; requests.push(FileSpray.SprayFixed({ diff --git a/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx index 13ffd9b8ccf..6e4d8accf49 100644 --- a/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx @@ -102,9 +102,8 @@ export const DelimitedImportForm: React.FunctionComponent = ({ request = data; if (!isContainer) { request["sourceIP"] = file.SourceIP; - } else { - request["sourcePlane"] = file.SourcePlane; } + request["sourcePlane"] = file.SourcePlane; request["sourcePath"] = file.SourceFile; request["sourceRecordSize"] = file.RecordSize; request["destLogicalName"] = data.namePrefix + (( diff --git a/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx index 9f6842a579a..d9042425a31 100644 --- a/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx @@ -84,9 +84,8 @@ export const VariableImportForm: React.FunctionComponent = ({ request = data; if (!isContainer) { request["sourceIP"] = file.SourceIP; - } else { - request["sourcePlane"] = file.SourcePlane; } + request["sourcePlane"] = file.SourcePlane; request["sourcePath"] = file.SourceFile; request["destLogicalName"] = data.namePrefix + (( data.namePrefix && data.namePrefix.substring(-2) !== "::" && diff --git a/esp/src/src-react/util/history.ts b/esp/src/src-react/util/history.ts index 9521b9902f4..e46d91e61a6 100644 --- a/esp/src/src-react/util/history.ts +++ b/esp/src/src-react/util/history.ts @@ -53,7 +53,8 @@ export function parseSearch>(_: strin return { ...parsed } as unknown as T; } -export function parseSort(_: string): QuerySortItem { +export function parseSort(_?: string): QuerySortItem | undefined { + if (!_) return undefined; const filter = parse(pick(_.substring(1), ["sortBy"])); let descending = false; let sortBy = filter?.sortBy?.toString(); diff --git a/thorlcr/activities/lookupjoin/thlookupjoinslave.cpp b/thorlcr/activities/lookupjoin/thlookupjoinslave.cpp index 025aa878c90..ae5dabcb914 100644 --- a/thorlcr/activities/lookupjoin/thlookupjoinslave.cpp +++ b/thorlcr/activities/lookupjoin/thlookupjoinslave.cpp @@ -905,6 +905,7 @@ class CInMemJoinBase : public CSlaveActivity, public CAllOrLookupHelper, { exception.setown(e); EXCLOG(e, "CRowProcessor"); + owner.broadcaster->cancel(e); } } } *rowProcessor;