Back to skill

Security audit

Protobuf Linter

Security checks for vulnerabilities and agentic risk

Overview

This is a local protobuf linter with no hidden network or persistence behavior, but it has a real denial-of-service parser bug and overstates some security coverage.

Review before installing. Use it only on trusted or reasonably sized protobuf files until the reserved-range handling is fixed, and do not treat its advertised security or compatibility coverage as comprehensive.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/protobuf_linter.py:169
Finding

Unbounded Reserved-Range Expansion Causes Memory Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/protobuf_linter.py, lines 169–175
Vulnerability Type: Uncontrolled resource consumption through unbounded range materialization
Risk Level: High

Vulnerable Code

python
elif 'to' in part:
    try:
        a, b = part.split('to')
        a, b = int(a.strip()), b.strip()
        if b == 'max':
            b = 536870911
        reserved_nums.extend(range(a, int(b)+1))
    except ValueError:
        pass

Technical Analysis

The parser stores every reserved field number as an individual integer. For a range declaration, it constructs a range and passes it to list.extend(), which materializes every value in memory.

Protocol Buffers permits declarations such as:

proto
reserved 1 to max;

The implementation translates max to 536870911, then attempts to append as many as 536,870,911 Python integers to reserved_nums. This requires many gigabytes of memory because each integer and list reference carries runtime overhead. No range-size limit, resource budget, or interval-based representation prevents this expansion.

The issue is reachable through normal processing of an attacker-controlled .proto file. It does not require command execution or elevated privileges.

Attack Path

  1. An attacker creates or modifies a .proto file containing a large reserved range, for example:

    proto
    syntax = "proto3";
    package example;
    
    message Payload {
      reserved 1 to max;
    }
    
  2. The attacker causes the file to be submitted to the Skill, or places it in a directory that the user scans recursively.

  3. The user or Agent invokes lint or validate.

  4. parse_proto() detects the reserved range and converts max to 536870911.

  5. reserved_nums.extend(range(...)) attempts to materialize hundreds of millions of integers.

  6. The linter consumes excessive memory and may be terminated by the operating system or destabilize the host.

Impact Assessment

Success ...[truncated 524 chars]

Remediation
View remediation

Remediation Suggestions

Represent reserved ranges as intervals rather than expanding them into individual integers. For example, maintain separate collections for individual numbers and (start, end) tuples, then test field numbers using interval comparisons.

Recommended hardening steps:

  1. Replace reserved_nums.extend(range(a, end + 1)) with storage of (a, end).
  2. Validate that the range start and end are legal protobuf field numbers and that start <= end.
  3. Reject malformed declarations rather than silently ignoring all ValueError exceptions.
  4. If expansion must be retained, enforce a strict maximum range size before allocation.
  5. Apply process memory and execution-time limits as defense in depth when scanning untrusted files.
  6. Add regression tests for reserved 1 to max;, very large numeric ranges, reversed ranges, and malformed range declarations.

A safe approach would resemble:

python
reserved_ranges = []

elif 'to' in part:
    try:
        start_text, end_text = part.split('to', 1)
        start = int(start_text.strip())
        end_text = end_text.strip()
        end = 536870911 if end_text == 'max' else int(end_text)

        if start < 1 or end > 536870911 or start > end:
            raise ValueError("Invalid reserved field range")

        reserved_ranges.append((start, end))
    except ValueError:
        # Report a parse or validation issue.
        pass

Reserved-number conflict checks should then use bounded interval comparisons:

python
if any(start <= f.number <= end for start, end in reserved_ranges):
    # Report the reserved-number conflict.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

The skill description claims security linting, broad 24-rule coverage, and breaking-change analysis that the implementation reportedly does not fully provide. This can cause users to rely on the tool for security or compatibility assurance that it does not actually deliver, leading to missed issues and unsafe deployment decisions.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.