Skip to content

fix(hooks): reject internal session namespaces#55767

Closed
RichardCao wants to merge 1 commit into
openclaw:mainfrom
RichardCao:fix/hooks-block-internal-session-namespaces
Closed

fix(hooks): reject internal session namespaces#55767
RichardCao wants to merge 1 commit into
openclaw:mainfrom
RichardCao:fix/hooks-block-internal-session-namespaces

Conversation

@RichardCao

Copy link
Copy Markdown
Contributor

Summary

  • reject hook-provided session keys that target internal subagent:, acp:, or cron: namespaces
  • validate hooks.defaultSessionKey against the same reserved namespaces at config load time
  • add unit + server hook regressions covering request, mapping, and config paths

Why

Hook agent runs execute through the isolated cron path. If external hooks can choose an existing internal session namespace, the resulting run inherits session-key scoped control state intended only for internal subagent / ACP / cron flows.

Testing

  • pnpm exec vitest run src/gateway/hooks.test.ts src/gateway/server.hooks.test.ts src/cron/isolated-agent/run.session-key.test.ts

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: S labels Mar 27, 2026
@greptile-apps

greptile-apps Bot commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR closes a session-key namespace injection path by rejecting hook-provided session keys that target internal control namespaces (subagent:, acp:, cron:). The fix correctly strips the agent:<id>: wrapper before the reserved-prefix check, applies case-insensitive matching, validates hooks.defaultSessionKey eagerly at config-load time, and catches request- and mapping-sourced keys at dispatch time via resolveHookSessionKey.\n\nKey changes:\n- New HOOK_RESERVED_SESSION_KEY_PREFIXES constant and two private helpers (normalizeHookValidatedSessionKey, resolveReservedHookSessionKeyPrefix) in hooks.ts\n- resolveHooksConfig now throws at startup when defaultSessionKey targets a reserved namespace\n- resolveHookSessionKey rejects reserved keys for both \"request\" and \"mapping\" sources, returning a descriptive 400 error\n- Unit tests cover the new rejection paths; a server integration test confirms HTTP 400 responses and that cronIsolatedRun is never invoked\n\nOne minor note: Static mapping sessionKey entries targeting reserved namespaces are only caught at request dispatch time rather than at config-load time, unlike defaultSessionKey which throws on startup. The injection remains blocked, but a misconfigured mapping silently loads and then returns 400 on every hit instead of failing fast at startup.

Confidence Score: 5/5

Safe to merge — the security boundary is fully enforced; all reserved-namespace injection paths are blocked at or before dispatch.

All remaining findings are P2 style/consistency suggestions. The core security fix is correct, case-insensitive, and covered by unit + integration tests. No P0 or P1 issues found.

No files require special attention; the single P2 note on mapping session key validation in hooks.ts is a hardening suggestion, not a blocking issue.

Important Files Changed

Filename Overview
src/gateway/hooks.ts Adds HOOK_RESERVED_SESSION_KEY_PREFIXES and two helpers to strip the agent prefix before checking reserved namespaces. Validation applied to defaultSessionKey at config-load time and to request/mapping keys at dispatch time. Logic is correct and case-insensitive.
src/gateway/hooks.test.ts Adds two unit tests: one for resolveHookSessionKey rejecting internal namespaces across request and mapping sources, one for resolveHooksConfig rejecting a reserved defaultSessionKey. Coverage is thorough for the new paths.
src/gateway/server.hooks.test.ts Adds an integration test covering both the direct /hooks/agent request path and a mapped path with a reserved session key. Verifies HTTP 400 responses with the correct error message and that cronIsolatedRun is never called.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/hooks.ts
Line: 73-78

Comment:
**Config-load gap: mapping `sessionKey` values are not validated against reserved prefixes**

`defaultSessionKey` is now validated at config-load time (throws on startup), but static mapping `sessionKey` values with reserved namespaces (e.g. `"cron:daily"` or `"agent:main:subagent:worker"`) pass `resolveHookMappings` silently and are only caught by `resolveHookSessionKey` at request dispatch time. The security boundary is still intact — no reserved-namespace injection is possible — but a misconfigured mapping will silently load and then return HTTP 400 on every hit to that endpoint, which is harder to diagnose than a startup error.

Consider calling `resolveReservedHookSessionKeyPrefix` for each static mapping `sessionKey` inside `resolveHookMappings` and throwing early (the same pattern used for `defaultSessionKey`). Template-rendered keys (those containing `{{`) could be skipped since they can only be checked at runtime.

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

Reviews (1): Last reviewed commit: "fix(hooks): reject internal session name..." | Re-trigger Greptile

Comment thread src/gateway/hooks.ts
Comment on lines +73 to +78
const reservedDefaultSessionPrefix = resolveReservedHookSessionKeyPrefix(defaultSessionKey);
if (reservedDefaultSessionPrefix) {
throw new Error(
`hooks.defaultSessionKey may not target internal session namespace ${reservedDefaultSessionPrefix}`,
);
}

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.

P2 Config-load gap: mapping sessionKey values are not validated against reserved prefixes

defaultSessionKey is now validated at config-load time (throws on startup), but static mapping sessionKey values with reserved namespaces (e.g. "cron:daily" or "agent:main:subagent:worker") pass resolveHookMappings silently and are only caught by resolveHookSessionKey at request dispatch time. The security boundary is still intact — no reserved-namespace injection is possible — but a misconfigured mapping will silently load and then return HTTP 400 on every hit to that endpoint, which is harder to diagnose than a startup error.

Consider calling resolveReservedHookSessionKeyPrefix for each static mapping sessionKey inside resolveHookMappings and throwing early (the same pattern used for defaultSessionKey). Template-rendered keys (those containing {{) could be skipped since they can only be checked at runtime.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/hooks.ts
Line: 73-78

Comment:
**Config-load gap: mapping `sessionKey` values are not validated against reserved prefixes**

`defaultSessionKey` is now validated at config-load time (throws on startup), but static mapping `sessionKey` values with reserved namespaces (e.g. `"cron:daily"` or `"agent:main:subagent:worker"`) pass `resolveHookMappings` silently and are only caught by `resolveHookSessionKey` at request dispatch time. The security boundary is still intact — no reserved-namespace injection is possible — but a misconfigured mapping will silently load and then return HTTP 400 on every hit to that endpoint, which is harder to diagnose than a startup error.

Consider calling `resolveReservedHookSessionKeyPrefix` for each static mapping `sessionKey` inside `resolveHookMappings` and throwing early (the same pattern used for `defaultSessionKey`). Template-rendered keys (those containing `{{`) could be skipped since they can only be checked at runtime.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 37c84c6. Static hook mapping session keys now fail fast during config load when they target reserved internal namespaces, while templated keys still defer to runtime validation. I also added coverage for both the startup rejection path and the templated runtime-only path.

@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: 84f099fe81

ℹ️ 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/gateway/hook-session-key.ts Outdated
Comment on lines +15 to +17
return value;
}
value = parsed.rest;

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.

P1 Badge Trim unwrapped session segment before prefix checks

The new reserved-namespace guard can be bypassed by adding whitespace after an agent: unwrap boundary (for example agent:main: subagent:worker). parseAgentSessionKey() preserves that leading space in rest, and this function returns it unchanged, so startsWith("subagent:")/acp:/cron: does not match here even though downstream session classifiers trim and still treat it as an internal namespace. That means external hooks can still target internal control sessions despite this fix.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 783cc48. I now trim each unwrapped segment before checking reserved prefixes, so values like agent:main: subagent:worker are rejected the same way as the canonical form. Re-ran pnpm exec vitest run src/gateway/hooks.test.ts src/gateway/server.hooks.test.ts.

@clawsweeper

clawsweeper Bot commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep this PR open for review rather than cleanup: the diff is still useful security hardening for Gateway hook session routing, and I did not find a current-main implementation that already rejects the reserved namespaces. I found no blocking code-review defect, but the PR still needs real behavior proof and maintainer acceptance of the intentional compatibility/message-delivery break for existing hook configs that route into internal namespaces.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main source shows an allowlisted hook sessionKey is accepted by resolveHookSessionKey and then dispatched by the hook request handler; I did not run a live server repro in this read-only review.

Is this the best way to solve the issue?

Yes, with maintainer acceptance of the compatibility break. The PR centralizes the reserved-prefix check before config/runtime acceptance and preserves wake-mapping compatibility, but it still needs real behavior proof and upgrade context.

Security review:

Security review cleared: The diff is security-sensitive but improves the hook session-key boundary and does not add dependencies, secrets handling, downloads, workflow permissions, or new code-execution surfaces.

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-03-27T12:05:15Z, is older than 30 days, and the latest review rated it F.
  • proof blocker: real behavior proof is mock_only and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • steipete: Recent GitHub history shows repeated gateway hook and session-key-adjacent refactors, including normalization extraction, trimmed gateway hook exports, transform symlink security hardening, and hook module loading/rate-limit security work. (role: recent area contributor; confidence: high; commits: 00d8d7ead059, 236bd42bb36c, f4dd0577b055; files: src/gateway/hooks.ts, src/gateway/hooks-mapping.ts, src/sessions/session-key-utils.ts)
  • vincentkoc: History shows multiple gateway hook refactors and fixes in the affected modules, including hook import graph trimming, type-surface splitting, and target-agent session-key rebinding. (role: gateway hooks contributor; confidence: high; commits: 546edcaa03f3, ea54beb08a23, 1ca12ec8bf76; files: src/gateway/hooks.ts, src/gateway/hooks-mapping.ts, src/gateway/server/hooks-request-handler.ts)
  • pgondhi987: Merged PR history shows pgondhi987 authored the closely related templated mapping sessionKey gate that introduced the current mapping-static/mapping-templated source distinction this PR extends. (role: related security-hardening contributor; confidence: medium; commits: 5275d008ed33; files: src/gateway/hooks.ts, src/gateway/hooks-mapping.ts, src/gateway/hooks.test.ts)
  • eleqtrizit: Recent history shows eleqtrizit changed hook allowedAgentIds/default-agent enforcement across hooks.ts, hooks tests, and the server request handler shortly before this review. (role: recent adjacent contributor; confidence: medium; commits: e72621e5668e; files: src/gateway/hooks.ts, src/gateway/hooks.test.ts, src/gateway/server/hooks-request-handler.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 421ea1f45806.

@RichardCao
RichardCao force-pushed the fix/hooks-block-internal-session-namespaces branch from 783cc48 to 7fe2ae9 Compare April 30, 2026 05:51
@RichardCao

Copy link
Copy Markdown
Contributor Author

Refreshed this PR onto latest upstream/main (99950c7f1272dff6e2c34c2be45dfc5f89e62a60) and force-pushed the updated branch.

New head: 7fe2ae98b28c98e1bff8dce40fe9645c2ceea927

What changed:

  • Rebuilt the hook namespace guard on top of current main.
  • Rejects reserved internal session namespaces for hook request keys, default session keys, static/templated mapping keys, and transform-provided agent session keys.
  • Handles nested routed agent prefixes and trims unwrapped internal session segments.
  • Addressed review feedback:
    • Static mapping fail-fast now uses the same hasHookTemplateExpressions predicate as runtime source classification.
    • Test source values now use the real "mapping-static" source literal.
    • Static reserved sessionKey validation is limited to effective action: "agent" mappings so ignored wake mapping session keys remain compatible.

Review:

  • Fresh gpt-5.5 xhigh review found two issues; both were fixed.
  • Second review found a wake-mapping compatibility issue; fixed.
  • Third review found no important remaining issues.

Verification:

  • Passed: ./node_modules/.bin/vitest run --config test/vitest/vitest.gateway-core.config.ts src/gateway/hooks.test.ts src/gateway/hooks-mapping.test.ts
  • Result after final fixes: 2 files passed, 58 tests passed.
  • Passed: git diff --check upstream/main..HEAD
  • Blocked locally: src/gateway/server.hooks.test.ts failed during import because this local node_modules is missing the declared typebox package. No server-hook assertions ran in that suite.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels May 21, 2026
@RomneyDa

Copy link
Copy Markdown
Member

Heads up: this PR needs to be updated against current main before the new required Dependency Guard check can pass.

@RichardCao
RichardCao force-pushed the fix/hooks-block-internal-session-namespaces branch from 7fe2ae9 to 83725fd Compare June 1, 2026 06:58
@RichardCao
RichardCao force-pushed the fix/hooks-block-internal-session-namespaces branch from 83725fd to 61467d8 Compare June 1, 2026 12:48
@clawsweeper

clawsweeper Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gateway Gateway runtime merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: M status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants