Skip to content

security(hooks): block prototype-chain traversal in webhook template getByPath#22213

Merged
vincentkoc merged 5 commits intoopenclaw:mainfrom
SleuthCo:security/hooks-getbypath-prototype-pollution
Feb 21, 2026
Merged

security(hooks): block prototype-chain traversal in webhook template getByPath#22213
vincentkoc merged 5 commits intoopenclaw:mainfrom
SleuthCo:security/hooks-getbypath-prototype-pollution

Conversation

@SleuthCo
Copy link
Contributor

@SleuthCo SleuthCo commented Feb 20, 2026

Summary

The getByPath() function in hooks-mapping.ts traverses attacker-controlled webhook payload data using arbitrary property path expressions but does not filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts:5) already blocks these exact keys for config path traversal via a BLOCKED_KEYS set, but the hooks template system was not protected with the same guard.

Vulnerability

When a webhook POST body is parsed via JSON.parse (no reviver), keys like __proto__ become own-properties on the resulting object. The getByPath function will then traverse into these attacker-injected properties if any hook mapping template references them.

Data flow:

POST /hooks/<path> → readJsonBody (JSON.parse, no reviver)
  → applyHookMappings → buildActionFromMapping
    → renderTemplate → resolveTemplateExpr → getByPath
      → current[part]  // no blocklist check

PoC

Config mapping:

hooks:
  mappings:
    - id: "custom"
      match: { path: "ingest" }
      action: "agent"
      messageTemplate: "meta: {{__proto__}}"

Request:

curl -X POST http://localhost:18789/hooks/ingest \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"__proto__": {"secret": "leaked_data"}}'

Before fix: Agent receives meta: {"secret":"leaked_data"} — attacker-injected data rendered into agent message via prototype chain property.

After fix: Agent receives meta: — blocked key returns undefined, rendered as empty string.

Fix

Add a BLOCKED_PATH_KEYS set (mirroring config-paths.ts) and check each path segment before traversal. Three test cases added covering __proto__, constructor, and prototype.

Severity

Medium (defense-in-depth). The path expressions are config-controlled (not attacker-controlled), so exploitation requires a template that references a blocked key. However, config-paths.ts already establishes that blocking these keys in path traversal is the project's security posture — this patch closes the gap in hooks-mapping.ts.

Fixes inconsistency between config-paths.ts:5 (BLOCKED_KEYS) and hooks-mapping.ts:getByPath (no blocklist).

Greptile Summary

Added prototype pollution protection to webhook template path resolution by blocking traversal into __proto__, constructor, and prototype keys in the getByPath function (hooks-mapping.ts:473-475). This mirrors the existing BLOCKED_KEYS protection in config-paths.ts:5 and prevents attacker-controlled webhook payloads from leaking data through these dangerous property names when referenced in hook mapping templates.

  • Introduced BLOCKED_PATH_KEYS constant matching the blocklist from config path traversal
  • Added early return when encountering blocked keys during path traversal
  • Three comprehensive test cases covering all three blocked keys
  • Defense-in-depth security improvement closing gap between config and webhook systems

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix is a straightforward defense-in-depth security improvement that mirrors an existing security pattern from config-paths.ts. The implementation is correct, properly placed before property access, and includes comprehensive test coverage for all three blocked keys. The change is minimal, focused, and follows established codebase conventions.
  • No files require special attention

Last reviewed commit: bafa8d1

(5/5) You can turn off certain types of comments like style here!

…getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>
@openclaw-barnacle openclaw-barnacle bot added gateway Gateway runtime size: S labels Feb 20, 2026
@vincentkoc
Copy link
Member

@greptileai review

@vincentkoc vincentkoc merged commit fe609c0 into openclaw:main Feb 21, 2026
3 checks passed
ly85206559 pushed a commit to ly85206559/openclaw that referenced this pull request Feb 21, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Hansen1018 pushed a commit to Hansen1018/openclaw that referenced this pull request Feb 21, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
mmyyfirstb pushed a commit to mmyyfirstb/openclaw that referenced this pull request Feb 21, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
clickmediapropy pushed a commit to clickmediapropy/openclaw that referenced this pull request Feb 22, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
obviyus pushed a commit to guirguispierre/openclaw that referenced this pull request Feb 22, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
mreedr pushed a commit to mreedr/openclaw-custom that referenced this pull request Feb 24, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
xianfeng92 pushed a commit to xianfeng92/openclaw that referenced this pull request Feb 24, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
(cherry picked from commit fe609c0)
jun-planfit pushed a commit to planfit/openclaw that referenced this pull request Feb 24, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 1, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
(cherry picked from commit fe609c0)

# Conflicts:
#	CHANGELOG.md
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Mar 3, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
(cherry picked from commit fe609c0)

# Conflicts:
#	CHANGELOG.md
#	src/gateway/hooks-mapping.ts
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…getByPath (openclaw#22213)

* security(hooks): block prototype-chain traversal in webhook template getByPath

The getByPath() function in hooks-mapping.ts traverses attacker-controlled
webhook payload data using arbitrary property path expressions, but does not
filter dangerous property names (__proto__, constructor, prototype).

The config-paths module (config-paths.ts) already blocks these exact keys
for config path traversal via a BLOCKED_KEYS set, but the hooks template
system was not protected with the same guard.

Add a BLOCKED_PATH_KEYS set mirroring config-paths.ts and reject traversal
into __proto__, prototype, or constructor in getByPath(). Add three test
cases covering all three blocked keys.

Signed-off-by: Alan Ross <alan@sleuthco.ai>

* test(gateway): narrow hook action type in prototype-pollution tests

* changelog: credit hooks prototype-path guard in PR 22213

* changelog: move hooks prototype-path fix into security section

---------

Signed-off-by: Alan Ross <alan@sleuthco.ai>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants