fix(status): avoid false shell-wrapper audit warnings (#81778)

This commit is contained in:
Andi Liao
2026-07-11 18:12:32 +08:00
committed by GitHub
parent 5e8a534fad
commit 410e6db74c
3 changed files with 59 additions and 1 deletions
+1
View File
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- **Gateway service audit:** treat POSIX shell `-c` wrappers as opaque for the gateway-subcommand check, avoiding false missing-command warnings for shell-wrapped macOS LaunchAgents without parsing inner commands or ports. Fixes #81751. (#81778) Thanks @liaoandi.
- **Outbound channel bootstrap:** suppress repeated failed plugin activation for the same channel, config, and registry generation while retrying after config or registry reloads. (#100377) Thanks @xialonglee.
- **OpenAI Realtime client-secret deadlines:** bound voice and transcription secret acquisition to 30 seconds through the guarded fetch boundary while preserving authentication and bounded response parsing. (#102860) Thanks @Alix-007.
- **Gateway client watchdog:** keep transport-stall detection active for unbounded and mixed pending requests so dead sockets reject pending requests, reconnect, and never replay rejected requests. (#103407) Thanks @NianJiuZst.
+36
View File
@@ -343,6 +343,42 @@ describe("auditGatewayServiceConfig", () => {
).toBe(false);
});
it("treats zsh -lc LaunchAgent commands as opaque for the gateway token audit", async () => {
const audit = await auditGatewayServiceConfig({
env: { HOME: "/tmp" },
platform: "darwin",
expectedPort: 18889,
command: {
programArguments: [
"/bin/zsh",
"-lc",
"exec /usr/bin/node /opt/openclaw/dist/index.js gateway --port 18890",
],
environment: {},
},
});
expect(hasIssue(audit, SERVICE_AUDIT_CODES.gatewayCommandMissing)).toBe(false);
expect(hasIssue(audit, SERVICE_AUDIT_CODES.gatewayPortMismatch)).toBe(false);
expect(hasIssue(audit, SERVICE_AUDIT_CODES.gatewayPathMissing)).toBe(true);
});
it.each([
["non-shell command", ["/usr/local/bin/helper", "-lc", "exec node gateway"]],
["shell without an inline-command flag", ["/bin/zsh", "-l", "exec node gateway"]],
])("keeps exact gateway token audit for %s", async (_name, programArguments) => {
const audit = await auditGatewayServiceConfig({
env: { HOME: "/tmp" },
platform: "darwin",
command: {
programArguments,
environment: {},
},
});
expect(hasIssue(audit, SERVICE_AUDIT_CODES.gatewayCommandMissing)).toBe(true);
});
it("flags gateway service port drift from the expected config port", async () => {
const audit = await auditGatewayServiceConfig({
env: { HOME: "/tmp" },
+22 -1
View File
@@ -10,6 +10,8 @@ import {
sortUniqueStrings,
} from "@openclaw/normalization-core/string-normalization";
import { normalizeEnvVarKey } from "../infra/host-env-security.js";
import { resolveInlineCommandMatch } from "../infra/shell-inline-command.js";
import { POSIX_SHELL_WRAPPERS } from "../infra/shell-wrapper-resolution.js";
import { parseTcpPort } from "../infra/tcp-port.js";
import { VERSION } from "../version.js";
import { resolveLaunchAgentPlistPath } from "./launchd.js";
@@ -87,6 +89,22 @@ function hasGatewaySubcommand(programArguments?: string[]): boolean {
return Boolean(programArguments?.some((arg) => arg === "gateway"));
}
const POSIX_SERVICE_INLINE_COMMAND_FLAGS = new Set(["-c"]);
const POSIX_SERVICE_SHELL_WRAPPERS: ReadonlySet<string> = POSIX_SHELL_WRAPPERS;
function isOpaquePosixShellInlineCommand(programArguments: string[]): boolean {
const executable = programArguments[0]?.trim();
const shellName = executable ? path.posix.basename(executable).toLowerCase() : "";
if (!POSIX_SERVICE_SHELL_WRAPPERS.has(shellName)) {
return false;
}
return (
resolveInlineCommandMatch(programArguments, POSIX_SERVICE_INLINE_COMMAND_FLAGS, {
allowCombinedC: true,
}).command !== null
);
}
function parseSystemdUnit(content: string): {
after: Set<string>;
wants: Set<string>;
@@ -249,7 +267,10 @@ function auditGatewayCommand(programArguments: string[] | undefined, issues: Ser
if (!programArguments || programArguments.length === 0) {
return;
}
if (!hasGatewaySubcommand(programArguments)) {
if (
!hasGatewaySubcommand(programArguments) &&
!isOpaquePosixShellInlineCommand(programArguments)
) {
issues.push({
code: SERVICE_AUDIT_CODES.gatewayCommandMissing,
message: "Service command does not include the gateway subcommand",