Skip to content

fix(exec): strip invalid approval policy enums during config normalization#59112

Merged
scoootscooob merged 2 commits into
openclaw:mainfrom
openperf:fix/exec-approvals-invalid-security-silent-fallback
Apr 2, 2026
Merged

fix(exec): strip invalid approval policy enums during config normalization#59112
scoootscooob merged 2 commits into
openclaw:mainfrom
openperf:fix/exec-approvals-invalid-security-silent-fallback

Conversation

@openperf

@openperf openperf commented Apr 1, 2026

Copy link
Copy Markdown
Member

Summary

This PR fixes the malformed exec-approvals.json policy-value path from #59006.

When ~/.openclaw/exec-approvals.json contains invalid enum values such as defaults.security: "none", normalizeExecApprovals() was preserving those raw strings in memory. Downstream exec policy resolution assumes normalized values, so those invalid strings could leak into comparisons like minSecurity() / maxAsk() and produce inconsistent behavior instead of cleanly falling back to the documented defaults.

The fix is intentionally narrow:

  • validate security, ask, and askFallback while normalizing the file
  • strip invalid or malformed values to undefined
  • let the existing resolution layer fall back to built-in defaults

What Changed

  • src/infra/exec-approvals.ts
    • added a small policy sanitizer for security, ask, and askFallback
    • non-string values are treated as invalid and stripped instead of reaching the enum normalizers directly
    • kept the rest of normalizeExecApprovals() focused on structural cleanup
  • src/infra/exec-approvals-config.test.ts
    • added focused coverage for invalid defaults values, invalid agent values, non-string malformed values, and downstream fallback behavior

Scope Boundary

This PR does not change:

  • sandbox or host target selection
  • allowlist evaluation logic
  • built-in exec approval defaults
  • approval routing or channel behavior

It only ensures that malformed policy values from exec-approvals.json do not corrupt the in-memory exec approvals config.

Reproduction

  1. Set an invalid approvals policy value in ~/.openclaw/exec-approvals.json, for example:
    {
      "version": 1,
      "defaults": {
        "security": "none"
      },
      "agents": {}
    }
  2. Run exec through a path that loads the approvals file.
  3. Before this fix, the invalid string was preserved and could poison downstream policy comparisons.
  4. After this fix, the invalid value is stripped during normalization and downstream resolution falls back to the intended defaults.

Verification

  • pnpm test -- src/infra/exec-approvals-config.test.ts
  • pnpm build
  • pnpm check

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

  • Gateway
  • Core
  • Security

Linked Issue/PR

Addresses the malformed exec-approvals.json policy-value path in #59006.

@greptile-apps

greptile-apps Bot commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a real bug where invalid enum strings like "none" set in ~/.openclaw/exec-approvals.json were silently propagated through normalizeExecApprovals, causing minSecurity/maxAsk to receive unrecognized values and producing unpredictable host-routing behavior. The fix adds explicit validation of security, ask, and askFallback fields using the existing normalizeExecSecurity/normalizeExecAsk helpers for both defaults and per-agent entries — stripping invalid values so the downstream resolveExecApprovalsFromFile layer falls through to the correct built-in defaults. The test suite is thorough and covers the full round-trip through resolveExecApprovalsFromFile.

  • Core fix (src/infra/exec-approvals.ts): normalizeExecApprovals now validates enum fields before constructing the normalized file, using the same helpers already relied upon in the resolution layer.
  • Minor issue: The agentChanged comparison uses validatedSecurity !== agent.security; when agent.security is undefined, normalizeExecSecurity returns null, and null !== undefined is true. This causes every agent with absent security/ask/askFallback fields to be unnecessarily rebuilt with those keys explicitly set to undefined — functionally harmless but a false-positive in change detection. See inline comment.
  • Tests (src/infra/exec-approvals-config.test.ts): Good coverage including per-field invalid value stripping, valid value preservation, agent-level and wildcard-agent entries, and a full resolution round-trip test.

Confidence Score: 5/5

Safe to merge; the bug fix is correct and well-tested, with only a minor false-positive in change detection that has no functional impact.

The core fix is correct: invalid enum values are now stripped at normalization time, and the downstream resolution layer correctly falls through to built-in defaults. The one remaining finding (null !== undefined false-positive causing spurious agent object rebuilds) is P2 — it does not affect JSON serialization, property access semantics, or runtime behavior in any observable way. All P2 findings default to 5/5.

No files require special attention for merge, though the agentChanged comparison in src/infra/exec-approvals.ts could be tightened to avoid spurious rebuilds of unchanged agent entries.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/infra/exec-approvals.ts
Line: 308-312

Comment:
**False-positive change detection for agents with absent fields**

When an agent doesn't have `security`, `ask`, or `askFallback` set at all (i.e. `undefined`), `normalizeExecSecurity(undefined)` returns `null`, making the comparison `null !== undefined` evaluate to `true`. This marks every agent without explicit security/ask/askFallback as changed, causing a new object to be created for it with those keys explicitly present as `undefined`.

Example: an agent like `{ allowlist: [...] }` (no security fields) will have `agentChanged = true` solely because `null !== undefined`, and the rebuilt object will have `security: undefined, ask: undefined, askFallback: undefined` as explicit keys — different from the original (no keys at all).

While functionally harmless today (`JSON.stringify` drops `undefined` and property access returns `undefined` either way), this defeats the purpose of the change-detection guard and makes the returned `agents` object differ in shape from the input even for unmodified entries.

A straightforward fix is to convert `null``undefined` before comparing:

```suggestion
    const agentChanged =
      allowlist !== agent.allowlist ||
      (validatedSecurity ?? undefined) !== agent.security ||
      (validatedAsk ?? undefined) !== agent.ask ||
      (validatedAskFallback ?? undefined) !== agent.askFallback;
```

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

Reviews (1): Last reviewed commit: "fix(exec): strip invalid security/ask en..." | Re-trigger Greptile

Comment thread src/infra/exec-approvals.ts Outdated
@aisle-research-bot

aisle-research-bot Bot commented Apr 1, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

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

# Severity Title
1 🟠 High Type confusion enables unintended activation of autoAllowSkills in exec approvals policy
2 🟠 High Prototype Pollution via attacker-controlled agent keys in normalizeExecApprovals
1. 🟠 Type confusion enables unintended activation of autoAllowSkills in exec approvals policy
Property Value
Severity High
CWE CWE-840
Location src/infra/exec-approvals.ts:293-301

Description

sanitizeExecApprovalPolicy() normalizes string enum fields (security, ask, askFallback) but passes autoAllowSkills through without type validation. Downstream, resolveExecApprovalsFromFile() converts the value with Boolean(...), so any truthy non-boolean (e.g., {}, [], "true", 1) will enable autoAllowSkills.

Impact:

  • If an attacker can influence the exec-approvals JSON (or any config source feeding into it), they can enable implicit skill-bin allowlisting by supplying a truthy non-boolean value.
  • This is a policy bypass / permissions broadening risk because autoAllowSkills is security-sensitive and used to allow additional execution paths.

Vulnerable code:

function sanitizeExecApprovalPolicy(...): ExecApprovalsDefaults {
  return {
    ...,
    autoAllowSkills: policy?.autoAllowSkills,
  };
}

And later:

autoAllowSkills: Boolean(defaults.autoAllowSkills ?? fallbackAutoAllowSkills),

Recommendation

Validate/coerce autoAllowSkills to a strict boolean and avoid Boolean(...) on untrusted values.

Suggested fix:

function toBooleanOrUndefined(value: unknown): boolean | undefined {
  return typeof value === "boolean" ? value : undefined;
}

function sanitizeExecApprovalPolicy(...): ExecApprovalsDefaults {
  return {
    security: normalizeExecSecurity(toStringOrUndefined(policy?.security)) ?? undefined,
    ask: normalizeExecAsk(toStringOrUndefined(policy?.ask)) ?? undefined,
    askFallback: normalizeExecSecurity(toStringOrUndefined(policy?.askFallback)) ?? undefined,
    autoAllowSkills: toBooleanOrUndefined((policy as any)?.autoAllowSkills),
  };
}// When resolving, treat only explicit true as enabled
autoAllowSkills: (defaults.autoAllowSkills ?? fallbackAutoAllowSkills) === true,

This ensures non-boolean values cannot silently enable the feature.

2. 🟠 Prototype Pollution via attacker-controlled agent keys in normalizeExecApprovals
Property Value
Severity High
CWE CWE-1321
Location src/infra/exec-approvals.ts:314-332

Description

normalizeExecApprovals() loads file.agents from JSON (JSON.parse) and then iterates user-controlled keys, writing them into a plain object. Keys like __proto__, constructor, or prototype can mutate the prototype chain (prototype pollution).

  • Input: file.agents comes from an on-disk JSON config (see loadExecApprovals() / readExecApprovalsSnapshot()).
  • Dangerous behavior: user-controlled key is used as an object property on a normal object (agents[key] = ...).
  • Impact: a malicious config could inject properties onto Object.prototype (or otherwise alter lookup behavior), potentially affecting later logic that trusts object properties (e.g., security policy decisions), and can also cause crashes/DoS.

Vulnerable code:

for (const [key, agent] of Object.entries(agents)) {// ...
  if (agentChanged) {
    agents[key] = {
      ...agent,
      allowlist,
      security: sanitizedPolicy.security,
      ask: sanitizedPolicy.ask,
      askFallback: sanitizedPolicy.askFallback,
    };
  }
}

Recommendation

Treat agents as an untrusted dictionary:

  1. Use a null-prototype object for dictionaries and avoid dangerous keys.
  2. Reject __proto__, prototype, constructor keys (and optionally any non-^[a-zA-Z0-9._-]+$ ids).

Example fix:

const agents: Record<string, ExecApprovalsAgent> = Object.create(null);
for (const [key, agent] of Object.entries(file.agents ?? {})) {
  if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
  agents[key] = agent;
}// ... then iterate over Object.entries(agents) safely

Alternatively, store agents in a Map<string, ExecApprovalsAgent> and only convert to an object at the boundary using a safe serializer.


Analyzed PR: #59112 at commit 6f2c432

Last updated on: 2026-04-02T08:32:34Z

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aed0c0aa1c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/exec-approvals.ts Outdated
@openperf
openperf force-pushed the fix/exec-approvals-invalid-security-silent-fallback branch from aed0c0a to 1ca2d4a Compare April 1, 2026 15:53
@openperf

openperf commented Apr 1, 2026

Copy link
Copy Markdown
Member Author

All bot feedback has been addressed in the latest commits:

  1. Prototype Pollution (CWE-1321): The agents dictionary is now built using Object.create(null), and dangerous keys (__proto__, constructor, prototype) are explicitly filtered out during normalization.
  2. Type Confusion (CWE-704): Added a toStringOrUndefined helper to ensure non-string values (like numbers or booleans) in the config don't cause .trim() to throw a TypeError.
  3. Object Reconstruction: Fixed the null !== undefined comparison issue raised by Greptile, so agents without explicit security fields no longer trigger unnecessary object reconstruction.

(Note: The "DoS via null agent entry" finding from the security bot refers to a scenario where an agent value in the JSON is null rather than an object—this is a pre-existing input validation gap that predates this PR's changes. Addressing it would require a broader type guard across all agent consumers, which falls outside the scope of this config enum normalization fix.)

The CI failure on checks-windows-node-test-5 is a transient infrastructure issue (The self-hosted runner lost communication with the server), unrelated to these changes.

Ready for review.

@scoootscooob scoootscooob changed the title fix(exec): strip invalid security/ask enum values during config normalization fix(exec): strip invalid approval policy enums during config normalization Apr 2, 2026
@scoootscooob
scoootscooob merged commit a597938 into openclaw:main Apr 2, 2026
65 of 71 checks passed
ngutman pushed a commit that referenced this pull request Apr 3, 2026
…ation (#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
steipete pushed a commit to duncanita/openclaw that referenced this pull request Apr 4, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
Nachx639 pushed a commit to Nachx639/clawdbot that referenced this pull request Jun 17, 2026
…ation (openclaw#59112)

* fix(exec): strip invalid security/ask enum values during config normalization

* fix(exec): narrow invalid approvals config cleanup

---------

Co-authored-by: scoootscooob <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants