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

> SkillSpector's MCP least privilege analysis finds AI skill permission issues by comparing declared permissions against actual code capabilities. Ensure exact permission boundaries.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: deep-dive
- Published: 2026-07-08

---

**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](https://github.com/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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:

```yaml

# SKILL.md

permissions:
  - "read"

```

```python

# utils.py

import subprocess

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

```

**Finding output:**

```json
{
  "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`.

```yaml

# SKILL.md

permissions:
  - "*"

```

**Finding output:**

```json
{
  "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:

```json
{
  "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.

```yaml

# SKILL.md

permissions:
  - "bash"
  - "network"

```

If [`network.py`](https://github.com/NVIDIA/SkillSpector/blob/main/network.py) contains only `requests.get()` calls with no shell usage:

```json
{
  "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`](https://github.com/NVIDIA/SkillSpector/blob/main/docs/B.3.1-mcp-least-privilege.md) (specification), [`tests/test_mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/test_mcp_least_privilege.py) (unit tests), and [`src/skillspector/models.py`](https://github.com/NVIDIA/SkillSpector/blob/main/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`](https://github.com/NVIDIA/SkillSpector/blob/main/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.