fix(docker): validate timezone in runtime image (#116153)

This commit is contained in:
Vincent Koc
2026-07-30 09:19:01 +08:00
committed by GitHub
parent ec46d30fbf
commit ffa99ca81d
2 changed files with 80 additions and 5 deletions

View File

@@ -409,9 +409,16 @@ contains_disallowed_chars() {
[[ "$value" == *$'\n'* || "$value" == *$'\r'* || "$value" == *$'\t'* ]]
}
is_valid_timezone() {
is_valid_timezone_in_image() {
local value="$1"
[[ -e "/usr/share/zoneinfo/$value" && ! -d "/usr/share/zoneinfo/$value" ]]
docker run --rm --network none --entrypoint node "$IMAGE_NAME" -e '
const timezone = process.argv[1];
try {
new Intl.DateTimeFormat("en", { timeZone: timezone }).format(0);
} catch {
process.exit(1);
}
' "$value"
}
validate_mount_path_value() {
@@ -497,9 +504,6 @@ if [[ -n "$TIMEZONE" ]]; then
if [[ ! "$TIMEZONE" =~ ^[A-Za-z0-9/_+\-]+$ ]]; then
fail "OPENCLAW_TZ must be a valid IANA timezone string (e.g. Asia/Shanghai)."
fi
if ! is_valid_timezone "$TIMEZONE"; then
fail "OPENCLAW_TZ must match a timezone in /usr/share/zoneinfo (e.g. Asia/Shanghai)."
fi
fi
mkdir -p "$OPENCLAW_CONFIG_DIR"
@@ -782,6 +786,10 @@ else
fi
fi
if [[ -n "$TIMEZONE" ]] && ! is_valid_timezone_in_image "$TIMEZONE"; then
fail "OPENCLAW_TZ must be supported by $IMAGE_NAME (e.g. Asia/Shanghai)."
fi
# Ensure bind-mounted data directories are writable by the container's `node`
# user (uid 1000). Host-created dirs inherit the host user's uid which may
# differ, causing EACCES when the container tries to mkdir/write.

View File

@@ -71,6 +71,54 @@ function extractInstallE2eInstallerFunction(): string {
return script.slice(start, end);
}
function extractDockerTimezoneValidator(): string {
const script = readFileSync(DOCKER_SETUP_PATH, "utf8");
const match = script.match(
/(is_valid_timezone_in_image\(\) \{[\s\S]*?\n\})\n\nvalidate_mount_path_value/u,
);
if (!match) {
throw new Error("Docker timezone validator was not found");
}
return expectDefined(match[1], "Docker timezone validator capture");
}
function runDockerTimezoneValidator(timezone: string) {
const root = tempDirs.make("openclaw-docker-timezone-");
const binDir = join(root, "bin");
const dockerPath = join(binDir, "docker");
mkdirSync(binDir, { recursive: true });
writeFileSync(
dockerPath,
[
"#!/bin/bash",
"set -euo pipefail",
'while [[ "$#" -gt 0 && "$1" != "-e" ]]; do shift; done',
'exec "$HOST_NODE" "$@"',
"",
].join("\n"),
{ mode: 0o755 },
);
return spawnSync(
"bash",
[
"--noprofile",
"--norc",
"-c",
`${extractDockerTimezoneValidator()}\nIMAGE_NAME=openclaw:test\nis_valid_timezone_in_image "$TIMEZONE"`,
],
{
encoding: "utf8",
env: {
HOME: root,
HOST_NODE: process.execPath,
PATH: `${binDir}:${process.env.PATH ?? ""}`,
TIMEZONE: timezone,
},
},
);
}
function runInstallE2eInstallerFixture(params: {
curlExitCode?: number;
installTag: string;
@@ -904,6 +952,25 @@ printf 'status=%s\\n' "$status"
expect(script).not.toContain('docker pull "$IMAGE_NAME"');
});
it("validates Docker timezones against the selected image runtime", () => {
for (const timezone of ["Asia/Shanghai", "UTC", "US/Pacific"]) {
const result = runDockerTimezoneValidator(timezone);
expect(result.stderr).toBe("");
expect(result.status).toBe(0);
}
for (const timezone of ["zone.tab", "iso3166.tab", "Factory", "localtime"]) {
const result = runDockerTimezoneValidator(timezone);
expect(result.status).toBe(1);
}
const script = readFileSync(DOCKER_SETUP_PATH, "utf8");
expect(script).not.toContain("/usr/share/zoneinfo");
expect(script).toContain(
'fail "OPENCLAW_TZ must be supported by $IMAGE_NAME (e.g. Asia/Shanghai)."',
);
});
it("bounds Podman setup image pulls", () => {
const script = readFileSync(PODMAN_SETUP_PATH, "utf8");