Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/base/src/validateThemeRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ const validateThemeRoot = (themeRoot: string) => {
let isSameOrigin = false;

try {
if (themeRoot.startsWith(".") || themeRoot.startsWith("/")) {
if (themeRoot.startsWith(".") || (themeRoot.startsWith("/") && !themeRoot.startsWith("//"))) {
// Handle relative url
// new URL("/newExmPath", "http://example.com/exmPath") => http://example.com/newExmPath
// new URL("./newExmPath", "http://example.com/exmPath") => http://example.com/exmPath/newExmPath
// new URL("../newExmPath", "http://example.com/exmPath") => http://example.com/newExmPath
resultUrl = new URL(themeRoot, getLocationHref()).toString();
isSameOrigin = true;
} else {
const themeRootURL = new URL(themeRoot);
// Protocol-relative URLs (//host/path) need a base to resolve the protocol
const themeRootURL = themeRoot.startsWith("//") ? new URL(themeRoot, getLocationHref()) : new URL(themeRoot);
const origin = themeRootURL.origin;
const currentOrigin = new URL(getLocationHref()).origin;

Expand Down
2 changes: 2 additions & 0 deletions packages/main/src/bundle.esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@ui5/webcomponents-base/dist/features/OpenUI5Support.js";
import "./Assets.js";

// Icons
import { boot } from "@ui5/webcomponents-base/dist/Boot.js";
import "@ui5/webcomponents-icons/dist/Assets.js";
import "@ui5/webcomponents-icons/dist/AllIcons.js";

Expand Down Expand Up @@ -197,6 +198,7 @@ ignoreCustomElements("my-");
const icons = [accept, acceptv4, acceptv5, actor, actorv2, actorv3, icon3d, icon3dv1, icon3dv2];

const testAssets = {
boot,
configuration: {
getAnimationMode,
setAnimationMode,
Expand Down
128 changes: 128 additions & 0 deletions packages/main/test/pages/ThemeRoot.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>Base package default test page</title>

<script src="%VITE_BUNDLE_PATH%" type="module"></script>

<style>
.result { margin-left: 8px; font-weight: bold; }
.result.allowed { color: green; }
.result.blocked { color: red; }
</style>
</head>

<body>
<h1>Without meta tag</h1>
<a href="./ThemeRoot.html">Reset URL</a>
<ul>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@http://example2.com/themes/">?sap-ui-theme=sap_horizon@http://example2.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example2.com/themes/">?sap-ui-themeRoot=http://example2.com/themes/</a>
<br /> http://example2.com/ — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('http://example2.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@https://example.com/themes/">?sap-ui-theme=sap_horizon@https://example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=https://example.com/themes/">?sap-ui-themeRoot=https://example.com/themes/</a>
<br /> https://example.com/ — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('https://example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@http://example:9090.com/themes/">?sap-ui-theme=sap_horizon@http://example:9090.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example:9090.com/themes/">?sap-ui-themeRoot=http://example:9090.com/themes/</a>
<br /> http://example:9090.com/ — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('http://example:9090.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@http://example.com/themes/">?sap-ui-theme=sap_horizon@http://example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example.com/themes/">?sap-ui-themeRoot=http://example.com/themes/</a>
<br /> http://example.com/ — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('http://example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@//example2.com/themes/">?sap-ui-theme=sap_horizon@//example2.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example2.com/themes/">?sap-ui-themeRoot=//example2.com/themes/</a>
<br /> //example2.com/ — inherits current page protocol (e.g. http://example2.com/) — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('//example2.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@//example:9090.com/themes/">?sap-ui-theme=sap_horizon@//example:9090.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example:9090.com/themes/">?sap-ui-themeRoot=//example:9090.com/themes/</a>
<br /> //example:9090.com/ — inherits current page protocol (e.g. http://example:9090.com/) — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('//example:9090.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@//example.com/themes/">?sap-ui-theme=sap_horizon@//example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example.com/themes/">?sap-ui-themeRoot=//example.com/themes/</a>
<br /> //example.com/ — inherits current page protocol (e.g. http://example.com/) — no meta tag present, blocked
<br /> <button onclick="testSetThemeRoot('//example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@/themes/">?sap-ui-theme=sap_horizon@/themes/</a>
<br /><a href="?sap-ui-themeRoot=/themes/">?sap-ui-themeRoot=/themes/</a>
<br /> /themes/
<br /> (resolves to the current page's origin, e.g. http://localhost:8080/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@./themes/">?sap-ui-theme=sap_horizon@./themes/</a>
<br /><a href="?sap-ui-themeRoot=./themes/">?sap-ui-themeRoot=./themes/</a>
<br /> ./themes/
<br /> (resolves relative to the current page's URL, e.g. http://localhost:8080/test/pages/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('./themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@../themes/">?sap-ui-theme=sap_horizon@../themes/</a>
<br /><a href="?sap-ui-themeRoot=../themes/">?sap-ui-themeRoot=../themes/</a>
<br /> ../themes/
<br /> (resolves relative to the current page's URL, e.g. http://localhost:8080/test/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('../themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
</ul>
<script type="module">
const bundle = window["sap-ui-webcomponents-bundle"];

if (window.location.search.includes("sap-ui-theme")) {
(async () => {
await bundle.boot();
const link = document.head.querySelector('link[sap-ui-webcomponents-theme]');
if (link) {
alert(`✅ Link created: ${link.href}`);
} else {
alert("❌ Blocked — no link element created");
}
})();
}

window.testSetThemeRoot = async (themeRoot, btn) => {
if (window.location.search.includes("sap-ui-theme")) {
alert("Please remove the 'sap-ui-theme' URL parameter before testing setThemeRoot.");
return;
}

const resultEl = btn.nextElementSibling;
await bundle.configuration.setThemeRoot(themeRoot);
const link = document.head.querySelector('link[sap-ui-webcomponents-theme]');
if (link) {
resultEl.textContent = `✅ Link created: ${link.href}`;
resultEl.className = "result allowed";
} else {
resultEl.textContent = "❌ Blocked — no link element created";
resultEl.className = "result blocked";
}
};

// ❌ await bundle.configuration.setThemeRoot("http://example2.com/themes/") // no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("https://example.com/themes/") // no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("http://example:9090.com/themes/") // no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("http://example.com/themes/") // no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("//example2.com/themes/") // inherits current page protocol (e.g. http://example2.com/) — no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("//example:9090.com/themes/") // inherits current page protocol (e.g. http://example:9090.com/) — no meta tag present, blocked
// ❌ await bundle.configuration.setThemeRoot("//example.com/themes/") // inherits current page protocol (e.g. http://example.com/) — no meta tag present, blocked
// ✅ await bundle.configuration.setThemeRoot("/themes/") // resolves to current page's origin — same-origin, always allowed
// ✅ await bundle.configuration.setThemeRoot("./themes/") // resolves relative to current page's URL — same-origin, always allowed
// ✅ await bundle.configuration.setThemeRoot("../themes/") // resolves relative to current page's URL — same-origin, always allowed
</script>
</body>

</html>
130 changes: 130 additions & 0 deletions packages/main/test/pages/ThemeRoot2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<meta name="sap-allowed-theme-origins" content="http://example.com" />
<title>Base package default test page</title>

<script src="%VITE_BUNDLE_PATH%" type="module"></script>

<style>
.result { margin-left: 8px; font-weight: bold; }
.result.allowed { color: green; }
.result.blocked { color: red; }
</style>
</head>

<body>
<h1>With meta tag</h1>
<a href="./ThemeRoot2.html">Reset URL</a>
<p>Following page is using the meta tag <code>sap-allowed-theme-origins</code> to specify allowed theme origins with <code>http://example.com</code> as the allowed origin.</p>
<ul>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@http://example2.com/themes/">?sap-ui-theme=sap_horizon@http://example2.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example2.com/themes/">?sap-ui-themeRoot=http://example2.com/themes/</a>
<br /> http://example2.com/ vs http://example.com/ — different origin, blocked
<br /> <button onclick="testSetThemeRoot('http://example2.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@https://example.com/themes/">?sap-ui-theme=sap_horizon@https://example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=https://example.com/themes/">?sap-ui-themeRoot=https://example.com/themes/</a>
<br /> https://example.com/ vs http://example.com/ — different protocol, blocked
<br /> <button onclick="testSetThemeRoot('https://example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@http://example:9090.com/themes/">?sap-ui-theme=sap_horizon@http://example:9090.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example:9090.com/themes/">?sap-ui-themeRoot=http://example:9090.com/themes/</a>
<br /> http://example:9090.com/ vs http://example.com/ — different port, blocked
<br /> <button onclick="testSetThemeRoot('http://example:9090.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a
href="?sap-ui-theme=sap_horizon@http://example.com/themes/">?sap-ui-theme=sap_horizon@http://example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=http://example.com/themes/">?sap-ui-themeRoot=http://example.com/themes/</a>
<br /> http://example.com/ vs http://example.com/ — matches allowed origin. <br /> Expected link element
<br /> <button onclick="testSetThemeRoot('http://example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@//example2.com/themes/">?sap-ui-theme=sap_horizon@//example2.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example2.com/themes/">?sap-ui-themeRoot=//example2.com/themes/</a>
<br /> //example2.com/ — inherits current page protocol (e.g. http://example2.com/) vs http://example.com/ — different origin, blocked
<br /> <button onclick="testSetThemeRoot('//example2.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>❌ <a
href="?sap-ui-theme=sap_horizon@//example:9090.com/themes/">?sap-ui-theme=sap_horizon@//example:9090.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example:9090.com/themes/">?sap-ui-themeRoot=//example:9090.com/themes/</a>
<br /> //example:9090.com/ — inherits current page protocol (e.g. http://example:9090.com/) vs http://example.com/ — different port, blocked
<br /> <button onclick="testSetThemeRoot('//example:9090.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a
href="?sap-ui-theme=sap_horizon@//example.com/themes/">?sap-ui-theme=sap_horizon@//example.com/themes/</a>
<br /><a href="?sap-ui-themeRoot=//example.com/themes/">?sap-ui-themeRoot=//example.com/themes/</a>
<br /> //example.com/ — inherits current page protocol (e.g. http://example.com/) vs http://example.com/ — matches allowed origin. <br /> Expected link element
<br /> <button onclick="testSetThemeRoot('//example.com/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@/themes/">?sap-ui-theme=sap_horizon@/themes/</a>
<br /><a href="?sap-ui-themeRoot=/themes/">?sap-ui-themeRoot=/themes/</a>
<br /> /themes/
<br /> (resolves to the current page's origin, e.g. http://localhost:8080/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('/themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@./themes/">?sap-ui-theme=sap_horizon@./themes/</a>
<br /><a href="?sap-ui-themeRoot=./themes/">?sap-ui-themeRoot=./themes/</a>
<br /> ./themes/
<br /> (resolves relative to the current page's URL, e.g. http://localhost:8080/test/pages/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('./themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
<li>✅ <a href="?sap-ui-theme=sap_horizon@../themes/">?sap-ui-theme=sap_horizon@../themes/</a>
<br /><a href="?sap-ui-themeRoot=../themes/">?sap-ui-themeRoot=../themes/</a>
<br /> ../themes/
<br /> (resolves relative to the current page's URL, e.g. http://localhost:8080/test/themes/) <br /> Same-origin — expected link element
<br /> <button onclick="testSetThemeRoot('../themes/', this)">Test setThemeRoot</button> <span class="result"></span>
</li>
</ul>
<script type="module">
const bundle = window["sap-ui-webcomponents-bundle"];

if (window.location.search.includes("sap-ui-theme")) {
(async () => {
await bundle.boot();
const link = document.head.querySelector('link[sap-ui-webcomponents-theme]');
if (link) {
alert(`✅ Link created: ${link.href}`);
} else {
alert("❌ Blocked — no link element created");
}
})();
}

window.testSetThemeRoot = async (themeRoot, btn) => {
if (window.location.search.includes("sap-ui-theme")) {
alert("Please remove the 'sap-ui-theme' URL parameter before testing setThemeRoot.");
return;
}

const resultEl = btn.nextElementSibling;
await bundle.configuration.setThemeRoot(themeRoot);
const link = document.head.querySelector('link[sap-ui-webcomponents-theme]');
if (link) {
resultEl.textContent = `✅ Link created: ${link.href}`;
resultEl.className = "result allowed";
} else {
resultEl.textContent = "❌ Blocked — no link element created";
resultEl.className = "result blocked";
}
};

// ❌ await bundle.configuration.setThemeRoot("http://example2.com/themes/") // different origin, blocked
// ❌ await bundle.configuration.setThemeRoot("https://example.com/themes/") // different protocol, blocked
// ❌ await bundle.configuration.setThemeRoot("http://example:9090.com/themes/") // different port, blocked
// ✅ await bundle.configuration.setThemeRoot("http://example.com/themes/") // matches allowed origin — expected link element
// ❌ await bundle.configuration.setThemeRoot("//example2.com/themes/") // inherits current page protocol (e.g. http://example2.com/) — different origin, blocked
// ❌ await bundle.configuration.setThemeRoot("//example:9090.com/themes/") // inherits current page protocol (e.g. http://example:9090.com/) — different port, blocked
// ✅ await bundle.configuration.setThemeRoot("//example.com/themes/") // inherits current page protocol (e.g. http://example.com/) — matches allowed origin — expected link element
// ✅ await bundle.configuration.setThemeRoot("/themes/") // resolves to current page's origin — same-origin — expected link element
// ✅ await bundle.configuration.setThemeRoot("./themes/") // resolves relative to current page's URL — same-origin — expected link element
// ✅ await bundle.configuration.setThemeRoot("../themes/") // resolves relative to current page's URL — same-origin — expected link element
</script>
</body>

</html>
Loading