Back to skill

Security audit

KU Portal

Security checks for vulnerabilities and agentic risk

Overview

This KU portal skill appears purpose-aligned, but it needs review because it stores KUPID passwords locally and auto-installs unpinned code that receives those credentials.

Review this before installing if you will use login features. Prefer not to store your KUPID password in a plaintext file; if you proceed, keep the credential file and parent directory private, avoid shared machines, rotate the password if exposed, and consider pinning or reviewing ku-portal-mcp before running setup.

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

T08 · Insecure Dependencies

Error
Location
scripts/setup.sh:18
Finding
Unpinned Automatically Upgraded Dependency Handles Portal Credentials## Vulnerability Details **File Location**: `scripts/setup.sh:18-21` **Additional Locations**: `README.md:11,45`; `skill.json:15-17`; `ku_query.py:39-40,337` **Vulnerability Type**: Unpinned privileged third-party dependency **Risk Level**: High **Complete Vulnerable Code Snippet**: ```bash # 2) pip upgrade and package installation echo "📥 $PKG installation/update in progress..." "$VENV_DIR/bin/pip" install --upgrade pip -q "$VENV_DIR/bin/pip" install --upgrade "$PKG" -q ``` The dependency is declared without a version constraint: ```json "requires": [ "python3", "pip:ku-portal-mcp" ] ``` The installed package subsequently receives the user's credentials: ```python os.environ["KU_PORTAL_ID"] = creds["id"] os.environ["KU_PORTAL_PW"] = creds["pw"] ``` ```python lms_session = await lms_login(os.environ["KU_PORTAL_ID"], os.environ["KU_PORTAL_PW"]) ``` ### Technical Analysis The setup script installs the latest available `ku-portal-mcp` package and explicitly upgrades it each time setup is run. It does not pin a reviewed version, verify package hashes, or use a dependency lock file. Consequently, the code executed by this Skill can change after the Skill itself has been audited. This dependency is central to the declared portal functionality, so using it is functionally justified. However, automatically trusting every future package release is not the minimum safe privilege necessary for a component that imports executable Python code and handles institutional account credentials. Imported Python packages execute with the same operating-system privileges as the Skill process. The wrapper also places the KUPID identifier and password into environment variables and passes them directly to the dependency's LMS login function. A compromised package release would therefore have immediate access to those credentials and to all other files and resources accessible to the user running the Skill. ...[truncated 1623 chars]
Remediation
## Remediation Suggestions 1. Pin `ku-portal-mcp` to an exact, reviewed version instead of installing the latest release. 2. Maintain a locked requirements file containing cryptographic hashes, and install it with: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` 3. Remove automatic dependency upgrades from normal setup. Perform upgrades only through a deliberate review and release process. 4. Pin and verify transitive dependencies where practical. 5. Record the reviewed dependency version consistently in `scripts/setup.sh`, `README.md`, and `skill.json`. 6. Test package updates in an isolated environment before distributing them. 7. Minimize credential exposure to the dependency and avoid placing credentials in global process environment variables unless required by a verified API.

T09 · Insecure Skill Coding Practices

Warning
Location
README.md:47
Finding
Credential Setup Can Temporarily Create a Readable Plaintext Password File## Vulnerability Details **File Location**: `README.md:47-52` **Additional Locations**: `scripts/setup.sh:30-38`; `ku_query.py:31-40` **Vulnerability Type**: Non-atomic credential-file permission hardening and excessive environment exposure **Risk Level**: Medium **Complete Vulnerable Code Snippet**: ```bash # Credential configuration for login functionality mkdir -p ~/.config/ku-portal cat > ~/.config/ku-portal/credentials.json << 'EOF' {"id": "your-kupid-id", "pw": "your-kupid-password"} EOF chmod 600 ~/.config/ku-portal/credentials.json ``` The setup script recommends the same non-atomic procedure: ```bash CREDS="$HOME/.config/ku-portal/credentials.json" if [ ! -f "$CREDS" ]; then echo "" echo "⚠️ KUPID credential file is missing." echo " Library/menu queries are available, but create the following file before using authenticated functions:" echo "" echo " mkdir -p ~/.config/ku-portal" echo ' echo '\''{"id": "student-number", "pw": "password"}'\'' > ~/.config/ku-portal/credentials.json' echo " chmod 600 ~/.config/ku-portal/credentials.json" else echo "✅ Credential file found" fi ``` The credentials are then copied into process environment variables: ```python def load_credentials(): """Load KUPID credentials from config file.""" if not CREDS_FILE.exists(): print(f"❌ Credential file missing: {CREDS_FILE}") print('Create it using the format {"id": "your-kupid-id", "pw": "your-kupid-password"}.') sys.exit(1) with open(CREDS_FILE) as f: creds = json.load(f) os.environ["KU_PORTAL_ID"] = creds["id"] os.environ["KU_PORTAL_PW"] = creds["pw"] ``` ### Technical Analysis The documented procedure writes the plaintext password before applying mode `0600`. File permissions at creation time depend on the user's current umask. With a common umask of `022`, the new file may initially be c ...[truncated 2377 chars]
Remediation
## Remediation Suggestions 1. Apply a restrictive umask before creating the file: ```bash install -d -m 700 "$HOME/.config/ku-portal" umask 077 cat > "$HOME/.config/ku-portal/credentials.json" << 'EOF' {"id": "your-kupid-id", "pw": "your-kupid-password"} EOF ``` 2. Alternatively, create the file using an operation that applies mode `0600` at creation time rather than correcting permissions afterward. 3. Validate before reading that the credential file: - Is owned by the current user. - Is a regular file rather than a symbolic link. - Has no group or other permissions. 4. Refuse authenticated operation with a clear warning if these checks fail. 5. Prefer an operating-system credential store or keyring over a plaintext JSON file. 6. Avoid copying the password into global environment variables. Pass it only to the narrow authentication function that requires it, then remove references as soon as practical. 7. Update both `README.md` and `scripts/setup.sh` so that all documented setup paths use the hardened procedure.
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (27)

Credential Access

High
Category
Privilege Escalation
Content
# 자격 증명 설정 (로그인 기능 사용 시)
mkdir -p ~/.config/ku-portal
cat > ~/.config/ku-portal/credentials.json << 'EOF'
{"id": "your-kupid-id", "pw": "your-kupid-password"}
EOF
chmod 600 ~/.config/ku-portal/credentials.json
Confidence
86% confidence
Finding
The skill requires users to place portal credentials in a plaintext JSON file under the home directory. Even with restrictive permissions, plaintext credential-at-rest handling increases the blast radius of local compromise, backups, accidental disclosure, or unsafe tooling that reads dotfiles and config directories.

Credential Access

High
Category
Privilege Escalation
Content
cat > ~/.config/ku-portal/credentials.json << 'EOF'
{"id": "your-kupid-id", "pw": "your-kupid-password"}
EOF
chmod 600 ~/.config/ku-portal/credentials.json
```

OpenClaw 스킬 문서 안에서는 `{baseDir}`를 사용할 수 있습니다.
Confidence
84% confidence
Finding
This line reinforces persistent plaintext storage of a username/password credential file. The context is not overtly malicious, but the skill handles SSO/KSSO credentials for university systems, so storing them in a readable local file creates a meaningful credential exposure risk if the endpoint is compromised or synced insecurely.

Credential Access

High
Category
Privilege Escalation
Content
## 로컬 파일 접근 / 보안

- 자격 증명 읽기: `~/.config/ku-portal/credentials.json` (chmod 600 권장)
- 포털 세션 캐시: `~/.cache/ku-portal-mcp/session.json` (30분 TTL)
- LMS 세션 캐시: `~/.cache/ku-portal-mcp/lms_session.json` (약 25분 TTL)
- 서버 로그: `~/.cache/ku-portal-mcp/server.log`
Confidence
88% confidence
Finding
The security section explicitly states that the skill reads credentials from `~/.config/ku-portal/credentials.json`, confirming a design that depends on local plaintext secret access. In a skill context, this is more dangerous because the tool also maintains authenticated sessions and accesses academic/LMS data, so compromise could expose personal educational records and portal access.

Credential Access

High
Category
Privilege Escalation
Content
requires:
      bins: ["python3"]
      config:
        - "~/.config/ku-portal/credentials.json"
        - "~/.cache/ku-portal-mcp/session.json"
        - "~/.cache/ku-portal-mcp/lms_session.json"
        - "~/.cache/ku-portal-mcp/server.log"
Confidence
88% confidence
Finding
The skill explicitly requires a plaintext credential file in the user's home directory and also maintains reusable session caches. Even though this is presented as normal functionality, accessing stored credentials and sessions is security-sensitive because compromise of the skill, logs, or surrounding agent runtime could expose university account access.

Credential Access

High
Category
Privilege Escalation
Content
이 스킬은 로그인/캐시/내보내기 기능 때문에 아래 경로를 사용합니다.

- 읽기: `~/.config/ku-portal/credentials.json` — KUPID 자격 증명
- 쓰기/읽기: `~/.cache/ku-portal-mcp/session.json` — 포털 세션 캐시
- 쓰기/읽기: `~/.cache/ku-portal-mcp/lms_session.json` — LMS 세션 캐시
- 쓰기: `~/.cache/ku-portal-mcp/server.log` — MCP 서버 로그
Confidence
90% confidence
Finding
This section documents direct read access to a credential file and read/write access to session caches outside the skill directory. The context makes the finding more serious because the skill is designed to automate authenticated portal and LMS access, so any leakage of those files could enable account takeover or unauthorized access to academic data.

Credential Access

High
Category
Privilege Escalation
Content
- `menu --date 2026-03-10` — 특정 날짜 메뉴

### 로그인 필요 (KUPID SSO)
자격 증명: `~/.config/ku-portal/credentials.json`
```json
{"id": "your-kupid-id", "pw": "your-kupid-password"}
```
Confidence
93% confidence
Finding
The skill provides a concrete example showing a JSON file containing an ID and password, which normalizes storing credentials in plaintext. In this context, the skill targets SSO-backed university services, so exposure of this file would likely grant broad access to notices, coursework, LMS data, and other personal academic information.

Credential Access

High
Category
Privilege Escalation
Content
SKILL_DIR = Path(__file__).resolve().parent
VENV_DIR = SKILL_DIR / ".venv"
CREDS_FILE = Path.home() / ".config" / "ku-portal" / "credentials.json"


def _check_deps():
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
SKILL_DIR = Path(__file__).resolve().parent
VENV_DIR = SKILL_DIR / ".venv"
CREDS_FILE = Path.home() / ".config" / "ku-portal" / "credentials.json"


def _check_deps():
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
SKILL_DIR = Path(__file__).resolve().parent
VENV_DIR = SKILL_DIR / ".venv"
CREDS_FILE = Path.home() / ".config" / "ku-portal" / "credentials.json"


def _check_deps():
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
echo "  도서관/메뉴 조회는 바로 가능하지만, 로그인 필요 기능은 아래 파일을 먼저 만들어주세요:"
    echo ""
    echo "  mkdir -p ~/.config/ku-portal"
    echo '  echo '\''{"id": "학번", "pw": "비밀번호"}'\'' > ~/.config/ku-portal/credentials.json'
    echo "  chmod 600 ~/.config/ku-portal/credentials.json"
else
    echo "✅ 자격 증명 파일 확인됨"
Confidence
95% confidence
Finding
This line explicitly instructs users to create a file containing their ID and password in plaintext. Because this is an authentication secret for a university portal, compromise of the file could enable unauthorized access to personal academic and account data, and the installer normalizes an unsafe credential handling pattern.

Session Persistence

Medium
Category
Rogue Agent
Content
python3 -m pip install ku-portal-mcp

# 자격 증명 설정 (로그인 기능 사용 시)
mkdir -p ~/.config/ku-portal
cat > ~/.config/ku-portal/credentials.json << 'EOF'
{"id": "your-kupid-id", "pw": "your-kupid-password"}
EOF
Confidence
80% confidence
Finding
The documented flow establishes persistent local authentication material: a long-lived credential file plus cached portal and LMS sessions noted elsewhere in the README. Session persistence is not inherently malicious, but it increases risk on shared or compromised machines because an attacker may be able to reuse valid sessions or harvest credentials without reauthentication.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
cat > ~/.config/ku-portal/credentials.json << 'EOF'
{"id": "your-kupid-id", "pw": "your-kupid-password"}
EOF
chmod 600 ~/.config/ku-portal/credentials.json
```

OpenClaw 스킬 문서 안에서는 `{baseDir}`를 사용할 수 있습니다.
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill invokes a Python script, uses local credential/session files, writes logs and exports, and likely performs network access, but it declares no explicit tool scope or permissions boundary. In an agent environment, missing scope declarations increases the chance that the skill is granted broader shell, file, and network capabilities than users expect, which weakens containment if the underlying script is compromised or misused.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
Credentials are read from a local JSON file and copied into process environment variables without any warning or minimization. Environment variables are often inherited by subprocesses, exposed in debugging contexts, and retained longer than necessary, which increases the chance of credential disclosure in a multi-tool or agent environment.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
Multiple commands perform login and subsequent remote fetches using the user's portal credentials, and the LMS command separately authenticates with those credentials. The file contains no general warning in help text, comments, or prompts that invoking these commands will send authentication and account-associated data over the network.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill adds a cafeteria menu scraping feature that is outside the manifest-described KUPID portal scope. Scope expansion matters in agent skills because it silently broadens what network destinations and data flows users are exposed to, making review and consent less reliable.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The code makes unauthenticated requests to `koreapas.com`, an external site not implied by the core university portal integration. Adding undisclosed third-party scraping increases attack surface, leaks usage metadata to another domain, and can expose users to content or behavior not covered by their expectations for a KUPID-only skill.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script instructs the user to store their KUPID ID and password in a plaintext JSON file under the home directory. Even with a suggested chmod 600, plaintext credential storage increases the risk of credential theft from local compromise, backups, shell history mistakes, or accidental disclosure, and the script does not clearly warn users about these risks or recommend safer storage.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "⚠️  KUPID 자격 증명 파일이 없습니다."
    echo "  도서관/메뉴 조회는 바로 가능하지만, 로그인 필요 기능은 아래 파일을 먼저 만들어주세요:"
    echo ""
    echo "  mkdir -p ~/.config/ku-portal"
    echo '  echo '\''{"id": "학번", "pw": "비밀번호"}'\'' > ~/.config/ku-portal/credentials.json'
    echo "  chmod 600 ~/.config/ku-portal/credentials.json"
else
Confidence
91% confidence
Finding
The script encourages persistent storage of reusable login credentials in a stable location under ~/.config, creating long-lived session/authentication material on disk. In the context of a university portal skill, this increases exposure if the workstation, backups, dotfiles, or user profile are compromised.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
echo ""
    echo "  mkdir -p ~/.config/ku-portal"
    echo '  echo '\''{"id": "학번", "pw": "비밀번호"}'\'' > ~/.config/ku-portal/credentials.json'
    echo "  chmod 600 ~/.config/ku-portal/credentials.json"
else
    echo "✅ 자격 증명 파일 확인됨"
fi
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
echo ""
    echo "  mkdir -p ~/.config/ku-portal"
    echo '  echo '\''{"id": "학번", "pw": "비밀번호"}'\'' > ~/.config/ku-portal/credentials.json'
    echo "  chmod 600 ~/.config/ku-portal/credentials.json"
else
    echo "✅ 자격 증명 파일 확인됨"
fi
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The script's natural-language output, prompts, and usage instructions are consistently presented in Korean. For a general-purpose skill, forcing a single language without offering a user-selectable locale can violate language/locale policy requirements.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Rp1

Low
Category
MCP Rug Pull
Confidence
60% confidence
Finding
pip install without ==version installs the latest release, which could include malicious changes.

Static analysis

No suspicious patterns detected.