mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 10:41:35 +00:00
fix(docker): validate timezone in runtime image (#116153)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user