Skip to content

fix(vnext): address security review feedback + add PR CI - #6

Open
eshwar-sundar-glean wants to merge 1 commit into
mainfrom
eshwar/vnext-review-fixes
Open

fix(vnext): address security review feedback + add PR CI#6
eshwar-sundar-glean wants to merge 1 commit into
mainfrom
eshwar/vnext-review-fixes

Conversation

@eshwar-sundar-glean

Copy link
Copy Markdown
Contributor

Summary

Addresses the review feedback from @swarup-padhi-glean on #3 (merged) — a set of security hardening fixes to the glean-vnext runtime server, plus the missing PR-triggered CI.

Fixes (mapped to review threads)

Sev Area Fix
High run-tool.ts Approval gate no longer fails open. It keyed on toolMeta?.requires_approval, so a missing/unparseable tool JSON (evicted by evictStaleSkills, called from memory without a fresh discover, or corrupt) made it falsy → the tool executed with zero approval while the native prompt was already suppressed via readOnlyHint. Now fail closed: only skip the gate when requires_approval is explicitly false.
High hooks.json, auto-approve-run-tool.mjs Hook matcher scoped to this plugin's server. mcp__.*glean.*run_tool / includes("glean") matched any server containing "glean" — including a user-connected remote glean server, whose run_tool would be auto-approved (native prompt suppressed) while glean-local's elicitation never runs for it. Now matches the exact glean-local__run_tool segment.
High (cursor hook) Already resolved — cursor's target components exclude hooks (and glean-vnext), so the auto-approve hook is claude-only. Noted for completeness.
Med auth-provider.ts OAuth state CSRF check made live. The SDK never set a state param, so expectedState was always undefined and the loopback callback's check was dead code. Generate a state nonce on the authorize URL; the server echoes it back and the callback verifies it.
Med remote-client.ts Serialize token refresh. Concurrent handlers sharing the singleton auth provider could each drive the SDK refresh with the same refresh_token; with rotation, the loser gets invalid_grantinvalidateCredentials("tokens") wipes the just-saved tokens. connect/finishAuth now run behind a shared in-flight lock, so the second reads fresh tokens and skips refresh.
Med approval-args.ts Sanitize approval-prompt argument keys. Keys were interpolated raw, so a prompt-injected arg key with newlines could forge fake lines in the elicitation message. Collapse whitespace in keys like values, and disclose hidden args with a (+N more) line.
Med skill-writer.ts Windows path fix. endsWith("/SKILL.md") dropped the SKILL.md reference on Windows (\ separator); match either separator.
Med .github/workflows/test.yml Add PR CI. publish.yml only runs on release. New workflow runs typecheck:bundle + test:bundle + npm test (build/validate) on PRs and pushes to main.

Testing

  • npm run typecheck:bundle — clean
  • npm run test:bundle194/194 (5 new: fail-closed accept/decline, arg-key injection, remote-glean-not-approved, prefixed-form matches)
  • npm test — build + validate green (cursor 62 / claude 65 / codex 56)

Open question

Replying separately on the targets/codex/.mcp.json thread re: why codex needs a per-target override.

Addresses Swarup's review on #3:

- [High] Approval gate fails open on unknown metadata (run-tool.ts): the gate
  keyed on `toolMeta?.requires_approval`, so a missing/unparseable tool JSON
  (evicted by evictStaleSkills, called from memory without a fresh discover,
  or corrupt) made it falsy and the tool ran with zero approval while the
  native prompt was already suppressed. Now fail CLOSED — only skip the gate
  when requires_approval is explicitly false.
- [High] Auto-approve hook matcher too broad (hooks.json,
  auto-approve-run-tool.mjs): `mcp__.*glean.*run_tool` / includes("glean")
  matched any *glean* server, so a user-connected REMOTE glean server's
  run_tool got auto-approved (native prompt suppressed, our elicitation never
  runs for it). Scope to the exact glean-local server segment.
- [Med] OAuth state CSRF check was dead code (auth-provider.ts): the SDK never
  set a `state` param so expectedState was always undefined. Generate a state
  nonce on the authorize URL so the loopback callback's check is live.
- [Med] Concurrent token refresh could wipe tokens (remote-client.ts):
  serialize connect/finishAuth behind a shared in-flight lock so parallel
  handlers don't race the SDK refresh and trip invalidateCredentials on
  refresh-token rotation.
- [Med] Approval-prompt line injection via argument keys (approval-args.ts):
  keys were interpolated raw. Sanitize keys like values and disclose omitted
  args with a "(+N more)" line.
- [Med] Windows SKILL.md reference dropped (skill-writer.ts): match SKILL.md
  under either path separator.
- [Med] No CI ran the suite (.github/workflows/test.yml): run typecheck:bundle
  + test:bundle + build/validate on PRs and pushes to main.

Note: [High] "hook shipped to cursor" is already resolved — cursor's target
components exclude `hooks` (and glean-vnext), so the hook is claude-only.

typecheck:bundle clean; test:bundle 194/194 (5 new); npm test 62/65/56.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant