diff --git a/.gitignore b/.gitignore
index f56c925342..7e09c0d4e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,7 @@ emhttp/plugins/pnpm-lock.yaml
# api source code
unraid-api/
+unraid/
# Node.js version
.node-version
@@ -88,6 +89,9 @@ lib/node_modules
bin/
sbin/unraid-api
+# Provided by Unraid API
+emhttp/redirect.htm
+
# Unraid API readme/changelog
emhttp/plugins/dynamix.unraid.net
diff --git a/emhttp/auth-request.php b/emhttp/auth-request.php
index b1c5ebea2d..dc21578877 100644
--- a/emhttp/auth-request.php
+++ b/emhttp/auth-request.php
@@ -14,8 +14,52 @@
session_write_close();
}
-// Include JS caching functions
-require_once '/usr/local/emhttp/webGui/include/JSCache.php';
+function isPathInDocroot(string $realPath, string $docroot): bool {
+ return $realPath === $docroot || str_starts_with($realPath, $docroot . '/');
+}
+
+function getCanonicalRequestUri(string $docroot): string {
+ $requestUri = getRequestUriPath();
+
+ $realRequestPath = realpath($docroot . '/' . ltrim($requestUri, '/'));
+ if (!is_string($realRequestPath) || !isPathInDocroot($realRequestPath, $docroot)) {
+ return '';
+ }
+
+ $canonicalRequestUri = substr($realRequestPath, strlen($docroot));
+ return $canonicalRequestUri === '' ? '/' : $canonicalRequestUri;
+}
+
+function isWebComponentsRequest(string $requestUri): bool {
+ $webComponentsDirectory = '/plugins/dynamix.my.servers/unraid-components';
+ return $requestUri === $webComponentsDirectory || str_starts_with($requestUri, $webComponentsDirectory . '/');
+}
+
+function getRequestUriPath(): string {
+ $requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
+ return is_string($requestUri) ? $requestUri : '/';
+}
+
+function getAllowedExternalPublicAssetTargets(): array {
+ return [
+ '/webGui/images/case-model.png' => '/boot/config/plugins/dynamix/case-model.png',
+ ];
+}
+
+function isAllowedPublicAssetRequest(string $requestUri, string $docroot, array $arrWhitelist): bool {
+ if (!in_array($requestUri, $arrWhitelist, true)) {
+ return false;
+ }
+
+ $realRequestPath = realpath($docroot . '/' . ltrim($requestUri, '/'));
+ if (is_string($realRequestPath) && isPathInDocroot($realRequestPath, $docroot)) {
+ return true;
+ }
+
+ $allowedExternalTargets = getAllowedExternalPublicAssetTargets();
+ return isset($allowedExternalTargets[$requestUri]) &&
+ $realRequestPath === $allowedExternalTargets[$requestUri];
+}
// Base whitelist of files
$arrWhitelist = [
@@ -54,12 +98,22 @@
'/manifest.json'
];
-// Whitelist ALL files from the unraid-components directory
-$webComponentsDirectory = '/plugins/dynamix.my.servers/unraid-components/';
-$requestUri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? '/';
+// Use canonical filesystem path checks against the trusted docroot.
+$docroot = '/usr/local/emhttp';
+$requestUri = getRequestUriPath();
+$canonicalRequestUri = getCanonicalRequestUri($docroot);
+
+// Allow explicit public assets with strict target checks.
+if (isAllowedPublicAssetRequest($requestUri, $docroot, $arrWhitelist)) {
+ http_response_code(200);
+ exit;
+}
-// Check if the request is for any file in the unraid-components directory
-if (str_starts_with($requestUri, $webComponentsDirectory) || in_array($requestUri, $arrWhitelist)) {
+// Allow canonical requests under unraid-components.
+if (
+ $canonicalRequestUri !== '' &&
+ isWebComponentsRequest($canonicalRequestUri)
+) {
// authorized
http_response_code(200);
} else {
diff --git a/emhttp/languages/en_US/helptext.txt b/emhttp/languages/en_US/helptext.txt
index 08a4d856c9..e66f84d57f 100644
--- a/emhttp/languages/en_US/helptext.txt
+++ b/emhttp/languages/en_US/helptext.txt
@@ -2367,6 +2367,12 @@ Generally speaking, it is recommended to leave this setting to its default value
IMPORTANT NOTE: If adjusting port mappings, do not modify the settings for the Container port as only the Host port can be adjusted.
:end
+:docker_fixed_mac_help:
+Assigns the container's MAC address on the selected Docker network endpoint. Use a valid unicast MAC address; the first octet must be even, e.g. 02:42:9a:0d:7e:c0.
+
+This avoids using the legacy container-level --mac-address option in Extra Parameters.
+:end
+
:docker_container_network_help:
This allows your container to utilize the network configuration of another container. Select the appropriate container from the list.
This setup can be particularly beneficial if you wish to route your container's traffic through a VPN.
:end
diff --git a/emhttp/plugins/dynamix.docker.manager/DockerContainers.page b/emhttp/plugins/dynamix.docker.manager/DockerContainers.page
index 8cf6e687ca..053c6d9585 100755
--- a/emhttp/plugins/dynamix.docker.manager/DockerContainers.page
+++ b/emhttp/plugins/dynamix.docker.manager/DockerContainers.page
@@ -37,7 +37,7 @@ $cpus = cpu_list();