Back to skill

Security audit

canvas-parent-api

Security checks for vulnerabilities and agentic risk

Overview

The skill’s Canvas API purpose is legitimate, but it gives users unsafe credential-handling commands that can execute unpinned remote code and expose Canvas tokens if variables are manipulated.

Review before installing or following the commands. Use only a verified Canvas institution URL over HTTPS, avoid the eval/npx OAuth helper unless you have pinned and reviewed it, keep refresh tokens and client secrets out of shell history and broad environments, and validate file URLs before sending a bearer token or downloading files.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:39
Finding
Unpinned Remote Package Output Is Executed Through eval<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 39-51 **Vulnerability Type**: Remote package execution and unsafe shell evaluation **Risk Level**: Critical ### Vulnerable Code ```sh **B. OAuth via the mobile QR-login flow** In the Canvas mobile app: Account → **QR for Login** (or "Pair with Observer/QR Login"), scan it with any camera to get the URL it encodes (`https://sso.canvaslms.com/canvas/login?domain=...&code=...`). Exchange it once for OAuth credentials with the helper this same repo ships (no MCP server needs to be *running* — it's a one-off CLI). It prints four `NAME=value` lines to stdout (`CANVAS_BASE_URL`/`CANVAS_CLIENT_ID`/`CANVAS_CLIENT_SECRET`/`CANVAS_REFRESH_TOKEN`) — **export them into the shell**, since the next `curl` reads them as env vars and this step can't be skipped: ```sh eval "$(npx canvas-parent-mcp-qr-login "<qr-url>" | sed 's/^/export /')" ``` ``` ### Technical Analysis The command invokes an unpinned npm package through `npx`. If that package is not already installed locally, `npx` can retrieve and execute it from the package registry. The effective implementation can therefore change after the Skill has been reviewed. The package's standard output is subsequently transformed with `sed` and executed directly by the current shell through `eval`. There is no parser restricting the output to the four expected environment-variable assignments. Shell metacharacters, command substitutions, redirections, and additional statements emitted by the package would all be interpreted as commands. The combination is more dangerous than merely using an unpinned dependency: arbitrary output from remotely sourced executable code is explicitly passed into a second shell-evaluation stage. ### Attack Path 1. An attacker compromises the npm package, a transitive dependency, the publisher account, or the package-distribution channel. 2. The affected package version is made to emit malicious shell syntax in addition to, ...[truncated 841 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `eval` entirely. 2. Pin the helper package to a specifically reviewed version instead of allowing `npx` to select the current registry version. 3. Lock and verify package integrity through a reviewed lockfile or an equivalent cryptographic integrity mechanism. 4. Prefer shipping a small, auditable helper with the Skill rather than retrieving executable code during use. 5. Parse helper output as data: - Permit only `CANVAS_BASE_URL`, `CANVAS_CLIENT_ID`, `CANVAS_CLIENT_SECRET`, and `CANVAS_REFRESH_TOKEN`. - Reject duplicate, missing, or unexpected fields. - Validate values before assigning them. - Assign values using shell-safe mechanisms rather than evaluating generated source code. 6. Run any unavoidable credential helper in a constrained subprocess with minimum filesystem and network privileges. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:66
Finding
Canvas Bearer Token Can Be Sent to an Arbitrary Configured Origin<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 66-72 **Vulnerability Type**: Unvalidated credential destination **Risk Level**: High ### Vulnerable Code ```sh ## Core call pattern ```sh curl -s \ -H "Authorization: Bearer $CANVAS_TOKEN" \ -H "Accept: application/json+canvas-string-ids, application/json" \ "$CANVAS_BASE_URL/api/v1/users/self/profile" \ | sed 's/^while(1);//' | jq . ``` ``` The same pattern is repeated across the endpoint examples in `SKILL.md` and `references/canvas-endpoints.md`. ### Technical Analysis Sending an authorization token to the user's legitimate Canvas institution is necessary for the declared functionality. However, the documented command attaches the bearer token to an environment-controlled `CANVAS_BASE_URL` without enforcing HTTPS or validating that the destination is the intended institutional Canvas origin. Environment variables are mutable process inputs. If another script, copied setup command, compromised helper, or attacker-controlled shell configuration changes `CANVAS_BASE_URL`, the next documented API request sends the bearer token to that destination. The Skill states that the host should be institution-specific, but this is only explanatory text and is not an effective security control. The command itself accepts arbitrary URL schemes and origins. ### Attack Path 1. The victim obtains a valid Canvas bearer token and exports it as `CANVAS_TOKEN`. 2. An attacker influences `CANVAS_BASE_URL`, including through a malicious setup command, altered shell profile, compromised credential helper, or social engineering. 3. The victim executes one of the documented `curl` commands. 4. `curl` sends the `Authorization: Bearer` header to the attacker-selected endpoint. 5. The attacker records the token and uses it against the real Canvas API until it expires or is revoked. ### Impact Assessment The attacker obtains the same Canvas API privileges granted to the bearer token. Depending on th ...[truncated 488 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `CANVAS_BASE_URL` to use HTTPS. 2. Parse the URL and reject: - HTTP and other non-HTTPS schemes. - Embedded usernames or passwords. - IP literals unless explicitly required and confirmed. - Fragments, malformed hosts, and unexpected ports. 3. Require the user to explicitly confirm or allowlist the institutional Canvas origin before any credential is sent. 4. Resolve and display the final normalized origin during setup, then bind credentials to that origin. 5. Do not rely solely on suffix matching such as `endsWith("instructure.com")`; perform proper hostname parsing and exact or administratively approved origin matching. 6. Avoid authenticated redirects across origins. 7. Use short-lived, minimally scoped access tokens where supported and document immediate revocation procedures for suspected exposure. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
references/canvas-endpoints.md:208
Finding
Bearer Token Is Attached to an Unvalidated File URL While Redirects Are Followed<![CDATA[ ## Vulnerability Details **File Location**: `references/canvas-endpoints.md`, lines 208-218 **Vulnerability Type**: Credential disclosure through an unvalidated absolute URL **Risk Level**: High ### Vulnerable Code ```sh ## 18. Download a file Use the absolute `url` field from §17 directly — it's already a fully-qualified, Bearer-authable Canvas URL (no `/api/v1` prefix needed): ```sh curl -sL -H "Authorization: Bearer $CANVAS_TOKEN" "$FILE_URL" -o /path/to/destination.pdf ``` `-L` follows Canvas's redirect to the actual file storage backend. The MCP's `canvas_download_file` tool additionally refuses to overwrite an existing destination unless told to and validates the parent directory exists — replicate that yourself in a script if it matters (`[ -f dest ] && exit 1`, `[ -d "$(dirname dest)" ] || exit 1`). ``` ### Technical Analysis `FILE_URL` is an absolute, variable-controlled URL. The command attaches the Canvas bearer token before validating that the initial destination belongs to the trusted Canvas origin. Consequently, a substituted, copied, or otherwise attacker-controlled value can directly receive the token. The command also enables redirect following through `-L`. Curl generally attempts to avoid forwarding sensitive authentication headers to a different host, but security should not depend on version-specific redirect behavior, local curl configuration, or assumptions about every redirect chain. The initial URL remains wholly unvalidated regardless of redirect handling. Downloading from storage infrastructure may be necessary, but the bearer token should only be disclosed to an origin explicitly authorized to receive it. ### Attack Path 1. The victim has a valid `CANVAS_TOKEN` in the environment. 2. An attacker causes `FILE_URL` to reference an attacker-controlled HTTPS server. This can occur through variable substitution, a copied malicious command, untrusted input, or compromised upstream response data. 3. The victim executes t ...[truncated 777 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse and normalize `FILE_URL` before issuing a request. 2. Require HTTPS and verify that the initial origin is the confirmed institutional Canvas origin or another explicitly approved Canvas download origin. 3. Do not attach the bearer token to arbitrary absolute URLs. 4. Handle redirects in stages: - Make the authenticated request only to the approved Canvas origin. - Inspect the redirect response without automatically forwarding credentials. - Validate the redirected URL. - Download from the validated storage URL without the Canvas bearer token when the URL is already signed. 5. Set a bounded redirect count and reject HTTPS-to-HTTP downgrades. 6. Use a destination created with safe exclusive-write semantics and verify the downloaded content type when feasible. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:104
Finding
Predictable Pagination Files Permit Overwrite and Symlink Attacks<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 104-112 **Vulnerability Type**: Unsafe temporary and output file handling **Risk Level**: Medium ### Vulnerable Code ```sh ```sh url="$CANVAS_BASE_URL/api/v1/courses/123/students/submissions?student_ids[]=self&per_page=100" while [ -n "$url" ]; do headers=$(curl -sD - -o page.json -H "Authorization: Bearer $CANVAS_TOKEN" \ -H "Accept: application/json+canvas-string-ids, application/json" "$url") sed 's/^while(1);//' page.json | jq -c '.[]' >> all.jsonl url=$(echo "$headers" | grep -i '^link:' | grep -oE '<[^>]+>; rel="next"' | grep -oE 'https?://[^>]+') done ``` ``` ### Technical Analysis The pagination example uses fixed filenames in the current directory: - `page.json` is overwritten on every iteration. - `all.jsonl` is opened in append mode. - Neither path is checked for preexistence, ownership, file type, or symbolic links. - Existing data in `all.jsonl` is silently mixed with the current query results. If the command is run in a shared or attacker-influenced directory, an attacker can pre-create either path as a symbolic link. The shell and curl can then write through that link to another file writable by the victim. Even without an attacker, predictable append behavior can corrupt output integrity and retain sensitive educational data from previous runs. ### Attack Path 1. An attacker predicts that the victim will run the pagination example in a writable shared directory. 2. The attacker creates `page.json` or `all.jsonl` as a symbolic link to another path writable by the victim. 3. The victim runs the documented loop. 4. Curl overwrites the target of `page.json`, or the shell appends Canvas records to the target of `all.jsonl`. 5. The attacker causes file corruption or arranges for sensitive data to be written to an unintended location. Alternatively, a stale `all.jsonl` from a previous run causes unrelated records to be combined, undermining confidentiality and int ...[truncated 440 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private temporary directory using `mktemp -d`. 2. Set a restrictive umask, such as `umask 077`, before storing Canvas responses. 3. Register a cleanup trap so temporary response and header files are deleted on normal exit and interruption. 4. Create final output files using exclusive-write semantics and fail if they already exist unless overwrite is explicitly requested. 5. Reject symbolic links and verify that expected files are regular files owned by the current user. 6. Truncate or newly create the output once before pagination instead of silently appending to stale data. 7. Store output only in an explicitly selected directory with appropriate permissions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (12)

Credential Access

High
Category
Privilege Escalation
Content
---
name: canvas-parent-api
description: "Query Canvas LMS (Instructure) from a shell with curl and a bearer access token instead of running the canvas-parent-mcp server — courses, grades, assignments, submissions, calendar, planner, announcements, conversations, discussions, and files for yourself or a linked observee. Use when you want Canvas data without the MCP, in a script, or on a machine where the MCP isn't installed."
---

# Canvas LMS via curl (no MCP)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
---
name: canvas-parent-api
description: "Query Canvas LMS (Instructure) from a shell with curl and a bearer access token instead of running the canvas-parent-mcp server — courses, grades, assignments, submissions, calendar, planner, announcements, conversations, discussions, and files for yourself or a linked observee. Use when you want Canvas data without the MCP, in a script, or on a machine where the MCP isn't installed."
---

# Canvas LMS via curl (no MCP)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
---
name: canvas-parent-api
description: "Query Canvas LMS (Instructure) from a shell with curl and a bearer access token instead of running the canvas-parent-mcp server — courses, grades, assignments, submissions, calendar, planner, announcements, conversations, discussions, and files for yourself or a linked observee. Use when you want Canvas data without the MCP, in a script, or on a machine where the MCP isn't installed."
---

# Canvas LMS via curl (no MCP)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
---
name: canvas-parent-api
description: "Query Canvas LMS (Instructure) from a shell with curl and a bearer access token instead of running the canvas-parent-mcp server — courses, grades, assignments, submissions, calendar, planner, announcements, conversations, discussions, and files for yourself or a linked observee. Use when you want Canvas data without the MCP, in a script, or on a machine where the MCP isn't installed."
---

# Canvas LMS via curl (no MCP)
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
**A. Personal access token (simplest, if allowed)**

Canvas web UI → Account → Settings → **+ New Access Token**. Most
institutions disable this for non-admins, so try B if it's not offered.

```sh
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
| jq -r '.access_token'
```

Export the result as `CANVAS_TOKEN` (access tokens expire in ~1h — re-run
this when calls start 401ing).

## Core call pattern
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill tells users to export long-lived OAuth credentials, including client secret and refresh token, directly into the shell environment via `eval`, but does not warn about shell history, process environment exposure, inherited child processes, or persistence in terminal logs. Because these secrets enable continued token minting, leakage could grant long-term unauthorized access to Canvas data for the user or linked observees.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The skill instructs users to execute `npx canvas-parent-mcp-qr-login` without pinning an exact package version or integrity hash. That allows a future malicious or compromised package release to run arbitrary code on the user's machine, which is especially risky here because the command handles OAuth material and prints credentials intended for shell export.

External Transmission

Medium
Category
Data Exfiltration
Content
token with `curl` directly — no need to re-scan the QR each time:

```sh
curl -s -X POST "$CANVAS_BASE_URL/login/oauth2/token" \
  -d grant_type=refresh_token \
  -d client_id="$CANVAS_CLIENT_ID" \
  -d client_secret="$CANVAS_CLIENT_SECRET" \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The file explains how to query `users/self` or an observee's linked student data, and later examples retrieve profile, grades, submissions, inbox messages, and files. Although the commands are instructional, the document does not include an explicit warning that these requests access sensitive educational records and should only be used with appropriate authorization and care.

External Transmission

Medium
Category
Data Exfiltration
Content
## 8. Recent graded submissions in a course

`student_ids[]` defaults to `self`; `graded_since` defaults to 14 days ago
(ISO 8601, compute it yourself for curl — Canvas doesn't).

```sh
since=$(date -u -v-14d +%Y-%m-%dT%H:%M:%SZ)   # macOS date; use `date -u -d '14 days ago' ...` on GNU
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
fully-qualified, Bearer-authable Canvas URL (no `/api/v1` prefix needed):

```sh
curl -sL -H "Authorization: Bearer $CANVAS_TOKEN" "$FILE_URL" -o /path/to/destination.pdf
```

`-L` follows Canvas's redirect to the actual file storage backend. The MCP's
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.