Back to skill

Security audit

Canvas LMS IDP Auto Token Refresh

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it handles institutional login credentials and Canvas tokens while under-disclosing sensitive local artifact storage and token-deletion risks.

Review carefully before installing. Use only in an isolated local environment, avoid storing your institutional password in plaintext where possible, do not share debug_output, and patch or configure the tool so diagnostic files are opt-in, redacted, owner-only, and cleaned up. Treat cleanup-old-tokens as destructive and prefer a dry run until deletion behavior is confirmed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/elearning_login.py:100
Finding

Authentication Artifacts and Canvas API Tokens Are Persisted Unconditionally

Content
View full analysis
None: base_dir.mkdir(parents=True, exist_ok=True) target = base_dir / filename target.write_text(content, encoding="utf-8") LOGGER.debug("已写入调试文件:%s", target) ``` ```python login_result: LoginPreparationResult = auth_client.login_and_prepare_session(dry_run=args.dry_run) dump_debug_artifacts(debug_dir, "entry_response.html", login_result.entry_response.text) dump_debug_artifacts( debug_dir, "query_auth_methods.json", json.dumps(login_result.auth_methods_body, ensure_ascii=False, indent=2), ) dump_debug_artifacts( debug_dir, "get_js_public_key.json", json.dumps(login_result.public_key_body, ensure_ascii=False, indent=2), ) if args.dry_run: LOGGER.info("dry-run 完成:已拿到 lck/authChainCode/publicKey") dump_cookies(debug_dir, auth_client.session) return 0 LOGGER.info("RSA 加密完成。cipher_len=%s", len(login_result.encrypted_password or "")) auth_body = login_result.auth_body or {} dump_debug_artifacts( debug_dir, "auth_execute.json", json.dumps(auth_body, ensure_ascii=False, indent=2), ) LOGGER.info("已提取 loginToken(masked)=%s", mask(login_result.login_token or "")) if login_result.authn_engine_response is not None: dump_debug_artifacts(debug_dir, "authn_engine_response.html", login_result.authn_engine_response.text) dump_cookies(debug_dir, auth_client.session) ``` ```python csrf_token = auth_client.extract_csrf() token_body = token_manager.create_canvas_token(csrf_token) dump_debug_artifacts( debug_dir, "create_token.json", json.dumps(token_body, ensure_ascii=False, indent=2), ) ``` ### Technical Analysis The ...[truncated 3329 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/token_ops.py:104
Finding

Unvalidated Token Deletion URL Can Receive a Canvas CSRF Token

Content
View full analysis
Remediation
View remediation
`. 3. Reject all cross-origin targets before attaching CSRF or other sensitive headers: ```python from urllib.parse import urljoin, urlsplit resolved = urljoin(self.config.settings_referer, token.delete_url) base = urlsplit(self.config.settings_referer) target = urlsplit(resolved) same_origin = ( target.scheme == "https" and target.hostname == base.hostname and target.port == base.port and target.username is None and target.password is None ) expected_path = re.fullmatch( r"/api/v1/users/self/tokens/\d+", target.path, ) if not same_origin or not expected_path or target.fragment: raise FlowError("Rejected untrusted token deletion URL") ``` 4. Derive deletion URLs from validated numeric token IDs instead of trusting arbitrary URLs embedded in HTML. 5. Avoid hardcoding the `Origin` header to Fudan while allowing configurable Canvas endpoints. Derive it from the separately validated trusted Canvas origin. 6. Disable redirects for destructive requests or validate every redirect target before following it: ```python allow_redirects=False ``` 7. Add tests covering: - Cross-origin absolute URLs. - HTTP rather than HTTPS. - Lookalike hostnames. - Embedded credentials. - Nonstandard ports. - Protocol-relative URLs. - Redirects to another origin. - Unexpected deletion paths. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (21)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 28)May include surrounding context.

md
pip install -r requirements.txt

# 3. Configure credentials
cp .env.example .env
# Edit .env — fill in ELEARNING_USERNAME and ELEARNING_PASSWORD

# 4. Test (dry-run — only fetches public key, no login)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 29)May include surrounding context.

md
# 3. Configure credentials
cp .env.example .env
# Edit .env — fill in ELEARNING_USERNAME and ELEARNING_PASSWORD

# 4. Test (dry-run — only fetches public key, no login)
python elearning_login.py --dry-run --debug

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

The skill instructs users to place institutional SSO credentials in a local .env file so the automation can replay IDP login and mint fresh Canvas API tokens. Even though the document says not to commit the file, concentrating reusable username/password secrets in a plaintext file materially increases credential exposure risk through local compromise, accidental disclosure, shell/editor tooling, backups, or misuse by the automation itself.

Content

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

Copy .env.example to .env and fill in:

bash
cp scripts/.env.example scripts/.env

Required fields:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The quick-start commands prominently include --cleanup-old-tokens, which performs deletion of existing API tokens with the same purpose label, but the surrounding instructions do not foreground that this is a destructive action. In an automation skill that manages authentication material, users may run the example verbatim and unintentionally revoke still-in-use tokens, causing service disruption or breaking other integrations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This file explicitly accepts raw username and password values, encrypts the password, and submits credentials to remote authentication endpoints to establish a session. In context this is the intended purpose of the skill, but it still creates real security risk because credential collection, session establishment, and token handling are implemented without any visible user-consent prompt, storage policy, or safeguards against misuse if the skill is embedded in a broader agent workflow.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/auth_session.py (reported line 530)May include surrounding context.

python
if re.fullmatch(r"[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+", text):
            return text
        try:
            payload = __import__("json").loads(text)
        except Exception:
            return ""
        return find_jwt_token(payload)

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code writes authentication-related responses and session cookie data to local debug files. Even though cookie values are masked, the stored responses may contain login tokens, auth state, identifiers, or other session artifacts that can expose authentication flow details or secrets if the debug directory is accessible to other users or collected by tooling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Old-token cleanup can be activated via environment-derived defaults rather than an explicit command-line action by the current user. Because token deletion is destructive and may remove valid credentials, this behavior can surprise operators, break integrations, or be abused in automation environments where environment variables are inherited unintentionally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

During normal execution, the script dumps entry responses, auth method responses, public key responses, authentication payloads, optional auth engine HTML, and cookies to disk without prompting the user. These artifacts can contain sensitive login and session data, creating credential exposure risk through local compromise, backups, CI logs, or shared workspaces.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script creates personal access tokens and can optionally delete older tokens matching a purpose, which is a sensitive credential-management capability. In this file, that destructive behavior can be enabled from environment-driven defaults and runs without an interactive confirmation or strong scope restriction, increasing the chance of accidental credential loss or misuse.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code issues DELETE requests to remove existing tokens, which is a destructive operation affecting user credentials. While there is logging for dry-run mode and failure/completion, there is no explicit confirmation prompt or clear user-facing warning at the point where real deletion occurs in this file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The session header forces Accept-Language to prefer zh-CN, and the login flow also later fixes locale to zh-CN. This is a natural-language/locale policy concern because the skill does not provide opt-in or configuration for users who may prefer a different language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The authnEngine endpoint is invoked with locale=zh-CN embedded in the URL. Because the file does not expose locale selection or document a justified region-specific constraint, this appears to force a specific locale in violation of the stated policy criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

Multiple log messages, help strings, and printed status outputs are written only in Chinese, which imposes a specific language on users without opt-in. This is a natural-language policy concern when no locale selection or documented region-specific scope is provided in the file.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency uses a lower-bound version specifier rather than a fully pinned version, which makes builds non-reproducible and can allow unexpected or vulnerable releases to be installed later. In a security-sensitive environment, this weakens supply-chain control and makes it harder to verify whether known advisories affect the deployed package.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
pycryptodome>=3.20.0
python-dotenv>=1.0.1

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin a specific version, it is impossible to determine from this file whether an affected release may be installed. This uncertainty increases the chance of shipping a vulnerable HTTP client, which can matter if the skill performs network access or handles credentials.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

beautifulsoup4 is not pinned to a specific version, so installations may resolve to different releases over time. This creates supply-chain uncertainty and can introduce vulnerable or incompatible versions without any change to the manifest.

Content

Scanner excerpt · scripts/requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
pycryptodome>=3.20.0
python-dotenv>=1.0.1

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

pycryptodome is an unpinned cryptographic dependency, which is more sensitive than a typical library because cryptographic flaws can directly affect confidentiality or integrity. Allowing resolver drift here makes it difficult to guarantee that only vetted, non-vulnerable releases are used.

Content

Scanner excerpt · scripts/requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
pycryptodome>=3.20.0
python-dotenv>=1.0.1

Unverifiable Dependency: pycryptodome has 4 known advisory(ies) (CVE-2018-15560 (PyCryptodome integer overflow vulnerability); CVE-2023-52323 (PyCryptodome and pycryptodomex side-channel leakage for OAEP decryption); CVE-2018-15560 (PyCryptodome before 3.6.6 has an integer overflow in the data_len variable in AE) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding

pycryptodome has known security advisories, and the lack of version pinning means a vulnerable cryptographic release could be installed without visibility from the manifest alone. Because this library underpins encryption and decryption operations, affected versions can have outsized impact on confidentiality, integrity, or side-channel resistance.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

python-dotenv is specified with a minimum version only, so future installations may pull different releases with different security characteristics. This is a supply-chain hygiene issue that becomes more important if the package is used to read or modify environment configuration on developer or production systems.

Content

Scanner excerpt · scripts/requirements.txt (reported line 4)May include surrounding context.

text
requests>=2.31.0
beautifulsoup4>=4.12.0
pycryptodome>=3.20.0
python-dotenv>=1.0.1

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding

python-dotenv has published advisories, and without an exact pinned version there is no assurance that installed environments avoid affected releases. If the package is used in tooling that writes or loads .env files, a vulnerable version could expose configuration integrity or file-system safety issues.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.