Skip to content

security: require explicit confirmation for all live Steam write tools (0.9.0) - #21

Merged
TMHSDigital merged 11 commits into
mainfrom
security/confirm-gate-write-tools
Sep 18, 2026
Merged

TMHSDigital merged 11 commits into
mainfrom
security/confirm-gate-write-tools

Conversation

@TMHSDigital

Copy link
Copy Markdown
Owner

Summary

Five tools in the default steam-mcp bin POSTed to the live Steam Partner Web API as soon as they were invoked. There was no dry-run default, no confirmation flag, and no code path that could refuse an unconfirmed call.

The repo already gated Partner-admin image and trailer uploads behind refuseIfUnconfirmed plus a separate process requiring STEAM_PARTNER_ADMIN=1. The five default-bin write tools had neither layer.

Two read tools returned Steam user-authored text into agent context without labeling it as untrusted.

Before / after

Before (0.8.0): steam_uploadStoreImage and steam_uploadTrailer required dry_run / confirm and were not in the default bin. steam_grantInventoryItem, steam_setAchievement, steam_clearAchievement, steam_uploadLeaderboardScore, and steam_updateWorkshopItem sent immediately.

After (0.9.0): those five tools use the shared gate in src/utils/confirm.ts. Default is dry_run: true. A live call without confirm: true returns [CONFIRM_REQUIRED] and sends nothing. Dry-run runs before requireApiKey().

Breaking change

Write tools no-op by default. Callers that invoked them with no flags previously sent a live POST. They now receive a dry-run plan.

Old call:

{ "appid": 480, "steamid": "76561197960435530", "achievement": "ACH_WIN_ONE_GAME" }

New call (sends for real):

{ "appid": 480, "steamid": "76561197960435530", "achievement": "ACH_WIN_ONE_GAME", "dry_run": false, "confirm": true }

Credit

Reported by Syed Anas Mohiuddin, Independent Researcher, Maintainer of mcp-safeguard (https://github.com/SyedAnas01/mcp-safeguard )

Test plan

  • npm run build
  • npm test (93 passed)
  • confirm/dry_run present on all five write tools
  • mutation POSTs in src/tools/ pass through refuseIfUnconfirmed before steamPartnerPost / live fetch

Manual follow-up

  • Enable GitHub private vulnerability reporting (repo setting, not a file)
  • Publish the Security Advisory (draft at .github/SECURITY_ADVISORY_DRAFT.md)
  • Publish 0.9.0 to npm
  • Send the PR link to the reporter

Made with Cursor

TMHSDigital and others added 9 commits September 17, 2026 17:38
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…gelog

Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@TMHSDigital

Copy link
Copy Markdown
Owner Author

Follow-up on this branch:

  • Credit is name-only (no URL) in SECURITY.md, CHANGELOG.md, and the advisory draft.
  • steam_getWorkshopItem now labels untrusted title/description with _warning, same class as reviews/workshop search. Still ungated: that POST is a Steam read endpoint, not a mutation.
  • README security model names all three untrusted-content paths and states that the label is defense in depth; the confirm gate is the control.

Partner-admin refusals still return { ok: false, error } without isError / [CONFIRM_REQUIRED]. Filed separately: #22

TMHSDigital and others added 2 commits September 17, 2026 18:02
…tNewsForApp

Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@TMHSDigital
TMHSDigital merged commit 86a128d into main Sep 18, 2026
6 checks passed
@TMHSDigital
TMHSDigital deleted the security/confirm-gate-write-tools branch September 18, 2026 11:16
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