Back to skill

Security audit

Ubuntu Encyclopedia

Security checks for vulnerabilities and agentic risk

Overview

The skill helps agents use and cache Ubuntu documentation locally, with no hidden or destructive behavior found.

Install only if you want a workspace-local Ubuntu documentation and operations cache. Review .Ubuntu-Encyclopedia/ periodically, keep credentials and detailed access secrets out of notes, and use the cache helpers only for official Ubuntu docs or manpages until redirect and response-size hardening is added.

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 (4)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cache_doc.py:94
Finding

Redirect destinations bypass the official Ubuntu domain allowlist in cache_doc.py

Content
View full analysis

Vulnerability Details

File Location: scripts/cache_doc.py, lines 94-117 and 136-139
Vulnerability Type: Unvalidated HTTP redirect destination
Risk Level: Medium

Vulnerable Code

python
def validate_url(url: str) -> urllib.parse.ParseResult:
    parsed = urllib.parse.urlparse(url)
    if parsed.scheme not in ALLOWED_SCHEMES:
        raise ValueError(f'Only {sorted(ALLOWED_SCHEMES)} URLs are supported')
    if parsed.username or parsed.password:
        raise ValueError('Userinfo in URLs is not supported')
    if parsed.hostname is None:
        raise ValueError('URL must include a hostname')
    try:
        ipaddress.ip_address(parsed.hostname)
    except ValueError:
        pass
    else:
        raise ValueError('Direct IP URLs are not supported')
    if parsed.hostname not in ALLOWED_NETLOCS:
        raise ValueError(f'Only {sorted(ALLOWED_NETLOCS)} URLs are supported')
    if parsed.port is not None:
        raise ValueError('Explicit ports are not supported')
    if BLOCK_QUERY and (parsed.params or parsed.query or parsed.fragment):
        raise ValueError('Query strings, params, and fragments are not supported')
    normalized_relative_path(parsed)
    return parsed
python
def fetch_html(url: str) -> str:
    req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
    with urllib.request.urlopen(req, timeout=20) as resp:
        charset = resp.headers.get_content_charset() or 'utf-8'
        return resp.read().decode(charset, errors='replace')

Technical Analysis

The script validates only the URL supplied on the command line. Python's default urllib.request.urlopen() behavior automatically follows HTTP redirects, but neither each redirect target nor the final value of resp.geturl() is passed through validate_url().

Consequently, a URL initially hosted on documentation.ubuntu.com, help.ubuntu.com, or ubuntu.com ca ...[truncated 1543 chars]

Remediation
View remediation

Remediation Suggestions

  • Disable automatic redirects and implement a custom redirect handler.
  • Apply validate_url() to every redirect target before following it.
  • Set a small maximum redirect count to prevent redirect loops.
  • Reject HTTPS-to-HTTP transitions and continue enforcing the hostname, port, userinfo, query, and direct-IP restrictions.
  • After opening the response, validate resp.geturl() again before reading or caching its body.
  • Record the validated final URL in cache metadata so the provenance of cached content is explicit.
  • Consider resolving approved hostnames and rejecting addresses in loopback, private, link-local, reserved, and cloud-metadata ranges if server-side request forgery protections must remain effective after DNS resolution.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cache_manpage.py:94
Finding

Redirect destinations bypass the manpages.ubuntu.com allowlist in cache_manpage.py

Content
View full analysis

Vulnerability Details

File Location: scripts/cache_manpage.py, lines 94-117 and 135-138
Vulnerability Type: Unvalidated HTTP redirect destination
Risk Level: Medium

Vulnerable Code

python
def validate_url(url: str) -> urllib.parse.ParseResult:
    parsed = urllib.parse.urlparse(url)
    if parsed.scheme not in ALLOWED_SCHEMES:
        raise ValueError(f'Only {sorted(ALLOWED_SCHEMES)} URLs are supported')
    if parsed.username or parsed.password:
        raise ValueError('Userinfo in URLs is not supported')
    if parsed.hostname is None:
        raise ValueError('URL must include a hostname')
    try:
        ipaddress.ip_address(parsed.hostname)
    except ValueError:
        pass
    else:
        raise ValueError('Direct IP URLs are not supported')
    if parsed.hostname not in ALLOWED_NETLOCS:
        raise ValueError(f'Only {sorted(ALLOWED_NETLOCS)} URLs are supported')
    if parsed.port is not None:
        raise ValueError('Explicit ports are not supported')
    if BLOCK_QUERY and (parsed.params or parsed.query or parsed.fragment):
        raise ValueError('Query strings, params, and fragments are not supported')
    normalized_relative_path(parsed)
    return parsed
python
def fetch_html(url: str) -> str:
    req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
    with urllib.request.urlopen(req, timeout=20) as resp:
        charset = resp.headers.get_content_charset() or 'utf-8'
        return resp.read().decode(charset, errors='replace')

Technical Analysis

The initial URL must use HTTPS and manpages.ubuntu.com, but urllib.request.urlopen() follows redirects automatically. Redirect targets and the final response URL are not validated against the same restrictions.

This means the fetch operation can leave the declared manpages.ubuntu.com network boundary despite the initial validation. Content retrieved from t ...[truncated 1097 chars]

Remediation
View remediation

Remediation Suggestions

  • Replace automatic redirect handling with a bounded redirect handler that validates every Location target.
  • Require every redirect and final response to remain HTTPS and hosted on manpages.ubuntu.com.
  • Revalidate resp.geturl() before reading the response.
  • Reject redirects containing userinfo, explicit ports, queries, fragments, direct IP addresses, or paths outside /manpages.
  • Store the validated final URL alongside the original source URL to preserve provenance.
  • Where strong SSRF resistance is required, validate resolved addresses against private, loopback, link-local, reserved, and metadata address ranges.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cache_doc.py:136
Finding

Unbounded remote response processing in cache_doc.py

Content
View full analysis

Vulnerability Details

File Location: scripts/cache_doc.py, lines 136-139
Vulnerability Type: Unbounded network response read and resource exhaustion
Risk Level: Medium

Vulnerable Code

python
def fetch_html(url: str) -> str:
    req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
    with urllib.request.urlopen(req, timeout=20) as resp:
        charset = resp.headers.get_content_charset() or 'utf-8'
        return resp.read().decode(charset, errors='replace')

Technical Analysis

Calling resp.read() without a size argument loads the entire response into memory. The script does not impose a response-byte limit, validate Content-Length, restrict the response MIME type, or stream content through a bounded reader.

The response is then decoded, parsed into additional Python strings, normalized, and written to disk. These operations can require multiple times the original response size in memory. The 20-second timeout does not provide a reliable size limit and may not stop a server that continuously supplies data within socket timeout intervals.

Attack Path

  1. The requested endpoint returns an extremely large response, or redirects to an endpoint that does so.
  2. resp.read() attempts to buffer the full body in process memory.
  3. Decoding and HTML extraction allocate additional memory and consume CPU.
  4. The resulting text is written to the local cache without a disk quota.
  5. The process or host may suffer memory pressure, excessive CPU consumption, or disk exhaustion.

Impact Assessment

A malicious or malfunctioning remote server can cause denial of service within the privileges and resource limits of the process. The issue can affect process availability and consume workspace or filesystem capacity. It does not directly provide code execution, access to secrets, or elevated system permissions.

Remediation
View remediation

Remediation Suggestions

  • Define a conservative maximum response size suitable for documentation pages.
  • Reject responses whose declared Content-Length exceeds that limit.
  • Read the response incrementally in fixed-size chunks and abort if the cumulative size exceeds the limit; do not rely exclusively on Content-Length.
  • Permit only expected MIME types such as text/html and selected textual formats.
  • Bound parsing output and title length as defense in depth.
  • Write through a temporary file only after validation, then atomically replace the destination.
  • Apply filesystem quotas or per-cache size limits where the Skill may run against shared or sensitive storage.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cache_manpage.py:135
Finding

Unbounded remote response processing in cache_manpage.py

Content
View full analysis

Vulnerability Details

File Location: scripts/cache_manpage.py, lines 135-138
Vulnerability Type: Unbounded network response read and resource exhaustion
Risk Level: Medium

Vulnerable Code

python
def fetch_html(url: str) -> str:
    req = urllib.request.Request(url, headers={'User-Agent': USER_AGENT})
    with urllib.request.urlopen(req, timeout=20) as resp:
        charset = resp.headers.get_content_charset() or 'utf-8'
        return resp.read().decode(charset, errors='replace')

Technical Analysis

The response body is read into memory without a maximum byte count. No Content-Length boundary, MIME-type restriction, streaming limit, parser-output limit, or cache quota is enforced.

Because the body is subsequently decoded and processed by TextExtractor, peak memory consumption can substantially exceed the downloaded byte count. The socket timeout limits waiting behavior but does not establish a dependable upper bound on the amount of data accepted.

Attack Path

  1. A requested manpage endpoint returns a very large body, either directly or through an unvalidated redirect.
  2. The process buffers the complete response through resp.read().
  3. Decoding and HTML parsing create additional in-memory representations.
  4. The extracted output is written to the workspace cache.
  5. Available memory, CPU time, or disk capacity may be exhausted.

Impact Assessment

The issue enables resource-exhaustion denial of service with the process's ordinary filesystem and resource privileges. It may terminate the caching process or impair other workloads on the same host. No direct code execution, secret disclosure, persistence, or privilege escalation was identified.

Remediation
View remediation

Remediation Suggestions

  • Enforce a strict maximum download size appropriate for manpage HTML.
  • Check Content-Length when present and also enforce the limit while reading chunks.
  • Abort and discard partial data when the cumulative byte limit is exceeded.
  • Accept only expected textual MIME types.
  • Bound extracted-text and title sizes before constructing the cached document.
  • Use controlled temporary storage and atomic replacement to avoid leaving incomplete cache files.
  • Consider total cache quotas and cleanup policies to prevent cumulative disk exhaustion.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents an Ubuntu-specific assistance workflow for answering questions and handling maintenance/troubleshooting scenarios. The supplied code does not implement system administration, package management, diagnostics, or Ubuntu troubleshooting logic. Instead, it is a narrowly scoped document-cache tool: it accepts a docs URL, restricts it to approved Ubuntu documentation hosts, downloads the page, extracts text from HTML, and stores the result locally as Markdown. While this may support a documentation-first workflow, the actual code’s primary purpose is fetching and caching documentation content, which is a materially different and undeclared capability compared with the broad Ubuntu support behavior described.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad Ubuntu support and troubleshooting workflow for Ubuntu-specific questions and maintenance tasks. The supplied code does not implement such a workflow. Instead, it validates a restricted manpages.ubuntu.com HTTPS URL, downloads the page, extracts text from HTML, and saves it locally as Markdown. While this is related to Ubuntu documentation, the actual primary purpose is document retrieval and caching, not answering Ubuntu questions, performing diagnostics, or handling system administration tasks. The code also introduces concrete capabilities not reflected in the description: outbound HTTP access and filesystem writes for caching.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a skill focused on Ubuntu-specific operational guidance and troubleshooting behavior. The supplied code does not implement any Ubuntu documentation retrieval, diagnostics, package/service/network/storage analysis, or question-answering workflow. Instead, it only initializes a local folder structure and seed files for an '.Ubuntu-Encyclopedia' workspace. That is a materially different primary purpose from the declared skill behavior, so this should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill clearly instructs use of network fetching and local file writes via caching and workspace initialization, but it declares no explicit tool scope or permissions. That creates an authorization and transparency gap: an agent may perform outbound requests and modify the filesystem without the skill metadata making those capabilities visible for policy enforcement or reviewer approval.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The workflow explicitly encourages storing local operational knowledge such as deployment/access inventory, host-role notes, and sensitive boundaries, but it provides no safeguards for minimizing, redacting, classifying, or protecting sensitive data. In a live Ubuntu administration context, this can cause credentials-adjacent details, network topology, access paths, or security-relevant configuration notes to be written into the workspace cache where they may be retained, exposed, or reused beyond the original task.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.