VYPR
High severityNVD Advisory· Published Mar 11, 2026· Updated Mar 11, 2026

OpenClaw 2026.2.19-2 < 2026.2.21 - Command Injection via Newline in systemd Unit Generation

CVE-2026-32063

Description

OpenClaw version 2026.2.19-2 prior to 2026.2.21 contains a command injection vulnerability in systemd unit file generation where attacker-controlled environment values are not validated for CR/LF characters, allowing newline injection to break out of Environment= lines and inject arbitrary systemd directives. An attacker who can influence config.env.vars and trigger service install or restart can execute arbitrary commands with the privileges of the OpenClaw gateway service user.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
openclawnpm
< 2026.2.212026.2.21

Affected products

1

Patches

1
61f646c41fb4

Daemon: harden systemd unit env rendering

https://github.com/openclaw/openclawShadowFeb 20, 2026via ghsa
3 files changed · +46 5
  • CHANGELOG.md+1 0 modified
    @@ -53,6 +53,7 @@ Docs: https://docs.openclaw.ai
     
     - Discord/Gateway: handle close code 4014 (missing privileged gateway intents) without crashing the gateway. Thanks @thewilloftheshadow.
     - Security/Net: strip sensitive headers (`Authorization`, `Proxy-Authorization`, `Cookie`, `Cookie2`) on cross-origin redirects in `fetchWithSsrFGuard` to prevent credential forwarding across origin boundaries. (#20313) Thanks @afurm.
    +- Security/Systemd: reject CR/LF in systemd unit environment values and fix argument escaping so generated units cannot be injected with extra directives. Thanks @thewilloftheshadow.
     - Skills/Security: sanitize skill env overrides to block unsafe runtime injection variables and only allow sensitive keys when declared in skill metadata, with warnings for suspicious values. Thanks @thewilloftheshadow.
     - Auto-reply/Runner: emit `onAgentRunStart` only after agent lifecycle or tool activity begins (and only once per run), so fallback preflight errors no longer mark runs as started. (#21165) Thanks @shakkernerd.
     - Agents/Failover: treat non-default override runs as direct fallback-to-configured-primary (skip configured fallback chain), normalize default-model detection for provider casing/whitespace, and add regression coverage for override/auth error paths. (#18820) Thanks @Glucksberg.
    
  • src/daemon/systemd-unit.test.ts+26 0 added
    @@ -0,0 +1,26 @@
    +import { describe, expect, it } from "vitest";
    +import { buildSystemdUnit } from "./systemd-unit.js";
    +
    +describe("buildSystemdUnit", () => {
    +  it("quotes arguments with whitespace", () => {
    +    const unit = buildSystemdUnit({
    +      description: "OpenClaw Gateway",
    +      programArguments: ["/usr/bin/openclaw", "gateway", "--name", "My Bot"],
    +      environment: {},
    +    });
    +    const execStart = unit.split("\n").find((line) => line.startsWith("ExecStart="));
    +    expect(execStart).toBe('ExecStart=/usr/bin/openclaw gateway --name "My Bot"');
    +  });
    +
    +  it("rejects environment values with line breaks", () => {
    +    expect(() =>
    +      buildSystemdUnit({
    +        description: "OpenClaw Gateway",
    +        programArguments: ["/usr/bin/openclaw", "gateway", "start"],
    +        environment: {
    +          INJECT: "ok\nExecStartPre=/bin/touch /tmp/oc15789_rce",
    +        },
    +      }),
    +    ).toThrow(/CR or LF/);
    +  });
    +});
    
  • src/daemon/systemd-unit.ts+19 5 modified
    @@ -1,8 +1,17 @@
     import { splitArgsPreservingQuotes } from "./arg-split.js";
     import type { GatewayServiceRenderArgs } from "./service-types.js";
     
    +const SYSTEMD_LINE_BREAKS = /[\r\n]/;
    +
    +function assertNoSystemdLineBreaks(value: string, label: string): void {
    +  if (SYSTEMD_LINE_BREAKS.test(value)) {
    +    throw new Error(`${label} cannot contain CR or LF characters.`);
    +  }
    +}
    +
     function systemdEscapeArg(value: string): string {
    -  if (!/[\\s"\\\\]/.test(value)) {
    +  assertNoSystemdLineBreaks(value, "Systemd unit values");
    +  if (!/[\s"\\]/.test(value)) {
         return value;
       }
       return `"${value.replace(/\\\\/g, "\\\\\\\\").replace(/"/g, '\\\\"')}"`;
    @@ -18,9 +27,12 @@ function renderEnvLines(env: Record<string, string | undefined> | undefined): st
       if (entries.length === 0) {
         return [];
       }
    -  return entries.map(
    -    ([key, value]) => `Environment=${systemdEscapeArg(`${key}=${value?.trim() ?? ""}`)}`,
    -  );
    +  return entries.map(([key, value]) => {
    +    const rawValue = value ?? "";
    +    assertNoSystemdLineBreaks(key, "Systemd environment variable names");
    +    assertNoSystemdLineBreaks(rawValue, "Systemd environment variable values");
    +    return `Environment=${systemdEscapeArg(`${key}=${rawValue.trim()}`)}`;
    +  });
     }
     
     export function buildSystemdUnit({
    @@ -30,7 +42,9 @@ export function buildSystemdUnit({
       environment,
     }: GatewayServiceRenderArgs): string {
       const execStart = programArguments.map(systemdEscapeArg).join(" ");
    -  const descriptionLine = `Description=${description?.trim() || "OpenClaw Gateway"}`;
    +  const descriptionValue = description?.trim() || "OpenClaw Gateway";
    +  assertNoSystemdLineBreaks(descriptionValue, "Systemd Description");
    +  const descriptionLine = `Description=${descriptionValue}`;
       const workingDirLine = workingDirectory
         ? `WorkingDirectory=${systemdEscapeArg(workingDirectory)}`
         : null;
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.