Back to skill

Security audit

Ohio State API

Security checks for vulnerabilities and agentic risk

Overview

The OSU data skill is mostly coherent, but its fetch helper can request any HTTP or HTTPS address, not just OSU, which is broader than disclosed.

Review this before installing if your agent runs in an environment with access to internal web services. Prefer the named MCP tools or service/path mode, and avoid allowing prompts or untrusted input to choose arbitrary full URLs for osu-fetch.mjs. Treat directory people-search results as personal information, and install dependencies from a reviewed lockfile where possible.

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/osu-fetch.mjs:93
Finding

Unrestricted Outbound HTTP Requests Enable SSRF

Content
View full analysis

Vulnerability Details

File Location: scripts/osu-fetch.mjs, lines 93–107 and 150–154
Vulnerability Type: Server-Side Request Forgery (SSRF) through an unrestricted user-supplied URL
Risk Level: Medium

Vulnerable Code

js
function buildUrl({ url, service, path, params }) {
  let finalUrl = url;

  if (!finalUrl) {
    if (!service || !path) {
      throw new Error("Provide either <url> or both --service and --path.");
    }
    const base = SERVICE_BASE_URLS[service];
    if (!base) {
      throw new Error(`Unknown service '${service}'. Use --help to see valid services.`);
    }
    const baseWithSlash = base.endsWith("/") ? base : `${base}/`;
    const normalizedPath = path.startsWith("/") ? path.slice(1) : path;
    finalUrl = new URL(normalizedPath, baseWithSlash).toString();
  } else if (!finalUrl.startsWith("http://") && !finalUrl.startsWith("https://")) {
    throw new Error(`URL must start with http(s):// (got ${finalUrl})`);
  }

  const u = new URL(finalUrl);

The resulting URL is then requested without destination validation:

js
res = await fetch(url, {
  method: "GET",
  headers: { accept: "application/json" },
  signal: controller.signal,
});

Technical Analysis

The positional URL mode accepts any destination whose string begins with http:// or https://. It does not enforce the documented content.osu.edu boundary or reject loopback, private-network, link-local, cloud metadata, embedded-credential, or attacker-controlled destinations.

Consequently, anyone able to influence the helper's arguments can cause a request to be issued from the agent's network context. Repeatable --param values are also appended to the selected URL, allowing caller-supplied data to be transmitted to an arbitrary destination.

This behavior exceeds the minimum privileges declared in SKILL.md, which describes access to public Ohio State University APIs hosted at content.osu.edu. No evidence was found that the ...[truncated 1399 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove arbitrary full-URL mode if it is unnecessary. Accept only a predefined service name and a validated relative API path.
  2. If full URLs must remain supported, require https: and enforce an exact hostname allowlist containing only content.osu.edu.
  3. Reject URLs containing embedded usernames or passwords, unexpected ports, or non-empty fragments.
  4. Resolve destination addresses and reject loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6.
  5. Disable automatic redirects or validate every redirect target against the same protocol, hostname, port, and address restrictions.
  6. Prevent path-based origin changes by validating the parsed URL after resolution rather than relying on string-prefix checks.
  7. Avoid placing sensitive values in query parameters because URLs may appear in logs and error messages.
  8. Add automated tests covering destinations such as localhost, 127.0.0.1, [::1], private RFC1918 addresses, link-local addresses, metadata endpoints, alternate ports, embedded credentials, and off-domain redirects.

A minimal hostname restriction should validate the parsed URL before calling fetch():

js
const u = new URL(finalUrl);

if (u.protocol !== "https:" || u.hostname !== "content.osu.edu") {
  throw new Error("Only HTTPS requests to content.osu.edu are allowed.");
}

if (u.username || u.password || (u.port && u.port !== "443")) {
  throw new Error("URL credentials and nonstandard ports are not allowed.");
}

Redirect validation and IP-range filtering should also be applied before considering the request boundary secure.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (9)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
These are used by the bundled MCP server and work with `osu-fetch.mjs`:

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The markdown advertises university directory and people-search functions, which can affect privacy by enabling lookup of identifiable individuals. The README does not include any warning or usage note about responsible handling of directory data or that results may contain personal information.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The production dependency uses a caret range instead of an exact version, so installs may resolve to different releases over time. This weakens supply-chain reproducibility and makes it harder to verify whether a deployed build includes vulnerable or unexpected upstream code.

Content

Scanner excerpt · mcp-server/package.json (reported line 21)May include surrounding context.

json
"author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.17.1"
  },
  "devDependencies": {
    "@types/node": "^20.19.9",

Unverifiable Dependency: @modelcontextprotocol/sdk has 3 known advisory(ies) (CVE-2026-25536 (@modelcontextprotocol/sdk has cross-client data leak via shared server/transport); CVE-2026-0621 (Anthropic's MCP TypeScript SDK has a ReDoS vulnerability); CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The manifest references @modelcontextprotocol/sdk without an exact version, and that package has multiple known advisories. Because the resolved installed version is not fixed here, it is not possible to determine from the manifest alone whether deployments may pull a vulnerable release, which is especially relevant for an MCP server dependency handling client/server protocol interactions.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
83% confidence
Finding

The devDependency @types/node is not pinned to an exact version, which can cause non-reproducible builds and unexpected changes in development or CI environments. While this is less severe than an unpinned runtime dependency, it still increases supply-chain uncertainty.

Content

Scanner excerpt · mcp-server/package.json (reported line 24)May include surrounding context.

json
"@modelcontextprotocol/sdk": "^1.17.1"
  },
  "devDependencies": {
    "@types/node": "^20.19.9",
    "typescript": "^5.9.2"
  },
  "bin": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
84% confidence
Finding

The TypeScript compiler dependency is specified with a caret range, allowing automatic adoption of newer minor/patch releases. This can introduce build-time supply-chain risk or unexpected compilation changes, even if it is not part of the runtime package.

Content

Scanner excerpt · mcp-server/package.json (reported line 25)May include surrounding context.

json
},
  "devDependencies": {
    "@types/node": "^20.19.9",
    "typescript": "^5.9.2"
  },
  "bin": {
    "osu-mcp-server": "build/index.js"

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The response text explicitly labels results as retrieved in Eastern Time, and the code consistently uses getCurrentEasternTime() for all tool outputs. This imposes a specific locale/timezone on users without offering opt-in or indicating that the tool is intentionally limited to an Eastern Time audience.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code explicitly formats timestamps with the 'en-US' locale in natural-language-visible output, which imposes a specific language/locale choice. There is no indication that users can choose another locale or that this constraint is documented as a justified region-specific requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This function returns a user-visible timestamp using the fixed 'en-US' locale, creating the same language/locale policy issue independently of the earlier formatter. The file does not show any user opt-in, configurability, or explicit justification for enforcing U.S. English formatting.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.