MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
MCPSafe.io

Security checks for MCP servers — public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany 🇩🇪

© 2026 MCPSafe. All rights reserved.

GDPR — Privacy Policy
← Threat Catalog

Configuration & Environment

OAuth Scope Over-Provisioning

MEDIUMCWE: CWE-250Rule: MCP-204

Declaring overly broad OAuth scopes (e.g., `repo`, `admin:org`) when only read operations are invoked grants unnecessary write and admin privileges that an attacker can exploit through a compromised token.

What it is

Over-provisioned OAuth scopes occur when a client is configured with permissions far exceeding what the code actually exercises. In the GitHub OAuth model, `repo` grants full read/write control over private repositories, while `admin:org` grants complete organization administration — both far exceed what is needed for operations like `issues.listForRepo` or `repos.get`. This violates the principle of least privilege at the API authorization layer.

Why it matters for MCP

MCP servers wire OAuth-authenticated API clients directly to LLM-driven tool invocations, meaning a single prompt injection or tool-chaining attack that hijacks execution can immediately leverage whatever scopes are provisioned — with no human approval step between the LLM decision and the API call. Traditional web APIs are typically invoked by human-initiated requests with narrow, purpose-built tokens, while MCP tools are composed dynamically by models that may be manipulated into invoking destructive operations the developer never anticipated. A leaked or exfiltrated token from an MCP server process therefore carries the full blast radius of its declared scopes.

Vulnerable example

example.js
1
import { Octokit } from '@octokit/rest';
2
3
export function buildClient(token: string) {
4
  return new Octokit({
5
    auth: token,
6
    scopes: ['repo', 'admin:org'],
7
  });
8
}
9
10
export async function listIssues(octokit: Octokit, owner: string, repo: string) {
11
  const issues = await octokit.rest.issues.listForRepo({ owner, repo });
12
  return issues.data.map((i) => ({ number: i.number, title: i.title }));
13
}

Secure example

example.js
1
import { Octokit } from '@octokit/rest';
2
3
export function buildClient(token: string) {
4
  return new Octokit({
5
    auth: token,
6
    scopes: ['public_repo', 'read:org'],
7
  });
8
}
9
10
export async function listIssues(octokit: Octokit, owner: string, repo: string) {
11
  const issues = await octokit.rest.issues.listForRepo({ owner, repo });
12
  return issues.data.map((i) => ({ number: i.number, title: i.title }));
13
}

How MCPSafe detects this

MCPSafe performs same-file taint analysis pairing scope literal arrays in Octokit constructor calls (or equivalent Google API auth config objects) against the set of SDK method calls reachable within the file; if `repo` or `admin:org` appear in the scope array but no write or admin SDK methods (e.g., `issues.create`, `repos.delete`, `orgs.setMembershipForUser`) are invoked, the rule fires. Negative fixtures using already-minimal scopes (`public_repo`, `read:org`, `repo:status`) are explicitly excluded from matching.

See the full threat catalog for every documented detection.

Further reading

  • GitHub OAuth Scopes Reference
  • CWE-250: Execution with Unnecessary Privileges
  • OWASP: Principle of Least Privilege
  • Google OAuth 2.0 Scopes — Incremental Authorization

Scan an MCP server for this issue

MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.

Scan now