# What Are MCP Least Privilege Violations and How SkillSpector Detects Them

> Discover MCP least privilege violations and how SkillSpector pinpoints them. Learn how SkillSpector analyzes declared vs. actual skill permissions to ensure security and prevent unauthorized access.

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

---

**MCP least privilege violations happen when a skill's declared permissions in its [`SKILL.md`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md) manifest do not precisely match the capabilities—such as network access, file operations, or shell execution—actually present in its code, and SkillSpector detects these mismatches through four specific rule families (LP1–LP4) that analyze the gap between manifest declarations and runtime behavior.**

NVIDIA SkillSpector enforces least privilege security for MCP-enabled skills by comparing the `permissions` list defined in a skill's manifest against the capabilities detected in its executable source files. The analyzer implements a bidirectional check that flags both under-privileged code (missing declarations) and over-privileged manifests (excessive declarations), ensuring that skills request exactly the access they need and no more.

## Understanding the Four Rule Families

SkillSpector's **MCP Least-Privilege analyzer** ([`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py)) categorizes violations into four distinct rule families, each with specific severity and confidence thresholds:

- **LP1 – Under-Declared Capability (HIGH severity):** A capability is detected in the code (e.g., `subprocess` calls, `httpx` imports, or `open()` statements) but has no corresponding entry in the [`SKILL.md`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md) `permissions` field. Confidence is **0.75** for production code or **0.55** if the capability appears only in test files.

- **LP2 – Wildcard Permission (MEDIUM severity):** The manifest contains overly broad permissions such as `*`, `all`, `full`, or `any`. These grant blanket access and violate the principle of least privilege with confidence **≥ 0.90**.

- **LP3 – No Permissions Declared (MEDIUM severity):** The `permissions` field is missing, `null`, or empty, yet the analyzer detects executable capabilities in the skill's code. This triggers with confidence **≥ 0.70**.

- **LP4 – Over-Declared Permission (LOW severity):** A permission is listed in the manifest but no corresponding capability exists in the code (e.g., declaring `write` permission while never opening a file for writing). Confidence is **≥ 0.65**.

## How the Detection Engine 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 executing a ten-step workflow that inspects both static metadata and source code:

1. **Early Exit:** Skips analysis if the skill lacks a manifest or contains no executable files (documentation-only skills).

2. **Manifest Ingestion:** Reads `manifest.get("permissions")`, handling `None`, empty lists, or string arrays.

3. **File Collection:** Gathers executable files from `component_metadata` and loads their content via `file_cache`.

4. **Capability Detection:** Uses `_detect_capabilities(content)` with regex patterns defined in `_CAPABILITY_PATTERNS` to identify imports like `subprocess` (mapped to *shell*), `httpx` (mapped to *network*), and `open(..., "r")` (mapped to *file_read*).

5. **Permission Mapping:** Normalizes declared permissions to capability categories via `_map_permissions_to_categories(permissions)`, using the case-insensitive `_PERM_TO_CAPABILITY` dictionary.

6. **Rule Evaluation:** Applies LP2 (wildcard check), LP3 (empty permissions check), LP1 (uncovered capabilities), and LP4 (unused declarations) sequentially.

7. **Finding Generation:** Emits structured `Finding` objects containing rule IDs, severity levels, confidence scores, and remediation advice.

## Code Examples of Violation Types

### Under-Declared Capabilities (LP1)

When code uses `httpx` and `subprocess` but the manifest omits the matching permissions, SkillSpector generates an LP1 finding with **HIGH** severity:

```python

# scripts/agent.py

import httpx          # network capability

import subprocess    # shell capability

```

```yaml

# SKILL.md

---
name: underdeclared-skill

# permissions: []  # Missing network and shell

triggers: []
---

```

The analyzer outputs:

```json
{
  "rule_id": "LP1",
  "severity": "HIGH",
  "confidence": 0.75,
  "message": "Code capability 'network' detected in scripts/agent.py but not covered by declared permissions."
}

```

As implemented in [`tests/test_mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/test_mcp_least_privilege.py), capabilities found only in files matching `test_*.py` or `*_test.py` patterns receive reduced confidence (**0.55**) because they likely represent test infrastructure rather than production behavior.

### Wildcard Permissions (LP2)

Manifests containing glob-style permissions trigger LP2:

```yaml

# SKILL.md

---
name: overprivileged-skill
permissions: ["*"]
triggers: []
---

```

Result:

```json
{
  "rule_id": "LP2",
  "severity": "MEDIUM",
  "confidence": 0.90,
  "message": "Permission list contains a wildcard entry ('*', 'all', 'full', or 'any'), granting blanket access ..."
}

```

### Missing Permission Blocks (LP3)

When a manifest completely lacks a `permissions` field despite containing executable code, the analyzer emits LP3:

```json
{
  "rule_id": "LP3",
  "severity": "MEDIUM",
  "confidence": 0.70,
  "message": "Skill has no declared permissions but code capabilities were detected: network, shell."
}

```

### Over-Declared Permissions (LP4)

Declaring permissions that never appear in code results in **LOW** severity findings:

```yaml

# SKILL.md

permissions: ["bash", "network", "write"]

```

```python

# scripts/helper.py

import httpx  # Only network is used

```

Output:

```json
{
  "rule_id": "LP4",
  "severity": "LOW",
  "confidence": 0.65,
  "message": "Permission 'write' is declared but no corresponding code capability (file_write) was detected."
}

```

## Confidence Scoring and Normalization

The analyzer employs **case-insensitive matching** for permission names, so declaring `["BASH", "Network"]` correctly maps to `shell` and `network` categories without triggering LP1 false positives. The `_map_permissions_to_categories` function normalizes inputs before comparison, ensuring that capitalization differences between manifest declarations and internal capability tags do not produce violations.

## Summary

- **MCP least privilege violations** represent mismatches between a skill's declared permissions and its actual code capabilities.
- **SkillSpector** detects these via [`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py) using four rule families: LP1 (under-declared), LP2 (wildcard), LP3 (no permissions), and LP4 (over-declared).
- **Regex-based detection** in `_detect_capabilities` identifies concrete capabilities like network, shell, and file operations.
- **Test-only code** receives reduced confidence scores (0.55 vs 0.75) to minimize false positives in development utilities.
- **Remediation** requires aligning the [`SKILL.md`](https://github.com/NVIDIA/SkillSpector/blob/main/SKILL.md) `permissions` list exactly with the capabilities present in production source files.

## Frequently Asked Questions

### What is the difference between LP1 and LP4 violations?

**LP1 (Under-Declared)** fires when code contains capabilities not listed in the manifest, representing a security risk where the skill might lack necessary sandbox restrictions. **LP4 (Over-Declared)** fires when the manifest lists permissions that the code never uses, representing unnecessary privilege grants that could be exploited if the code changes. LP1 carries **HIGH** severity while LP4 carries **LOW** severity.

### How does SkillSpector distinguish between production and test code?

The analyzer checks file paths against patterns `test_*.py` and `*_test.py` in [`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py). Capabilities found exclusively in these files trigger LP1 with a reduced confidence of **0.55** rather than the standard **0.75**, reflecting the lower risk associated with test-only functionality.

### Why are wildcard permissions considered a violation?

Wildcard entries like `*` or `all` in the `permissions` field violate the principle of least privilege by granting blanket access to all capabilities. The analyzer detects these via LP2 with **≥ 0.90** confidence because they eliminate the security boundary between the skill and the host system, contradicting the explicit permission model required by the MCP specification.

### Where is the MCP least privilege detection logic implemented?

The core detection engine resides in [`src/skillspector/nodes/analyzers/mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/mcp_least_privilege.py) within the NVIDIA/SkillSpector repository. This file contains the `_CAPABILITY_PATTERNS` regex definitions, `_detect_capabilities` extraction logic, and the rule families LP1–LP4 that generate findings. Comprehensive unit tests validating each rule are located in [`tests/test_mcp_least_privilege.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/test_mcp_least_privilege.py).