Back to skill

Security audit

NCCU OJ

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent for solving and submitting NCCUOJ problems, but it handles real account credentials and session cookies in under-protected ways that users should review before installing.

Review this skill carefully before installing. It appears intended for legitimate NCCUOJ problem solving, but do not provide a valuable or reused password through the documented command examples, avoid running it in shared or logged environments, check that NCCUOJ_BASE_URL is unset or points only to the real NCCUOJ site, and remove .nccuoj/cookies.txt after use if you proceed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/session.py:15
Finding
Unrestricted API origin override can redirect credentials, session cookies, and source code<![CDATA[ ## Vulnerability Details **File Location**: `scripts/session.py:15-16`; affected data flows also occur in `scripts/get_problem.py:159-163`, `scripts/submit.py:25-28, 46-57`, and `scripts/check_result.py:32-35, 57-58` **Vulnerability Type**: Unvalidated network destination for sensitive data **Risk Level**: High ### Vulnerable Code ```python # scripts/session.py:15-16 BASE_URL = os.environ.get("NCCUOJ_BASE_URL", "https://nccuoj.ebg.tw") API_URL = f"{BASE_URL}/api" ``` ```python # scripts/get_problem.py:159-163 if args.username and args.password: api_post(opener, cookie_jar, f"{API_URL}/login", { "username": args.username, "password": args.password, }) ``` ```python # scripts/submit.py:46-57 with open(args.code_file, "r") as f: code = f.read() opener, cookie_jar, csrf = get_session() login(opener, cookie_jar, args.username, args.password) payload = { "problem_id": args.problem_id, "language": args.language, "code": code, } if args.contest: payload["contest_id"] = args.contest data = api_post(opener, cookie_jar, f"{API_URL}/submission", payload) ``` ### Technical Analysis The environment variable `NCCUOJ_BASE_URL` completely controls the origin used for API requests. The value is not validated against the documented NCCUOJ hostname, restricted to HTTPS, or subject to an explicit development-mode safeguard. The login, problem retrieval, source submission, and result-checking scripts all import the derived `API_URL`. Consequently, a process that can influence the environment can redirect requests to an arbitrary server. The affected requests include plaintext NCCUOJ usernames and passwords inside JSON request bodies, authentication cookies managed by the shared cookie jar, and complete contents of the selected source file. Although transmitting credentials and source code to the legitimate NCCUOJ service is necessary for authenticated submissions, permitting silent redirection to an arbitrary origin exc ...[truncated 1571 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `NCCUOJ_BASE_URL` configurability from production operation and use the fixed documented origin `https://nccuoj.ebg.tw`. 2. If an override is necessary for development: - Require an explicit development-mode option. - Require the `https` scheme. - Validate the parsed hostname against a strict allowlist. - Reject URLs containing embedded credentials, fragments, unexpected paths, or unsupported ports. - Display the effective destination and require confirmation before sending credentials. 3. Prevent cross-origin redirects for requests containing passwords, cookies, or source code. Validate the final response URL after redirects. 4. Separate authenticated and public clients so public problem retrieval cannot accidentally reuse authentication cookies with an untrusted origin. 5. Add tests covering malicious values such as HTTP URLs, lookalike domains, embedded credentials, and redirect chains. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/submit.py:40
Finding
Passwords are accepted and documented as command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/get_problem.py:152-153`, `scripts/submit.py:40-41`, `scripts/check_result.py:52-53`; insecure usage is documented in `SKILL.md:59-69, 115-123, 151-155, 164-171` **Vulnerability Type**: Credential exposure through process arguments and command history **Risk Level**: Medium ### Vulnerable Code ```python # scripts/get_problem.py:152-153 parser.add_argument("--username", help="NCCUOJ username (required for some problems)") parser.add_argument("--password", help="NCCUOJ password (required for some problems)") ``` ```python # scripts/submit.py:40-41 parser.add_argument("--username", required=True, help="NCCUOJ username") parser.add_argument("--password", required=True, help="NCCUOJ password") ``` ```python # scripts/check_result.py:52-53 parser.add_argument("--username", required=True, help="NCCUOJ username") parser.add_argument("--password", required=True, help="NCCUOJ password") ``` The Skill explicitly instructs users and Agents to place credentials in commands: ```bash python $SCRIPTS/get_problem.py <problem_id> --username <username> --password <password> python $SCRIPTS/submit.py <problem_internal_id> "C++" .nccuoj/solution/public/<problem_id>/solution.cpp --username <username> --password <password> python $SCRIPTS/check_result.py <submission_id> --username <username> --password <password> --poll ``` ### Technical Analysis Command-line arguments are not an appropriate channel for passwords. Depending on the operating system and execution environment, arguments may be exposed through: - Shell history. - Process inspection interfaces and process-listing tools. - Agent tool-call transcripts and execution logs. - CI/CD logs, telemetry, or audit records. - Error reports that preserve the original command. - Terminal scrollback or copied command examples. Authentication is required for private problems, contests, submissions, and result access. However, requiring the password to appear in the p ...[truncated 1252 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the required `--password` argument. 2. Prompt interactively with Python's `getpass.getpass()` so the password is not echoed or included in the process argument vector. 3. For noninteractive execution, support a protected credential source such as: - Standard input through a clearly defined secret channel. - An operating-system credential manager. - A file descriptor supplied by the caller. - A secret file whose permissions are verified as owner-only. 4. Do not recommend environment variables as the primary alternative because they may also be exposed through diagnostics or child-process environments. 5. Update every `SKILL.md` example to omit literal passwords and instruct the Agent never to include credentials in generated command text, logs, or responses. 6. Prefer session reuse after one secure login so the user does not repeatedly provide the account password. 7. If backward compatibility requires temporary support for `--password`, mark it as deprecated and emit a warning explaining the exposure risk. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/session.py:20
Finding
Authentication cookies are persistently stored without enforced restrictive permissions or cleanup<![CDATA[ ## Vulnerability Details **File Location**: `scripts/session.py:20-21, 31-39, 52-61, 74-87` **Vulnerability Type**: Insecure persistence of authentication material **Risk Level**: Medium ### Vulnerable Code ```python # scripts/session.py:20-21 _NCCUOJ_DIR = os.path.join(os.getcwd(), ".nccuoj") _COOKIE_FILE = os.path.join(_NCCUOJ_DIR, "cookies.txt") ``` ```python # scripts/session.py:31-39 def _ensure_nccuoj_dir(): """Ensure .nccuoj directory exists.""" os.makedirs(_NCCUOJ_DIR, exist_ok=True) def _build_opener(): _ensure_nccuoj_dir() cookie_jar = http.cookiejar.MozillaCookieJar(_COOKIE_FILE) if os.path.exists(_COOKIE_FILE): cookie_jar.load(ignore_discard=True, ignore_expires=True) ``` ```python # scripts/session.py:52-61 def get_session(): """Create a session with CSRF token pre-fetched via GET /api/profile.""" opener, cookie_jar = _build_opener() # Hit profile to get csrftoken cookie req = urllib.request.Request(f"{API_URL}/profile", method="GET") opener.open(req) cookie_jar.save(ignore_discard=True, ignore_expires=True) csrf = _get_csrf_token(cookie_jar) return opener, cookie_jar, csrf ``` ```python # scripts/session.py:74-87 def api_post(opener, cookie_jar, url, payload): """Make a POST request with CSRF token and return parsed JSON data.""" csrf = _get_csrf_token(cookie_jar) data = json.dumps(payload).encode() req = urllib.request.Request(url, data=data, method="POST") req.add_header("Content-Type", "application/json") req.add_header("Referer", BASE_URL + "/") req.add_header("Origin", BASE_URL) if csrf: req.add_header("X-CSRFToken", csrf) with opener.open(req) as resp: body = json.loads(resp.read().decode()) cookie_jar.save(ignore_discard=True, ignore_expires=True) ``` ### Technical Analysis The shared session component persists cookies to the predictable workspace path `.nccuoj/cookies.txt`. It does not explicitly create the ...[truncated 1950 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create `.nccuoj` with owner-only permissions (`0700`) and create the cookie file with owner read/write permissions (`0600`). 2. Verify ownership and permissions before loading an existing cookie file. Refuse to use files that are group-readable, world-readable, unexpectedly owned, or symbolic links. 3. Use atomic file creation with no-follow and exclusive-create protections where supported. 4. Do not persist session-only cookies by default. Avoid `ignore_discard=True` unless the user explicitly requests persistent sessions. 5. Do not load expired cookies with `ignore_expires=True`. 6. Provide explicit logout and session-cleanup commands that invalidate the server-side session where supported and securely remove the local cookie file. 7. Add `.nccuoj/` to `.gitignore` and document that it must not be included in archives, build artifacts, backups, or shared workspace exports. 8. Consider storing authentication material in the operating system's credential store instead of a plaintext workspace file. ]]>
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 (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares no explicit tool restrictions even though it instructs use of network access, file reads, and environment-adjacent execution behavior through helper scripts. Without declared scope, an agent may apply broader capabilities than necessary, increasing the blast radius if the skill or referenced scripts are modified or abused.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The skill instructs users to pass NCCUOJ usernames and passwords directly as command-line arguments. CLI arguments are commonly exposed through shell history, process listings, logs, and telemetry, so this unnecessarily risks credential disclosure to other local users or monitoring systems.

Context-Inappropriate Capability

Medium
Confidence
99% confidence
Finding
The submission and result-check workflow repeats the insecure pattern of embedding plaintext credentials in command invocations. This expands exposure because the highest-value operations in the skill—submission and account-authenticated status checks—would routinely place credentials where they can be captured by logs or process inspection.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill not only suggests passing credentials on the command line but does so without any warning about visibility in shell history, audit logs, or process tables. In this skill's context, users are likely to follow canned commands verbatim, making silent credential exposure more probable.

Context-Inappropriate Capability

Medium
Confidence
99% confidence
Finding
The contest workflow also requires plaintext credentials in command arguments, creating the same disclosure risk in a context where compromise may additionally affect active contest participation. Repeated insecure guidance normalizes unsafe handling of passwords and increases the chance of accidental leakage across shared machines or recorded terminal sessions.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script accepts the account password via a command-line argument, which can expose credentials through shell history, process listings, job control logs, and CI/runtime telemetry. Because this skill is specifically for logging into an online judge and handling real user accounts, the issue is more dangerous in context: users are likely to supply actual NCCUOJ credentials that could be recovered by other local users or monitoring tools.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code accepts credentials from command-line arguments and immediately sends them in an HTTP login request, which is a safety-relevant network operation involving sensitive data. While the module docstring shows how to supply credentials, it does not warn that the password will be transmitted to a remote service or exposed via command-line usage patterns.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script persists authenticated session cookies to a predictable file under the current working directory and reloads them on later runs, but provides no disclosure, consent flow, or permission hardening. If the workspace is shared, backed up, or has weak filesystem permissions, another local user or process could reuse those cookies to access the NCCUOJ account without credentials.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script requires the password to be passed as a command-line argument, which commonly exposes credentials through shell history, process listings, job-control logs, and CI/CD command output. Even if the login request itself is expected behavior for this skill, the lack of any safer input method or warning makes accidental credential disclosure likely.

Static analysis

No suspicious patterns detected.