SkillSpector MCP Least Privilege Analysis: Detecting Permission Issues in AI Skills

NVIDIA SkillSpector's MCP least privilege analyzer cross-references declared skill permissions against actual code capabilities using four detection rules (LP1-LP4) to enforce exact permission boundaries.

The mcp_least_privilege node in the NVIDIA/SkillSpector repository implements a static analysis engine that evaluates AI skills against the MCP specification's section B.3.1. This analyzer detects when skills request excessive permissions or fail to declare required capabilities, generating SARIF-compatible findings for CI pipelines.

How the MCP Least Privilege Analyzer Works

The analyzer operates in src/skillspector/nodes/analyzers/mcp_least_privilege.py by comparing manifest-declared permissions against capabilities detected in executable code. It receives state["manifest"], state["file_cache"], and component metadata, then applies regex-based detection across six capability categories.

The Four LP Detection Rules

The implementation evaluates four specific violation patterns defined in the MCP specification:

Rule Violation Detected Trigger Condition
LP1 Under-declared capability Code uses a capability not listed in permissions or allowed-tools
LP2 Wildcard permission Manifest contains *, all, full, or any entries
LP3 No permissions declared Capabilities detected but no permissions or allowed-tools defined
LP4 Over-declared permission Permission declared but no corresponding code capability found

The analyzer maps permission strings to capability categories via _PERM_TO_CAPABILITY (lines 92-112) and normalizes allowed-tools entries through _normalize_allowed_tools, converting them to capability categories using _TOOL_TO_CAPABILITY (lines 165-181).

Rule-by-Rule Detection Examples

LP1: Under-Declared Capabilities

The analyzer flags capabilities exercised in code but absent from the manifest. Detection occurs via _detect_capabilities, which applies regex patterns from _CAPABILITY_PATTERNS to identify shell (subprocess, Popen, os.system), network (httpx, requests, urllib), file_read, file_write, env, and mcp usage.

When a skill declares limited permissions but executes shell commands, the analyzer generates a high-severity finding:


# SKILL.md

permissions:
  - "read"

# utils.py

import subprocess

def run_cmd():
    subprocess.run(["ls", "-l"])

Finding output:

{
  "rule_id": "LP1",
  "message": "Code capability 'shell' detected in utils.py but not covered by declared permissions.",
  "severity": "HIGH",
  "confidence": 0.75,
  "file": "utils.py",
  "category": "MCP Least Privilege"
}

Test-only code matches trigger LP1 with reduced confidence (clamped below 1.0) to minimize false positives in test suites.

LP2: Wildcard Permissions

The analyzer identifies wildcard entries that grant blanket access without granularity. The _WILDCARD_PERMS set (lines 38-40) contains *, all, full, and any.


# SKILL.md

permissions:
  - "*"

Finding output:

{
  "rule_id": "LP2",
  "message": "Permission list contains a wildcard entry ('*', 'all', 'full', or 'any'), granting blanket access with no least-privilege boundary.",
  "severity": "MEDIUM",
  "confidence": 0.9,
  "file": "SKILL.md",
  "category": "MCP Least Privilege",
  "tags": ["ASI02"]
}

LP3: Missing Permission Declarations

When a skill omits permissions entirely but the scanner detects any capability through patterns defined in lines 42-89, the analyzer raises a warning:

{
  "rule_id": "LP3",
  "message": "Skill has no declared permissions but code capabilities were detected: shell, network.",
  "severity": "HIGH",
  "confidence": 0.8,
  "file": "SKILL.md"
}

LP4: Over-Declared Permissions

The analyzer detects "tool-poisoning" risks by identifying permissions that exceed actual code requirements. This catches "laundered" permissions that could enable future abuse of integrated tools.


# SKILL.md

permissions:
  - "bash"
  - "network"

If network.py contains only requests.get() calls with no shell usage:

{
  "rule_id": "LP4",
  "message": "Permission 'bash' is declared but no corresponding code capability (shell) was detected.",
  "severity": "LOW",
  "confidence": 0.65,
  "file": "SKILL.md",
  "category": "MCP Least Privilege"
}

The LP4 logic (lines 62-100) specifically excludes wildcard entries from this check, focusing only on specific over-declarations.

Implementation Architecture

The analyzer follows a structured pipeline:

  1. Capability Detection: _CAPABILITY_PATTERNS applies regex collections for shell (lines 42-51), network (lines 52-60), file I/O (lines 61-78), environment access (lines 79-84), and MCP client usage (lines 85-89)
  2. Permission Mapping: Translates manifest strings to internal capability categories via _PERM_TO_CAPABILITY
  3. Rule Evaluation: Constructs Finding objects with severity, confidence, and remediation guidance
  4. SARIF Generation: Emits findings for downstream CI consumption

Key files supporting this analysis include docs/B.3.1-mcp-least-privilege.md (specification), tests/test_mcp_least_privilege.py (unit tests), and src/skillspector/models.py (data models).

Summary

  • Four rule categories: LP1-LP4 cover under-declared, wildcard, missing, and over-declared permissions
  • Regex-based detection: _detect_capabilities identifies six capability categories using targeted patterns for shell, network, file, and environment access
  • Confidence scoring: Test-only code and wildcards receive adjusted confidence levels to reduce false positives
  • Security enforcement: Prevents tool-poisoning attacks by ensuring skills request only necessary permissions
  • CI integration: Generates SARIF-compatible output for automated policy enforcement

Frequently Asked Questions

What is the difference between LP1 and LP4 in SkillSpector's analysis?

LP1 detects under-declared capabilities where code uses abilities not listed in the manifest (e.g., using subprocess without declaring shell permissions), while LP4 detects over-declared permissions where the manifest lists permissions that the code never exercises (e.g., declaring bash permission without any shell capability detected). LP1 poses immediate security risks through broken functionality, whereas LP4 indicates potential attack surface for tool-poisoning attacks.

How does SkillSpector detect wildcard permissions?

The analyzer checks the permissions list against the _WILDCARD_PERMS set containing *, all, full, and any (defined in lines 38-40 of mcp_least_privilege.py). When any wildcard string appears, the analyzer generates an LP2 finding with MEDIUM severity and 0.9 confidence, flagging the lack of least-privilege boundaries.

What code capabilities does the analyzer detect?

The _CAPABILITY_PATTERNS dictionary identifies six categories: shell (subprocess, Popen, os.system), network (httpx, requests, urllib), file_read (open with read mode, read_text), file_write (open with write mode, write_text, shutil.copy), env (os.environ, os.getenv), and mcp (create_session, MCPClient). Each category uses specific regex patterns applied across all executable files in the skill.

How does the analyzer handle test files?

Test-only code detections trigger LP1 (under-declared capabilities) with reduced confidence scores compared to production code. The analyzer distinguishes test contexts and clamps confidence values accordingly, ensuring that temporary test utilities do not generate high-severity false positives while still capturing genuine permission gaps in production implementations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →