Skip to content

[Security]: Fix security vulnerability through __proto__#34929

Closed
benediktjohannes wants to merge 1 commit intoopenclaw:mainfrom
benediktjohannes:patch-2
Closed

[Security]: Fix security vulnerability through __proto__#34929
benediktjohannes wants to merge 1 commit intoopenclaw:mainfrom
benediktjohannes:patch-2

Conversation

@benediktjohannes
Copy link
Copy Markdown
Contributor

@benediktjohannes benediktjohannes commented Mar 4, 2026

Summary

  • Problem: The helper function hasExplicitProviderAccountConfig used the in operator to check if an accountId exists in an accounts object. The in operator also traverses the prototype chain, so a specially crafted accountId like "proto" would return true even when no such account is actually configured.

  • Why it matters: This can cause the security audit to misinterpret the configuration, potentially skipping warnings for real accounts or treating non‑existent accounts as explicitly configured, which could weaken security guarantees.

  • What changed: Replaced accountId in accounts with Object.hasOwn(accounts, accountId) to ensure only the object’s own properties are considered.

What did NOT change: No behavior changes for normal account IDs.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

None.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Risk without this change: The helper function hasExplicitProviderAccountConfig (in src/security/audit-channel.ts) uses the JavaScript in operator to check whether a given accountId exists in the accounts configuration object. The in operator also traverses the object's prototype chain, meaning that if an attacker can supply a specially crafted accountId (e.g., __proto__ or constructor), the check will erroneously return true even though no such account is actually configured.

Repro + Verification

Because this is a proactive hardening fix and the exact exploit steps could be misused if published, we deliberately omit a full proof‑of‑concept.

Unforetunately, I don't have a local instance of OpenClaw installed, so I haven't tested this change, but I'm pretty confident from looking at the code that this is correct, but please correct me if I'm mistaken, thanks!

Environment

  • OS: N/A (code change only)
  • Runtime/container: N/A
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): N/A

@benediktjohannes
Copy link
Copy Markdown
Contributor Author

cc @shakkernerd could you please review, thanks! 👍

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR makes a focused, one-line security hardening change in src/security/audit-channel.ts: the hasExplicitProviderAccountConfig helper now uses Object.hasOwn(accounts, accountId) instead of the in operator to check whether an account ID exists in the configured accounts object.

The fix is correct and well-motivated. The in operator traverses the prototype chain, meaning a crafted input like "__proto__" or "constructor" would erroneously return true even with an empty accounts object, potentially causing the security audit to treat a non-existent account as explicitly configured and suppress related warnings. Object.hasOwn avoids this by only examining the object's own enumerable and non-enumerable properties. The project already targets ES2023 (lib: ["ES2023"], target: "es2023"), so Object.hasOwn is fully supported with no compatibility concerns.

Key points:

  • The fix is minimal, correct, and does not change behaviour for any legitimate account ID.
  • The codebase already applies similar prototype-pollution guards elsewhere (e.g. src/infra/prototype-keys.ts, src/gateway/hooks-mapping.ts, src/secrets/plan.ts), so this change brings audit-channel.ts into line with the established pattern.
  • No test coverage was added for the new __proto__-safe behaviour; given the existing test patterns for prototype-pollution vectors in this repo, a small unit test would be a welcome addition.

Confidence Score: 5/5

  • This PR is safe to merge — the change is a single, correct, and well-scoped security hardening fix with no behaviour change for legitimate inputs.
  • The one-line substitution (inObject.hasOwn) is unambiguously correct, Object.hasOwn is fully available in the project's ES2023 target, no existing tests are broken, and the change aligns with the existing prototype-pollution defense patterns throughout the codebase. The only minor gap is missing unit test coverage for the hardened path.
  • No files require special attention.

Last reviewed commit: 7158c11

return false;
}
return accountId in accounts;
return Object.hasOwn(accounts, accountId);
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.

Consider adding a test for the __proto__ edge case

The rest of the codebase has thorough test coverage for prototype-pollution vectors (e.g. src/config/merge-patch.proto-pollution.test.ts, src/routing/account-id.test.ts, src/config/runtime-overrides.test.ts), but there is no audit-channel.test.ts file and this PR doesn't add one. A small unit test verifying hasExplicitProviderAccountConfig returns false when accountId is "__proto__" (on an object that doesn't genuinely contain that own key) would lock in this behaviour and prevent future regressions, consistent with how the rest of the codebase covers similar vectors.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/security/audit-channel.ts
Line: 111

Comment:
**Consider adding a test for the `__proto__` edge case**

The rest of the codebase has thorough test coverage for prototype-pollution vectors (e.g. `src/config/merge-patch.proto-pollution.test.ts`, `src/routing/account-id.test.ts`, `src/config/runtime-overrides.test.ts`), but there is no `audit-channel.test.ts` file and this PR doesn't add one. A small unit test verifying `hasExplicitProviderAccountConfig` returns `false` when `accountId` is `"__proto__"` (on an object that doesn't genuinely contain that own key) would lock in this behaviour and prevent future regressions, consistent with how the rest of the codebase covers similar vectors.

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

@benediktjohannes
Copy link
Copy Markdown
Contributor Author

Check failure seems to be unrelated

@dvrshil
Copy link
Copy Markdown
Contributor

dvrshil commented Mar 5, 2026

test

@dvrshil
Copy link
Copy Markdown
Contributor

dvrshil commented Mar 5, 2026

Thanks for the work, @benediktjohannes. This issue has been addressed by maintainer-selected PR #34982 (tracking issue #34926).\n\nTo keep the queue clean, I’m closing this as a duplicate.

@dvrshil dvrshil closed this Mar 5, 2026
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.

[Security]: Use of in operator in hasExplicitProviderAccountConfig can lead to prototype pollution bypass

2 participants