Back to skill

Security audit

RFC lookup

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent RFC lookup helper that fetches and caches public RFC text, with the larger mirror sync disclosed and user-controlled.

Install this if you want an agent to look up public IETF RFCs accurately. Be aware that ordinary use may fetch and cache RFC text locally, and only approve the sync command if you are comfortable downloading and maintaining the full RFC text mirror in the configured directory.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill invokes a Python script that can use network access, shell utilities, environment variables, and local file paths, but the manifest declares no explicit tool scope or permissions boundary. That creates an over-privileged, ambiguous execution model where an agent may run broader capabilities than necessary, increasing the blast radius if the script is modified, compromised, or used in an unexpected context.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
RFC_URL = "https://www.rfc-editor.org/rfc/rfc{number}.txt"
RSYNC_MODULE = "rsync.rfc-editor.org::rfcs-text-only/"

# How long the index is served without asking the RFC Editor whether it moved.
# Revalidation is a conditional GET the CDN answers 304 to, so a refresh past
# this costs a round trip rather than 2 MB, and a day is as fine-grained as the
# corpus gets: RFCs publish in weekday batches at roughly one a day.
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _run_search(cmd: list[str]) -> list[str]:
    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
    except OSError as exc:
        raise RFCError(f"could not run {cmd[0]}: {exc}") from exc
    # Exit status 1 means "no matches" for both rg and grep; only 2+ is an error.
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("Cancelled.")
            return 1
    mirror.mkdir(parents=True, exist_ok=True)
    result = subprocess.run(cmd, check=False)
    if result.returncode == 0 and not dry_run:
        (mirror / SYNC_STAMP).write_text(f"synced {formatdate(usegmt=True)}\n", encoding="utf-8")
        print(f"\nDone. {count_documents(mirror)} documents in {mirror}")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
for match in _REFS.finditer(metadata):
        refs = [int(n) for n in _RFC_REF.findall(match.group(2))]
        key = match.group(1).lower().replace(" ", "_")
        getattr(record, key).extend(refs)
    for match in _ALSO.finditer(metadata):
        record.also.append(match.group(1).strip())
    status_match = _STATUS.search(metadata)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

No suspicious patterns detected.