From 1d58ac4d793dd1842ea04f50b994ee2b455103b7 Mon Sep 17 00:00:00 2001
From: Dale Glass
Date: Wed, 23 Sep 2020 23:33:22 +0200
Subject: [PATCH] Sanitize the URL further by removing leading slashes and any
duplicated ones
---
libraries/networking/src/ExternalResource.cpp | 23 +++++++++++++++++--
libraries/script-engine/src/ScriptEngine.h | 3 +--
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/libraries/networking/src/ExternalResource.cpp b/libraries/networking/src/ExternalResource.cpp
index e2b6a40682..a760e3554f 100644
--- a/libraries/networking/src/ExternalResource.cpp
+++ b/libraries/networking/src/ExternalResource.cpp
@@ -49,13 +49,32 @@ QUrl ExternalResource::getQUrl(Bucket bucket, const QUrl& path) {
base = _bucketBases[bucket];
}
- QUrl merged = base.resolved(path).adjusted(QUrl::NormalizePathSegments);
+ QUrl filteredPath = path;
+ QString pathQString = filteredPath.path();
+
+ // De-duplicate URL separators, since S3 doesn't actually have subdirectories, and treats a '/' as a part
+ // of the filename. This means that "/dir/file.txt" and "/dir//file.txt" are not equivalent on S3.
+ while(pathQString.contains("//")) {
+ pathQString = pathQString.replace("//", "/");
+ }
+
+ // If a path starts with a / it would have the effect of overriding any path specified in the bucket.
+ // We remove it, ensuring that getQUrl(Bucket.Assets, "/file.txt") and getQUrl(Bucket.Assets, "file.txt")
+ // are equivalent.
+ if (pathQString.startsWith("/")) {
+ pathQString.remove(0,1);
+ }
+
+ filteredPath.setPath(pathQString);
+
+ QUrl merged = base.resolved(filteredPath).adjusted(QUrl::NormalizePathSegments);
if ( merged.isValid() ) {
qCDebug(external_resource) << "External resource resolved to " << merged;
} else {
qCCritical(external_resource) << "External resource resolved to invalid URL " << merged << "; Error "
- << merged.errorString() << "; base = " << base << "; path = " << path;
+ << merged.errorString() << "; base = " << base << "; path = " << path
+ << "; filtered path = " << filteredPath;
}
return merged;
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index f393177c52..fff1cab00a 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -693,8 +693,7 @@ public:
* @param {string} path - The path within the external resource bucket where the asset is located.
* Normally, this should start with a path or filename to be appended to the bucket URL.
* Alternatively, it can be a relative path starting with ./
or ../
, to navigate within the
- * resource bucket's URL. Or it can be an absolute path starting with /
, in which case the bucket's path
- * is discarded when calculating the asset's URL.
+ * resource bucket's URL.
* @Returns {string} The URL of an external asset.
* @example Report the URL of a default particle.
* print(Script.getExternalPath(Script.ExternalPaths.Assets, "Bazaar/Assets/Textures/Defaults/Interface/default_particle.png"));