Skip to main content

performing-oauth-scope-minimization-review

Performs OAuth 2.0 scope minimization review to identify over-permissioned third-party application integrations, excessive API scopes, unused token grants, and risky OAuth consent patterns across identity providers and SaaS platforms. Activates for requests involving OAuth scope audit, API permission review, third-party app risk assessment, or consent grant minimization.

Aller à l'installation

Informations de source

Dépôt
Njones17/AI-agent-master-cyber-skills-list
Dernière activité de la source
6 mars 2026 à 16:13
Langue détectée de SKILL.md
anglais
Étoiles
23
Forks
6

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
performing-oauth-scope-minimization-review
description
Performs OAuth 2.0 scope minimization review to identify over-permissioned third-party application integrations, excessive API scopes, unused token grants, and risky OAuth consent patterns across identity providers and SaaS platforms. Activates for requests involving OAuth scope audit, API permission review, third-party app risk assessment, or consent grant minimization.
domain
cybersecurity
subdomain
identity-access-management
tags
["OAuth","scope-minimization","API-security","consent-review","third-party-risk","token-audit"]
version
1.0
author
mahipal
license
MIT
# Performing OAuth Scope Minimization Review ## When to Use - Annual or quarterly review of third-party application OAuth permissions - After a security incident involving compromised OAuth tokens or unauthorized data access - Compliance audit requiring documentation of third-party data access (GDPR Article 28, SOC 2) - Discovery of shadow IT applications accessing organizational data via OAuth grants - Migration or consolidation of SaaS applications requiring permission cleanup - Implementing least-privilege principle for API integrations **Do not use** for reviewing first-party application permissions within the same trust boundary; OAuth scope minimization focuses on third-party and cross-boundary consent grants. ## Prerequisites - Admin access to identity providers (Microsoft Entra ID, Okta, Google Workspace) - Microsoft Graph API permissions: Application.Read.All, OAuth2PermissionGrant.ReadWrite.All - Inventory of approved third-party integrations from procurement or IT governance - OAuth scope risk classification framework - Tools for token analysis (jwt.io for manual review, automated scripts for bulk analysis) ## Workflow ### Step 1: Inventory All OAuth Grants and Consent Permissions Enumerate all OAuth application registrations and delegated permissions: ```python """ OAuth Grant Inventory - Microsoft Entra ID Enumerates all application registrations, service principals, and delegated/application permission grants. """ import requests import json from collections import defaultdict class EntraOAuthAuditor: def __init__(self, tenant_id, client_id, client_secret): self.tenant_id = tenant_id self.base_url = "https://graph.microsoft.com/v1.0" self.token = self._get_token(client_id, client_secret) self.headers = {"Authorization": f"Bearer {self.token}"} def _get_token(self, client_id, client_secret): url = f"https://login.microsoftonline.com/{self.tenant_id}/oauth2/v2.0/token" response = requests.post(url, data={ "grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "scope": "https://graph.microsoft.com/.default" }) return response.json()["access_token"] def get_all_service_principals(self): """Get all service principals (enterprise applications).""" apps = [] url = f"{self.base_url}/servicePrincipals?$top=999&$select=id,appId,displayName,appOwnerOrganizationId,servicePrincipalType,accountEnabled,createdDateTime" while url: response = requests.get(url, headers=self.headers) data = response.json() apps.extend(data.get("value", [])) url = data.get("@odata.nextLink") return apps def get_oauth2_permission_grants(self): """Get all delegated permission grants (user consent).""" grants = [] url = f"{self.base_url}/oauth2PermissionGrants?$top=999" while url: response = requests.get(url, headers=self.headers) data = response.json() grants.extend(data.get("value", [])) url = data.get("@odata.nextLink") return grants def get_app_role_assignments(self, sp_id): """Get application permission assignments for a service principal.""" url = f"{self.base_url}/servicePrincipals/{sp_id}/appRoleAssignments" response = requests.get(url, headers=self.headers) return response.json().get("value", []) def build_permission_inventory(self): """Build comprehensive OAuth permission inventory.""" service_principals = self.get_all_service_principals() delegated_grants = self.get_oauth2_permission_grants() # Map service principal IDs to names sp_map = {sp["id"]: sp for sp in service_principals} inventory = [] # Process delegated permissions for grant in delegated_grants: sp = sp_map.get(grant["clientId"], {}) scopes = grant.get("scope", "").split() for scope in scopes: if not scope: continue inventory.append({ "app_name": sp.get("displayName", "Unknown"), "app_id": grant.get("clientId"), "publisher_tenant": sp.get("appOwnerOrganizationId"), "is_third_party": sp.get("appOwnerOrganizationId") != self.tenant_id, "permission_type": "Delegated", "scope": scope, "consent_type": grant.get("consentType"), # AllPrincipals or Principal "principal_id": grant.get("principalId"), "granted_date": sp.get("createdDateTime"), "is_enabled": sp.get("accountEnabled", True) }) # Process application permissions for sp in service_principals: app_roles = self.get_app_role_assignments(sp["id"]) for role in app_roles: inventory.append({ "app_name": sp.get("displayName"), "app_id": sp.get("id"), "publisher_tenant": sp.get("appOwnerOrganizationId"), "is_third_party": sp.get("appOwnerOrganizationId") != self.tenant_id, "permission_type": "Application", "scope": role.get("appRoleId"), "consent_type": "AdminConsent", "granted_date": role.get("createdDateTime"), "is_enabled": sp.get("accountEnabled", True) }) return inventory ``` ### Step 2: Classify OAuth Scopes by Risk Level Categorize permissions based on data access sensitivity: ```python """ OAuth Scope Risk Classification Maps API scopes to risk levels based on data sensitivity and access breadth. """ MICROSOFT_GRAPH_SCOPE_RISK = { # CRITICAL - Full administrative or unrestricted access "critical": { "scopes": [ "Directory.ReadWrite.All", "RoleManagement.ReadWrite.Directory", "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All", "Mail.ReadWrite", "Mail.Send", "Files.ReadWrite.All", "Sites.FullControl.All", "User.ReadWrite.All", "Group.ReadWrite.All", "MailboxSettings.ReadWrite", "full_access_as_app", ], "risk_description": "Can read/write all data, modify directory, or impersonate users", "review_frequency": "Monthly", "requires_admin_consent": True }, # HIGH - Broad read access or sensitive data write "high": { "scopes": [ "Mail.Read", "Mail.Read.Shared", "Calendars.ReadWrite", "Contacts.ReadWrite", "Files.Read.All", "Sites.Read.All", "User.Read.All", "Group.Read.All", "Directory.Read.All", "AuditLog.Read.All", "SecurityEvents.ReadWrite.All", "TeamSettings.ReadWrite.All", ], "risk_description": "Broad read access to sensitive organizational data", "review_frequency": "Quarterly", "requires_admin_consent": True }, # MEDIUM - Scoped data access "medium": { "scopes": [ "Calendars.Read", "Contacts.Read", "Files.ReadWrite", "Sites.ReadWrite.All", "Tasks.ReadWrite", "Notes.ReadWrite.All", "Chat.ReadWrite", "ChannelMessage.Send", "Team.ReadBasic.All", ], "risk_description": "Scoped access to specific data types with write capability", "review_frequency": "Semi-annually" }, # LOW - Minimal or user-profile only access "low": { "scopes": [ "User.Read", "openid", "profile", "email", "offline_access", "Calendars.Read.Shared", "People.Read", "User.ReadBasic.All", ], "risk_description": "Basic user profile or minimal scoped access", "review_frequency": "Annually" } } def classify_scope_risk(scope): """Classify a single OAuth scope by risk level.""" for risk_level, config in MICROSOFT_GRAPH_SCOPE_RISK.items(): if scope in config["scopes"]: return { "scope": scope, "risk_level": risk_level, "description": config["risk_description"], "review_frequency": config["review_frequency"] } # Unknown scopes default to HIGH risk return { "scope": scope, "risk_level": "high", "description": "Unknown scope - requires manual classification", "review_frequency": "Quarterly" } def analyze_app_risk(app_permissions): """Calculate aggregate risk score for an application's permissions.""" risk_weights = {"critical": 40, "high": 20, "medium": 10, "low": 2} total_score = 0 classified_scopes = [] for perm in app_permissions: classification = classify_scope_risk(perm["scope"]) classified_scopes.append(classification) total_score += risk_weights.get(classification["risk_level"], 10) # Bonus risk for application (vs delegated) permissions app_type_permissions = [p for p in app_permissions if p["permission_type"] == "Application"] total_score += len(app_type_permissions) * 15 # Bonus risk for admin-consented broad access admin_consent = [p for p in app_permissions if p["consent_type"] == "AllPrincipals"] total_score += len(admin_consent) * 10 if total_score >= 100: aggregate_risk = "CRITICAL" elif total_score >= 60: aggregate_risk = "HIGH" elif total_score >= 30: aggregate_risk = "MEDIUM" else: aggregate_risk = "LOW" return { "total_score": total_score, "aggregate_risk": aggregate_risk, "scope_count": len(app_permissions), "critical_scopes": len([s for s in classified_scopes if s["risk_level"] == "critical"]), "high_scopes": len([s for s in classified_scopes if s["risk_level"] == "high"]), "classified_scopes": classified_scopes } ``` ### Step 3: Identify Over-Permissioned Applications Detect apps requesting more permissions than functionally needed: ```python """ Over-Permission Detection Identifies applications with excessive OAuth scopes relative to their function. """ def detect_over_permissions(inventory, approved_apps_catalog): """ Compare actual permissions against approved scope catalog to find over-permissioned applications. """ findings = [] # Group permissions by application app_permissions = defaultdict(list) for perm in inventory: app_permissions[perm["app_name"]].append(perm) for app_name, permissions in app_permissions.items(): # Check against approved catalog approved = approved_apps_catalog.get(app_name) if not approved: # Unknown/unapproved application findings.append({ "app_name": app_name, "finding_type": "UNAPPROVED_APPLICATION", "severity": "HIGH", "detail": f"Application not in approved catalog with {len(permissions)} permission grants", "scopes": [p["scope"] for p in permissions], "recommendation": "Review and approve or revoke all permissions" }) continue approved_scopes = set(approved.get("approved_scopes", [])) actual_scopes = set(p["scope"] for p in permissions) # Find excessive scopes (granted but not approved) excessive = actual_scopes - approved_scopes if excessive: risk = analyze_app_risk([p for p in permissions if p["scope"] in excessive]) findings.append({ "app_name": app_name, "finding_type": "EXCESSIVE_SCOPES", "severity": risk["aggregate_risk"], "detail": f"{len(excessive)} scopes beyond approved list", "excessive_scopes": list(excessive), "approved_scopes": list(approved_scopes), "recommendation": "Remove excessive scopes or update approved catalog" }) # Find unused scopes (approved but activity logs show no API calls) # This requires API activity log correlation unused = approved_scopes - actual_scopes if unused: findings.append({ "app_name": app_name, "finding_type": "UNUSED_APPROVED_SCOPES", "severity": "LOW", "detail": f"{len(unused)} approved scopes not currently granted", "unused_scopes": list(unused) }) # Check for overly broad permissions broad_patterns = [ ("Mail.ReadWrite", "Mail.Read", "Write access to mail when only read needed"), ("Files.ReadWrite.All", "Files.Read.All", "Write access to all files when only read needed"), ("Directory.ReadWrite.All", "Directory.Read.All", "Write access to directory when only read needed"), ("User.ReadWrite.All", "User.Read.All", "Write access to users when only read needed"), ] for broad, narrow, description in broad_patterns: if broad in actual_scopes: findings.append({ "app_name": app_name, "finding_type": "OVERLY_BROAD_SCOPE",
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub