Skip to content

fix(security): block build-tool and glibc env injection vectors in host exec sandbox#49702

Merged
ademczuk merged 5 commits intoopenclaw:mainfrom
ademczuk:fix/security-env-blocklist-round2
Mar 18, 2026
Merged

fix(security): block build-tool and glibc env injection vectors in host exec sandbox#49702
ademczuk merged 5 commits intoopenclaw:mainfrom
ademczuk:fix/security-env-blocklist-round2

Conversation

@ademczuk
Copy link
Copy Markdown
Contributor

@ademczuk ademczuk commented Mar 18, 2026

Follow-up to #49025. Extends the host exec env var blocklist with seven more injection vectors verified against official documentation.

Changes

  • src/infra/host-env-security-policy.json:
    • blockedKeys: add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS, DOTNET_ADDITIONAL_DEPS
    • blockedOverrideKeys: add GRADLE_USER_HOME (path redirect, not direct injection - follows HOME/OPENSSL_CONF pattern)
  • apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift: regenerated
  • src/infra/host-env-security.test.ts: assertions for all new entries with case-insensitive coverage

Why these vars?

Variable Attack vector List Source
GLIBC_TUNABLES Buffer overflow in glibc ld.so (CVE-2023-4911). Not caught by existing LD_ prefix. blockedKeys Qualys advisory
MAVEN_OPTS Passed verbatim as JVM flags by Maven launcher. Accepts -javaagent:. blockedKeys Maven docs
SBT_OPTS Same mechanics for Scala Build Tool. blockedKeys SBT docs
GRADLE_OPTS Same mechanics for Gradle. blockedKeys Gradle docs
ANT_OPTS Same mechanics for Apache Ant. Launcher reads it and passes to JVM directly. blockedKeys Ant manual
GRADLE_USER_HOME Redirects Gradle config dir. Auto-executes init.d/*.gradle with full Groovy/OS access. blockedOverrideKeys Gradle init scripts
DOTNET_ADDITIONAL_DEPS Loads additional .deps.json manifests, enabling assembly resolution hijack. blockedKeys dotnet/runtime design doc

Why GRADLE_USER_HOME is blockedOverrideKeys, not blockedKeys

GRADLE_USER_HOME is a path-redirect variable, not a direct code injection flag. It follows the same pattern as HOME, CURL_HOME, and OPENSSL_CONF in blockedOverrideKeys:

  • User's inherited value is trusted (their own custom Gradle cache/config)
  • Only attacker-supplied overrides are dangerous
  • Stripping it from inherited env would break CI systems and devs who rely on custom Gradle homes

The JVM-opts vars (MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS) accept arbitrary JVM flags including -javaagent:, making even inherited values a potential injection vector. They follow the NODE_OPTIONS/JAVA_TOOL_OPTIONS pattern in blockedKeys.

Not included

  • GOFLAGS / RUSTFLAGS / CARGO_HOME: build flag injection but lower severity. Tracked for a follow-up.
  • GEM_HOME / GEM_PATH: RUBYLIB and RUBYOPT already blocked.
  • JAVA_OPTS: generic JVM opts convention, read by some tools (Tomcat, Kafka) but not by the four build tool launchers. Could add in a follow-up if needed.

Regression risk

MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, and ANT_OPTS are legitimately set by Java developers for heap tuning. Agent-spawned build commands will silently lose these flags. Same trade-off already accepted for NODE_OPTIONS and JAVA_TOOL_OPTIONS (#49025).

GRADLE_USER_HOME: NO regression for inherited values (blockedOverrideKeys preserves the user's setting). Only agent-supplied overrides are blocked.

Prior art

Follow-up to #49025 (merged). Both reference #22681.

Test plan

  • pnpm test -- src/infra/host-env-security.test.ts passes (17 tests)
  • Separate isDangerousHostEnvOverrideVarName test for GRADLE_USER_HOME
  • CI green

@openclaw-barnacle openclaw-barnacle bot added app: macos App: macos size: XS maintainer Maintainer-authored PR labels Mar 18, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 18, 2026

Greptile Summary

This PR extends the host exec environment variable blocklist with seven new injection vectors (follow-up to #49025): six JVM/glibc/dotnet direct-injection vars added to blockedKeys, and GRADLE_USER_HOME added to blockedOverrideKeys to mirror the HOME/OPENSSL_CONF path-redirect pattern. The JSON policy, its generated Swift counterpart, and the test suite are all updated in sync.

Changes:

  • src/infra/host-env-security-policy.json / HostEnvSecurityPolicy.generated.swift: Correctly categorizes MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS alongside the existing JAVA_TOOL_OPTIONS group; GLIBC_TUNABLES (CVE-2023-4911) and DOTNET_ADDITIONAL_DEPS fill gaps not caught by existing prefix rules. GRADLE_USER_HOME placement in blockedOverrideKeys is well-reasoned.
  • src/infra/host-env-security.test.ts: Assertions for all new entries with case-insensitive coverage are present and correct. Two test-quality issues exist: a duplicate describe("isDangerousHostEnvOverrideVarName") block is added instead of extending the existing one, and there is no sanitizeHostExecEnv integration test exercising the "preserve inherited GRADLE_USER_HOME, drop agent override" behavior — the principal behavioral guarantee of the blockedOverrideKeys placement.
  • CHANGELOG.md: Entry is accurate and references the correct PR number.

Confidence Score: 4/5

  • Safe to merge; all policy changes are correct and the generated Swift file is in sync — two minor test quality gaps remain but do not affect runtime behavior.
  • The security policy additions are well-researched and correctly categorized. The JSON source and Swift generated file are in sync. Case-insensitive unit test coverage exists for every new entry. The score is 4 rather than 5 only because of two test-quality gaps: a duplicate describe block for isDangerousHostEnvOverrideVarName and the absence of an integration-level sanitizeHostExecEnv test that verifies the key behavioral guarantee for GRADLE_USER_HOME (inherited value preserved, override dropped).
  • src/infra/host-env-security.test.ts — duplicate describe block and missing sanitizeHostExecEnv integration test for GRADLE_USER_HOME.

Comments Outside Diff (1)

  1. src/infra/host-env-security.test.ts, line 105-141 (link)

    P2 No sanitizeHostExecEnv integration coverage for GRADLE_USER_HOME

    The core behavioral claim of placing GRADLE_USER_HOME in blockedOverrideKeys — "the user's inherited value is trusted, only an agent-supplied override is dropped" — is not exercised through sanitizeHostExecEnv. The existing test here already validates this exact pattern for HOME and ZDOTDIR, making it the natural place to extend coverage.

    Without this, a future refactor that accidentally moves GRADLE_USER_HOME from blockedOverrideKeys to blockedKeys (stripping it from baseEnv too) would silently break CI users who rely on a custom Gradle home, and the test suite would not catch it.

    Suggested addition alongside the HOME/ZDOTDIR assertions:

    // inside the "blocks PATH and dangerous override values" test or as its own case:
    const env2 = sanitizeHostExecEnv({
      baseEnv: {
        PATH: "/usr/bin:/bin",
        GRADLE_USER_HOME: "/home/user/.gradle",   // user-set; should be preserved
      },
      overrides: {
        GRADLE_USER_HOME: "/tmp/attacker-gradle", // agent override; should be dropped
      },
    });
    expect(env2.GRADLE_USER_HOME).toBe("/home/user/.gradle");
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/infra/host-env-security.test.ts
    Line: 105-141
    
    Comment:
    **No `sanitizeHostExecEnv` integration coverage for `GRADLE_USER_HOME`**
    
    The core behavioral claim of placing `GRADLE_USER_HOME` in `blockedOverrideKeys` — "the user's inherited value is trusted, only an agent-supplied override is dropped" — is not exercised through `sanitizeHostExecEnv`. The existing test here already validates this exact pattern for `HOME` and `ZDOTDIR`, making it the natural place to extend coverage.
    
    Without this, a future refactor that accidentally moves `GRADLE_USER_HOME` from `blockedOverrideKeys` to `blockedKeys` (stripping it from `baseEnv` too) would silently break CI users who rely on a custom Gradle home, and the test suite would not catch it.
    
    Suggested addition alongside the `HOME`/`ZDOTDIR` assertions:
    
    ```typescript
    // inside the "blocks PATH and dangerous override values" test or as its own case:
    const env2 = sanitizeHostExecEnv({
      baseEnv: {
        PATH: "/usr/bin:/bin",
        GRADLE_USER_HOME: "/home/user/.gradle",   // user-set; should be preserved
      },
      overrides: {
        GRADLE_USER_HOME: "/tmp/attacker-gradle", // agent override; should be dropped
      },
    });
    expect(env2.GRADLE_USER_HOME).toBe("/home/user/.gradle");
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/host-env-security.test.ts
Line: 79-84

Comment:
**Duplicate `describe` block for `isDangerousHostEnvOverrideVarName`**

A second `describe("isDangerousHostEnvOverrideVarName", ...)` block is created here (lines 79–84), but an existing comprehensive block for the same function already lives at line 212. Test runners like Vitest will execute both, so the tests pass, but having two `describe` blocks with the same name in the same file is confusing and fragmented.

The new `GRADLE_USER_HOME` assertions (both the positive case and the negative case in `isDangerousHostEnvVarName` at line 75) are better placed inside the existing block at line 212, keeping all `isDangerousHostEnvOverrideVarName` coverage in one place:

```suggestion
  it("blocks GRADLE_USER_HOME as override-only (path redirect, not direct injection)", () => {
    expect(isDangerousHostEnvOverrideVarName("GRADLE_USER_HOME")).toBe(true);
    expect(isDangerousHostEnvOverrideVarName("gradle_user_home")).toBe(true);
  });
```
…moved into the block at line 212 instead.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/infra/host-env-security.test.ts
Line: 105-141

Comment:
**No `sanitizeHostExecEnv` integration coverage for `GRADLE_USER_HOME`**

The core behavioral claim of placing `GRADLE_USER_HOME` in `blockedOverrideKeys` — "the user's inherited value is trusted, only an agent-supplied override is dropped" — is not exercised through `sanitizeHostExecEnv`. The existing test here already validates this exact pattern for `HOME` and `ZDOTDIR`, making it the natural place to extend coverage.

Without this, a future refactor that accidentally moves `GRADLE_USER_HOME` from `blockedOverrideKeys` to `blockedKeys` (stripping it from `baseEnv` too) would silently break CI users who rely on a custom Gradle home, and the test suite would not catch it.

Suggested addition alongside the `HOME`/`ZDOTDIR` assertions:

```typescript
// inside the "blocks PATH and dangerous override values" test or as its own case:
const env2 = sanitizeHostExecEnv({
  baseEnv: {
    PATH: "/usr/bin:/bin",
    GRADLE_USER_HOME: "/home/user/.gradle",   // user-set; should be preserved
  },
  overrides: {
    GRADLE_USER_HOME: "/tmp/attacker-gradle", // agent override; should be dropped
  },
});
expect(env2.GRADLE_USER_HOME).toBe("/home/user/.gradle");
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "add ANT_OPTS to bloc..."

@ademczuk ademczuk closed this Mar 18, 2026
@ademczuk ademczuk reopened this Mar 18, 2026
@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Mar 18, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Blocked override env vars (e.g., GRADLE_USER_HOME) not enforced in gateway exec path

1. 🟠 Blocked override env vars (e.g., GRADLE_USER_HOME) not enforced in gateway exec path

Property Value
Severity High
CWE CWE-74
Location src/agents/bash-tools.exec-runtime.ts:57-76

Description

src/infra/host-env-security-policy.json adds GRADLE_USER_HOME to blockedOverrideKeys, and isDangerousHostEnvOverrideVarName() correctly detects it. However, the primary gateway/local host execution path for the exec tool does not consult the override blocklist at all.

As a result, an untrusted caller that can supply params.env to the exec tool can still set blockedOverrideKeys (including the newly-added GRADLE_USER_HOME) and influence execution of otherwise-allowlisted tools.

Impact examples:

  • GRADLE_USER_HOME can redirect Gradle’s user home to an attacker-controlled directory, enabling malicious init scripts or poisoned caches/plugins depending on tooling/config.
  • The same gap applies to other existing blockedOverrideKeys (e.g., HOME, GIT_SSH_COMMAND, SSH_ASKPASS, PAGER, EDITOR, etc.), which can lead to command execution/exfiltration in downstream tools.

Vulnerable code (override blocklist is ignored):

// src/agents/bash-tools.exec-runtime.ts
export function validateHostEnv(env: Record<string, string>): void {
  for (const key of Object.keys(env)) {
    const upperKey = key.toUpperCase();
    if (isDangerousHostEnvVarName(upperKey)) { /* ... */ }
    if (upperKey === "PATH") { /* ... */ }
  }
}

And this validation is the only guard before merging user-provided env overrides for host execution:

// src/agents/bash-tools.exec.ts
if (host !== "sandbox" && params.env) {
  validateHostEnv(params.env);
}
const mergedEnv = params.env ? { ...baseEnv, ...params.env } : baseEnv;

So GRADLE_USER_HOME (and other override-blocked keys) is accepted and propagated.

Recommendation

Enforce the override blocklist for host execution paths that accept user-supplied env (e.g., tools.exec on gateway).

Option A (minimal change): extend validateHostEnv to reject override-blocked keys/prefixes:

import {
  isDangerousHostEnvVarName,
  isDangerousHostEnvOverrideVarName,
} from "../infra/host-env-security.js";

export function validateHostEnv(env: Record<string, string>): void {
  for (const rawKey of Object.keys(env)) {
    const key = rawKey.trim();
    const upper = key.toUpperCase();

    if (isDangerousHostEnvVarName(upper) || isDangerousHostEnvOverrideVarName(upper)) {
      throw new Error(`Security Violation: Environment variable '${rawKey}' is forbidden.`);
    }

    if (upper === "PATH") {
      throw new Error("Security Violation: Custom 'PATH' variable is forbidden during host execution.");
    }
  }
}

Option B (preferred): reuse the centralized sanitizer for consistency:

  • Build the execution env via sanitizeHostExecEnv({ baseEnv: process.env, overrides: params.env, blockPathOverrides: true }).

Also add regression tests ensuring tools.exec rejects/strips keys from blockedOverrideKeys (including GRADLE_USER_HOME).


Analyzed PR: #49702 at commit e2508ac

Last updated on: 2026-03-18T12:10:48Z

@ademczuk ademczuk self-assigned this Mar 18, 2026
@ademczuk ademczuk closed this Mar 18, 2026
@ademczuk ademczuk reopened this Mar 18, 2026
minupla pushed a commit to minupla/openclaw that referenced this pull request Mar 18, 2026
…vectors in host exec

Adds GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS to
blockedKeys, and GRADLE_USER_HOME to blockedOverrideKeys.

Source: openclaw#49702
@ademczuk
Copy link
Copy Markdown
Contributor Author

CI note: the three persistent failures (contracts, channels, extensions) are pre-existing across all open PRs right now - confirmed on #48805, #48804, #48851 which have the same TS1360 type errors in channelContentConfig.ts. Nothing to do with this PR's JSON/Swift/test changes.

All code-relevant checks pass: check (lint/typecheck), both test shards, bun test, startup-memory, secrets, check-docs.

@ademczuk ademczuk merged commit 089a43f into openclaw:main Mar 18, 2026
31 of 41 checks passed
mrosmarin added a commit to mrosmarin/openclaw that referenced this pull request Mar 18, 2026
* main: (230 commits)
  fix llm-task invalid thinking timeout
  Build: narrow tsdown unresolved import guard
  Plugins: sync contract registry image providers
  Build: fail on unresolved tsdown imports
  Deps: align pi-agent-core for declaration builds
  Build: fail on plugin SDK declaration errors
  Release: add plugin npm publish workflow (openclaw#47678)
  fix(security): block build-tool and glibc env injection vectors in host exec sandbox (openclaw#49702)
  test simplify zero-state boundary guards
  ci enforce boundary guardrails
  test: enable vmForks for targeted channel test runs
  test(telegram): fix incomplete sticker-cache mocks in tests
  Config: align model compat thinking format types
  Tlon: pin api-beta to current known-good commit
  Plugin SDK: harden provider auth seams
  fix(config): add missing qwen-chat-template to thinking format schema
  Plugin SDK: register provider auth login entrypoint
  Plugin SDK: split provider auth login seam
  fix: serialize duplicate channel starts (openclaw#49583) (thanks @sudie-codes)
  Telegram: fix reply-runtime test typings
  ...
analysoor-assistant pushed a commit to analysoor-assistant/openclaw that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681

(cherry picked from commit 089a43f)
analysoor-assistant pushed a commit to analysoor-assistant/openclaw that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681

(cherry picked from commit 089a43f)
livingghost pushed a commit to livingghost/openclaw that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
livingghost pushed a commit to livingghost/openclaw that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
livingghost pushed a commit to livingghost/openclaw that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
ssfdre38 pushed a commit to ssfdre38/openclaw-community-edition that referenced this pull request Mar 18, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
@ademczuk ademczuk deleted the fix/security-env-blocklist-round2 branch March 19, 2026 07:47
brandontyler pushed a commit to brandontyler/clawdbot that referenced this pull request Mar 19, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
fuller-stack-dev pushed a commit to fuller-stack-dev/openclaw that referenced this pull request Mar 20, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
fuller-stack-dev pushed a commit to fuller-stack-dev/openclaw that referenced this pull request Mar 20, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
pholpaphankorn pushed a commit to pholpaphankorn/openclaw that referenced this pull request Mar 22, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 23, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681

(cherry picked from commit 089a43f)
alexey-pelykh pushed a commit to remoteclaw/remoteclaw that referenced this pull request Mar 23, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681

(cherry picked from commit 089a43f)
Interstellar-code pushed a commit to Interstellar-code/operator1 that referenced this pull request Mar 24, 2026
…st exec sandbox (openclaw#49702)

Add GLIBC_TUNABLES, MAVEN_OPTS, SBT_OPTS, GRADLE_OPTS, ANT_OPTS,
DOTNET_ADDITIONAL_DEPS to blockedKeys and GRADLE_USER_HOME to
blockedOverrideKeys in the host exec security policy.

Closes openclaw#22681

(cherry picked from commit 089a43f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos maintainer Maintainer-authored PR size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant