Back to skill

Security audit

Markdown Linter

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent Markdown linter with an optional external-link checker that should be used cautiously because it makes outbound requests to links found in files.

This skill is reasonable to install for local Markdown linting. Leave --check-external-links disabled for untrusted Markdown or when running inside sensitive corporate/cloud networks unless the tool is sandboxed or updated to block localhost, private IP ranges, metadata endpoints, unsafe redirects, and excessive URL checks.

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

Warning
Location
scripts/main.py:271
Finding

Server-Side Request Forgery Through Unrestricted External Link Validation

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py, lines 271–277
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: Medium

Vulnerable Code

python
url_pattern = r'\[.*?\]\((https?://[^\s)]+)\)'
for i, line in enumerate(lines):
    for match in re.finditer(url_pattern, line):
        url = match.group(1)
        col = match.start(1)
        try:
            response = requests.head(url, timeout=5, allow_redirects=True)

Technical Analysis

When external link checking is enabled, the linter extracts HTTP and HTTPS URLs directly from the contents of a Markdown file and sends a HEAD request to each URL. The destination is not checked against an allowlist, nor are resolved addresses rejected when they belong to loopback, private, link-local, reserved, or cloud metadata ranges.

Because allow_redirects=True is enabled, checking only the initial URL would also be insufficient: an attacker-controlled public endpoint could redirect the request to an internal address. Each redirect destination and its resolved addresses must be validated.

Although the request uses the HEAD method and does not normally download a response body, this does not eliminate SSRF. The operation can still reveal endpoint reachability and status codes, interact with services that process HEAD, or trigger behavior in endpoints that do not implement HTTP method semantics safely.

The vulnerable functionality is optional and is reached when the user invokes the documented --check-external-links option.

Attack Path

  1. An attacker creates or modifies a Markdown file that will be linted.

  2. The attacker embeds a link targeting a sensitive destination, such as a loopback service, a private-network host, a link-local metadata endpoint, or a public URL that redirects to one:

    markdown
    [Internal probe](http://127.0.0.1:8080/admin)
    
  3. A user or automated process runs the linter with external link validation enabled:

    ` ...[truncated 1455 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve destination hostnames before making requests and reject every address in loopback, private, link-local, multicast, reserved, unspecified, and other non-public ranges for both IPv4 and IPv6.
  2. Explicitly block known infrastructure metadata destinations, including link-local metadata addresses and provider-specific metadata hostnames.
  3. Disable automatic redirects by default. If redirects are required, validate the scheme, hostname, port, and all resolved IP addresses at every redirect hop.
  4. Permit only http and https, reject embedded credentials, and restrict destination ports to an approved set such as 80 and 443.
  5. Prefer an explicit hostname allowlist when the expected link destinations are known.
  6. Protect against DNS rebinding by connecting only to the validated resolved address while preserving safe hostname verification for HTTPS.
  7. Apply strict connection and total-request timeouts, redirect limits, URL-count limits, and concurrency limits to reduce network resource exhaustion.
  8. Run external-link checking in a sandbox with restricted outbound network access and no access to sensitive internal networks.
  9. Keep external validation disabled by default and clearly warn that it must not be enabled for untrusted Markdown unless network isolation is in place.
  10. Add automated tests covering loopback, RFC 1918 private ranges, IPv6 local ranges, link-local addresses, unusual IP representations, DNS rebinding scenarios, and public-to-private redirects.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (8)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 38)May include surrounding context.

md
./scripts/main.py run --input path/to/file.md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 43)May include surrounding context.

md
./scripts/main.py run --input path/to/file.md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 89)May include surrounding context.

md
./scripts/main.py run --input path/to/file.md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 94)May include surrounding context.

md
./scripts/main.py run --input path/to/file.md

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 99)May include surrounding context.

md
./scripts/main.py run --input path/to/file.md

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises file-reading and optional external link validation capabilities, which imply filesystem and network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity where the runtime may grant broader access than reviewers expect, increasing the risk of unintended file access or outbound requests when processing untrusted markdown paths or links.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest says the skill lints Markdown files for formatting issues, broken links, and style consistency, but this file adds active HTTP requests to external sites via requests.head when --check-external-links is enabled. While checking internal links is local linting behavior, contacting arbitrary external URLs is a separate network capability that is not clearly declared in the stated skill purpose.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

When --check-external-links is enabled, the tool performs outbound requests to URLs embedded in markdown without prominently warning users that processing a file may contact third-party hosts. If the markdown is attacker-controlled, this can leak network metadata, trigger requests to tracking or internal endpoints, and surprise users who expected offline linting.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.