Back to skill

Security audit

JWGL Query

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for querying a university JWGL system, but it handles real teacher passwords and authenticated records with weak local protections and broad URL trust.

Install only if you are comfortable letting the skill store teacher JWGL usernames and passwords locally in plaintext and automate logins to configured school URLs. Use a dedicated, least-privileged account if possible, keep config.json out of shared or synced folders and source control, avoid passing passwords on the command line, verify every school URL is the real HTTPS institutional domain before saving it, and avoid debug/probe modes unless you can protect and delete their output files.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/crawl.py:598
Finding

Stored credentials can be submitted to an unvalidated attacker-controlled URL

Content
View full analysis
str: return (url or "").strip().rstrip("/") def derive_login_url(base_url: str) -> str: cleaned = normalize_url(base_url) if not cleaned: return "" return f"{cleaned}{DEFAULT_LOGIN_PATH}" ``` ```python def normalize_school_fields( previous: dict[str, Any] | None = None, *, base_url: str | None = None, login_url: str | None = None ) -> dict[str, Any]: school = dict(previous or {}) if base_url is not None: school["base_url"] = normalize_url(base_url) if login_url is not None: school["login_url"] = normalize_url(login_url) resolved_base_url = normalize_url(school.get("base_url")) resolved_login_url = normalize_url(school.get("login_url")) or derive_login_url(resolved_base_url) if not resolved_base_url: print_json( { "ok": False, "error_code": "MISSING_REQUIRED_FIELDS", "message": "学校 URL 为空,无法保存", "required": ["school", "base_url"], }, exit_code=2, ) school["base_url"] = resolved_base_url school["login_url"] = resolved_login_url return school ``` ```python def cmd_school_add(args: argparse.Namespace) -> None: path = Path(args.config) config = load_config(path) schools = ensure_schools(config) school_name = require_school_name(args.school, action="school-add") previous = schools.get(school_name) if previous and not args.force: print_json( { "ok": False, "error_code": "SCHOOL_ALREADY_EXISTS", "message": f"学校 {school_name} 已存在", "school": school_name ...[truncated 2341 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/manage_accounts.py:29
Finding

Teacher credentials are persisted in plaintext without enforced file permissions

Content
View full analysis
None: apply_runtime_defaults(config) sync_legacy_urls(config) path.parent.mkdir(parents=True, exist_ok=True) with path.open("w", encoding="utf-8") as f: json.dump(config, f, ensure_ascii=False, indent=2) f.write("\n") ``` ```python def normalize_teacher_fields( previous: dict[str, Any] | None = None, *, username: str | None = None, password: str | None = None, email: str | None = None, phone: str | None = None ) -> dict[str, Any]: teacher = dict(previous or {}) if username is not None: teacher["username"] = username if password is not None: teacher["password"] = password if email is not None: teacher["email"] = email if phone is not None: teacher["phone"] = phone if not teacher.get("username") or not teacher.get("password"): print_json( { "ok": False, "error_code": "MISSING_REQUIRED_FIELDS", "message": "老师账号或密码为空,无法保存", "required": ["teacher", "username", "password"], }, exit_code=2, ) return teacher ``` The documented schema stores the secret directly: ```json "teachers": { "某老师": { "username": "teacher_account_a", "password": "REPLACE_ME", "school": "某学校A" } } ``` ### Technical Analysis Passwords are inserted directly into the configuration object and serialized to `config.json` as ordinary JSON. The save operation neither encrypts the credential nor enforces restrictive permissions such as mode `0600`. The resulting access level depends on the process umask and preexisting file mode. If the file already has permis ...[truncated 1154 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/manage_accounts.py:504
Finding

Passwords are accepted and documented as command-line arguments

Content
View full analysis
None: p.add_argument( "--teacher", "--name", dest="teacher", required=require_teacher, help="老师名称(兼容旧写法:--name)", ) p.add_argument( "--username", "--user", "--account", dest="username", help="登录账号(兼容旧写法:--user/--account)", ) p.add_argument( "--password", "--pass", dest="password", help="登录密码(兼容旧写法:--pass)", ) p.add_argument("--email", help="邮箱(可选)") p.add_argument("--phone", help="手机号(可选)") ``` The documented invocation explicitly places the secret in the argument vector: ```bash python3 scripts/manage_accounts.py --config config.json add \ --teacher "某老师" --username "账号" --password "密码" --set-current python3 scripts/manage_accounts.py --config config.json update \ --teacher "某老师" --password "新密码" ``` ### Technical Analysis Operating systems commonly expose process command lines to process-monitoring interfaces and administrative tools. Shells may persist commands in history files, while agent runtimes, terminal recorders, audit systems, and telemetry may log complete invocations. Quoting the password prevents shell word splitting but does not remove it from the process argument vector. The documented usage makes this exposure part of the normal credential-management workflow. ### Attack Path 1. A user or agent invokes the account-management script with `--password`. 2. The password becomes part of the process argument vector. 3. The invocation may be visible in a process listing while the command runs. 4. The shell, agent framework, audit subsystem, or telemetry servi ...[truncated 403 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/crawl.py:670
Finding

Authenticated pages and screenshots are written to unprotected debug artifacts

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/setup.sh:24
Finding

Normal execution can automatically download and execute components without integrity locking

Content
View full analysis
/dev/null PYTHON_BIN="$BASE_DIR/.venv/bin/python" "$PYTHON_BIN" scripts/crawl.py "$@" ``` Environment preparation can automatically run setup: ```bash if [ ! -x "$VENV_PYTHON" ]; then needs_setup=1 elif ! "$VENV_PYTHON" "$CHECK_SCRIPT" | tail -n 1 | grep -qx 'ready'; then needs_setup=1 fi if [ ! -f "$BASE_DIR/config.json" ]; then needs_setup=1 fi if [ "$needs_setup" -eq 1 ]; then bash "$SETUP_SCRIPT" fi ``` Setup downloads packages without hashes: ```bash "$VENV_PYTHON" -m pip install --upgrade pip >/dev/null "$VENV_PYTHON" -m pip install -r requirements.txt ``` Selenium Manager is also allowed to resolve a driver executable: ```python try: sm_args = ["--browser", "chrome", "--skip-driver-in-path"] if Path(browser_binary).exists(): sm_args.extend(["--browser-path", browser_binary]) driver_info = SeleniumManager().binary_paths(sm_args) driver_path = driver_info.get("driver_path") if driver_path: service = ChromeService( executable_path=driver_path, env=service_env, ) return webdriver.Chrome(service=service, options=options) except Exception: pass service = ChromeService(env=service_env) return webdriver.Chrome(service=service, options=options) ``` Package versions are pinned but not hash-locked: ```text selenium==4.28.1 pandas==2.3.3 beautifulsoup4==4.14.3 sqlalchemy==2.0.48 pymysql==1.1.2 ``` ### Technical Analysis Version pinning prevents unintended version selection but does not verify that downloaded ...[truncated 1729 chars]
Remediation
View remediation
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 (42)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill claims to be a user-facing JWGL query helper, but it also instructs automatic environment setup and script execution. That mismatch can be security-relevant because users and platform controls may not anticipate bootstrapping behavior that executes local shell scripts, creating an opportunity for unintended code execution during normal use.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill claims to be a user-facing JWGL query helper, but it also instructs automatic environment setup and script execution. That mismatch can be security-relevant because users and platform controls may not anticipate bootstrapping behavior that executes local shell scripts, creating an opportunity for unintended code execution during normal use.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/manage_accounts.py --config config.json list

Ssd 3

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs persistent storage of user-supplied teacher credentials for future automated queries, which directly increases the attack surface for account compromise. Because this skill targets real university JWGL systems and reuses saved credentials, compromise of the local config could expose teaching schedules, exam arrangements, and potentially broader teacher account access depending on the system's privileges.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/crawl.py (reported line 297)May include surrounding context.

python
service = ChromeService(executable_path=chromedriver_path)
        return webdriver.Chrome(service=service, options=options)

    service_env = os.environ.copy()
    path_parts = [p for p in service_env.get("PATH", "").split(os.pathsep) if p and p != "/usr/local/bin"]
    service_env["PATH"] = os.pathsep.join(path_parts)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly instructs the agent to collect teacher usernames and passwords and write them into a local config.json, but it does not warn that these are sensitive credentials or clarify whether they are stored in plaintext. In a skill designed to access university JWGL systems, this creates a realistic risk of credential exposure through local files, logs, backups, accidental commits, or other processes on the host.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill describes capabilities that require network access plus local file read/write and likely environment access, but it does not declare any explicit tool scope or permission boundaries. That increases the blast radius of misuse, because an agent integrating this skill may grant broader capabilities than users expect, including access to local credential stores and arbitrary remote JWGL endpoints.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The document instructs the agent to always '把结果整理成中文返回', and similar later lines reinforce returning Chinese summaries by default. This is a natural-language locale policy constraint that does not provide user opt-in or an alternative language choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The rule '查询结果优先直接整理成中文摘要返回' imposes a fixed output language. Because the file does not state that users may choose another language, this creates a locale policy violation under the natural-language policy rule.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill explicitly instructs the agent to solicit teacher usernames and passwords in conversation and persist them locally for reuse. Collecting and storing plaintext credentials through a chat-driven flow significantly increases the risk of credential disclosure, accidental logging, compromise of local files, and unauthorized reuse against university systems.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The query flow tells the agent to ask for missing credentials and record them before proceeding, normalizing credential capture as part of ordinary support interactions. In the context of a university JWGL system, this is especially sensitive because teacher accounts may expose personal, scheduling, and exam-administration data, making compromise materially harmful.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.