Back to skill

Security audit

pixiv-skill

Security checks for vulnerabilities and agentic risk

Overview

This Pixiv skill is broadly aligned with Pixiv automation, but its account credential handling is inconsistent and includes a real risk of exposing OAuth tokens during image downloads.

Review before installing. Do not put Pixiv cookies or OAuth tokens in a shared repository or broad workspace, treat config.yaml as a secret file, and avoid download/like/follow/monitor commands unless you are comfortable with authenticated Pixiv account actions. The publisher should align the authentication model, remove or fix disabled login paths, avoid sending bearer tokens to image hosts, pin dependencies, and add explicit confirmation guidance for account-changing actions.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pixiv.py:446
Finding
OAuth Bearer Token May Be Disclosed to an Unvalidated Download Host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/pixiv.py:446-472` **Vulnerability Type**: Authenticated request to an unvalidated URL **Risk Level**: High ### Vulnerable Code ```python pages = illust.get("meta_pages") or [] urls = [] if pages: for p in pages: original = p.get("image_urls", {}).get("original") if original: urls.append(original) else: original = illust.get("meta_single_page", {}).get("original_image_url") if original: urls.append(original) if not urls: print(f"作品{illust_id}无可下载原图") return False success_count = 0 for idx, url in enumerate(urls): ext = os.path.splitext(urlparse(url).path)[1] or ".jpg" save_path = save_dir / f"{illust_id}_p{idx}{ext}" if save_path.exists(): print(f"作品{illust_id}_p{idx}已存在,跳过") success_count += 1 continue headers = self._app_headers() headers["Referer"] = "https://app-api.pixiv.net/" try: resp = self.session.get(url, headers=headers, stream=True, timeout=40) ``` ### Technical Analysis The image URLs are obtained from the Pixiv API response and used without validating their scheme or destination hostname. The request headers are generated by `_app_headers()`, which includes the user's Pixiv OAuth access token in an `Authorization: Bearer ...` header. This creates a credential-forwarding vulnerability. If an image URL is unexpectedly changed to an attacker-controlled URL—such as through a compromised upstream response, malicious proxy, or service-side data integrity failure—the script sends the bearer token to that destination. Redirect behavior can create the same concern unless every redirect target is validated. Image downloads generally should not receive an account-level OAut ...[truncated 1373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not include the Pixiv OAuth bearer token in image CDN requests unless the destination explicitly requires it. 2. Validate every download URL before issuing a request: - Require the `https` scheme. - Maintain an explicit allowlist of expected Pixiv image domains. - Reject URLs containing embedded credentials. - Reject unexpected ports. 3. Disable automatic redirects for the initial request or validate every redirect destination before following it. 4. Use a dedicated download session with no default authentication headers. 5. Construct only the minimum headers needed for image retrieval, such as an approved `Referer` and user agent. 6. Fail closed when a URL does not match the expected domain policy. 7. Revalidate the final response URL before writing response content. A safer pattern is: ```python parsed = urlparse(url) allowed_hosts = {"i.pximg.net"} if parsed.scheme != "https" or parsed.hostname not in allowed_hosts: raise ValueError("Untrusted image URL") download_headers = { "User-Agent": APP_USER_AGENT, "Referer": "https://www.pixiv.net/", } resp = self.session.get( url, headers=download_headers, stream=True, timeout=40, allow_redirects=False, ) ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/get_rank.py:15
Finding
Pixiv Session Cookie Is Stored and Read from a Plaintext Configuration File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/get_rank.py:15-31` **Vulnerability Type**: Insecure storage of reusable session credentials **Risk Level**: Medium ### Vulnerable Code ```python with open("config.yaml", "r", encoding="utf-8") as f: config = yaml.safe_load(f) pixiv_cfg = config.get("pixiv", {}) download_dir = Path(pixiv_cfg.get("download_dir", "./downloads")) / "daily_rank" download_dir.mkdir(parents=True, exist_ok=True) session = requests.Session() session.headers.update( { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Cookie": pixiv_cfg.get("cookie", ""), "Referer": "https://www.pixiv.net/", } ) proxy = pixiv_cfg.get("proxy") ``` The documented configuration workflow also instructs users to place a reusable session cookie directly in `config.yaml`: ```yaml pixiv: cookie: "PHPSESSID=your-value;" ``` ### Technical Analysis The legacy ranking script reads the Pixiv session cookie directly from a plaintext YAML file. A Pixiv `PHPSESSID` value is a reusable authentication credential and should be protected at a level comparable to a password or API token. Although `PixivConfig.save()` elsewhere in the project applies mode `0600` when it writes a configuration file, users following the documented copy-and-edit workflow may create `config.yaml` manually. The legacy script does not verify or correct the file's permissions before reading the cookie. The project also does not include evidence of a `.gitignore` rule protecting `config.yaml`. Consequently, the file may be readable by other local users, collected by broad backup processes, or accidentally committed to source control. ### Attack Path 1. A user copies the example configuration to `config.yaml`. 2. The user inserts a valid Pixiv `PHPSESSID` cookie as instructed by the documentation. 3. The resulting file retains permissive filesystem pe ...[truncated 801 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential store or secret-management service rather than a plaintext YAML file. 2. If file-based storage must remain supported: - Require the credential file to be owned by the current user. - Require mode `0600` on POSIX systems. - Refuse to load credentials from a group-readable or world-readable file. 3. Add `config.yaml`, authentication session files, and generated credential files to `.gitignore`. 4. Support loading the cookie from a dedicated environment variable or secret-file path. 5. Avoid printing cookies or including them in exception messages. 6. Document that the cookie is a sensitive credential and provide explicit revocation and rotation instructions. 7. Remove the legacy Cookie-based path if OAuth is now the intended authentication mechanism. 8. Align `README.md`, `SKILL.md`, `config.example.yaml`, and the implementation so users are not encouraged to use an obsolete credential workflow. Example permission check: ```python config_path = Path("config.yaml") mode = stat.S_IMODE(config_path.stat().st_mode) if mode & (stat.S_IRWXG | stat.S_IRWXO): raise PermissionError("config.yaml must not be accessible by group or other users") ``` ]]>

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unpinned and Unnecessary Dependencies Increase Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-10` **Vulnerability Type**: Unconstrained and excessive third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```text requests>=2.31.0 PyYAML>=6.0 beautifulsoup4>=4.12.2 tqdm>=4.65.0 Pillow>=10.0.0 fake-useragent>=1.4.0 aiohttp>=3.8.6 asyncio>=3.4.3 pycryptodome>=3.19.0 qrcode>=7.4.2 ``` ### Technical Analysis Every dependency uses a lower-bound-only version constraint. This permits package managers to install any future release, including versions that have not been reviewed with this project. No lock file or package hashes are provided, so installations are not reproducible and package integrity is not pinned to reviewed artifacts. The audited scripts import only a subset of the listed packages. Unused dependencies unnecessarily increase the number of publishers, package releases, build scripts, and transitive dependencies trusted during installation. The explicit `asyncio` dependency is particularly unnecessary for supported Python 3 versions because `asyncio` is part of the Python standard library. Installing a third-party package with the same name increases ambiguity and avoidable supply-chain exposure. This finding does not establish that any listed package is currently malicious. The risk arises from unconstrained future resolution and installation of components not required by the audited implementation. ### Attack Path 1. A user installs the project dependencies with `pip install -r requirements.txt`. 2. The package resolver selects the latest versions satisfying the lower-bound constraints. 3. A future release, compromised maintainer account, malicious distribution artifact, or vulnerable transitive dependency enters the permitted version range. 4. The unreviewed package is downloaded and installed. 5. Malicious installation hooks or imported runtime code execute with the privileges of the user performing the installation or running the Skill. # ...[truncated 453 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove dependencies that are not imported or otherwise required by the shipped scripts. 2. Remove the third-party `asyncio` package because supported Python versions provide it in the standard library. 3. Pin each direct dependency to a reviewed version. 4. Generate and commit a lock file containing exact transitive versions. 5. Use cryptographic hashes for deployment installations, such as a hash-locked requirements file with `pip --require-hashes`. 6. Run dependency vulnerability and license scanning in continuous integration. 7. Use an isolated virtual environment and avoid installing dependencies as a privileged system account. 8. Establish a controlled update process that reviews release notes and reruns security tests before dependency upgrades. Based on the audited imports, the minimal direct dependency set should be reassessed around the packages actually used by the scripts, including `requests`, `PyYAML`, and `tqdm`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented security model says the skill uses only Cookie authentication and does not perform automatic login, while the referenced behavior reportedly uses OAuth bearer tokens and token-refresh/login scaffolding for authenticated actions. This mismatch is dangerous because operators may trust a narrower credential model than what the code actually exercises, leading to accidental exposure of more powerful tokens, unexpected account actions, and weakened review of the real auth path.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README instructs users to store a live Pixiv authentication cookie directly in a local config file, but does not warn that the cookie is a sensitive credential equivalent to an authenticated session token. This increases the risk of accidental disclosure through source control, logs, backups, screenshots, or file sharing, which could allow unauthorized account access and misuse of Pixiv actions such as viewing, downloading, liking, bookmarking, or following on behalf of the user.

Lp3

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding
The skill documentation exposes capabilities that imply file access, shell execution, and network operations, but it does not declare any tool scope restrictions such as allowed-tools or permissions. In an agent environment, that omission weakens containment and can let the skill be invoked with broader capabilities than users or orchestrators expect, increasing the chance of unintended file/system/network actions.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs users to place a live Pixiv cookie in config.yaml but does not explicitly warn that this is a sensitive credential equivalent to account access. In agent or shared workspace contexts, such credentials can be exposed via logs, repository commits, backups, or broader file-read permissions, enabling account takeover or unauthorized use.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The documentation describes like/follow actions without clearly warning that they mutate the user's Pixiv account state. In an agent setting, insufficient disclosure can cause accidental or unauthorized social/account actions, creating privacy, trust, and account-integrity risks even if the API calls are otherwise legitimate.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This manifest-style configuration file uses Chinese-only natural-language comments and labels throughout, such as the title and inline field explanations. Because the file does not offer a language choice or document that the skill is intended only for a Chinese-speaking or region-specific audience, it appears to impose a specific language without user opt-in.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The client headers hard-code "Accept-Language": "zh-cn", which enforces a specific language/locale choice for requests. The file does not offer a user-selectable locale or document why Chinese is required, so this is a natural-language policy violation under the locale-choice rule.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The skill metadata and disabled login commands tell users to authenticate via a Cookie in config.yaml, but the actual API implementation uses OAuth bearer and refresh tokens from pixiv.auth. This discrepancy can cause operators to place long-lived secrets into config files unnecessarily, misunderstand what credentials are active, and mishandle authentication data in ways that increase secret exposure risk.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The application request headers again hard-code "Accept-Language": "zh-cn" for normal API calls. Because the skill does not present a locale option or clearly justify a China-specific restriction, it violates the language/locale policy criterion.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The command registrations advertise working login capabilities such as "交互式 OAuth 登录", "浏览器自动登录并自动提取回调", and "生成 OAuth 授权链接 / 提交授权回调". However, the corresponding handlers at L657-L676 immediately raise an error saying these login flows were removed and instruct the user to use cookies manually, which directly contradicts the documented command intent.

Natural-Language Policy Violations

Low
Confidence
86% confidence
Finding
The user-facing skill documentation is presented in Chinese throughout, with no indication that the language is optional or that the skill is intentionally region- or locale-specific. This can violate language or locale policy when a skill forces a specific language without user opt-in.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
PyYAML>=6.0
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Confidence
98% confidence
Finding
The dependency specifier uses a lower bound only, so builds may resolve to different versions over time, including versions with newly introduced bugs or supply-chain compromises. In a skill that performs authenticated network actions using Pixiv cookies, dependency drift increases the chance of pulling a vulnerable HTTP client or parser into a privileged workflow.

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
97% confidence
Finding
requests has multiple known advisories, and because the manifest does not pin an exact version, it is impossible to verify whether deployments avoid affected releases. In this skill, requests would likely carry authenticated Pixiv traffic and cookies, so a vulnerable version could expose credentials, mishandle redirects, or weaken transport assumptions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
PyYAML>=6.0
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Pillow>=10.0.0
Confidence
98% confidence
Finding
PyYAML is unpinned, so installations may pull different releases depending on time and environment, making security posture non-reproducible. Because this skill explicitly relies on config.yaml for cookie-based authentication, an unsafe or vulnerable YAML parser version is especially relevant to handling sensitive local configuration.

Unverifiable Dependency: PyYAML has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
98% confidence
Finding
PyYAML has a history of unsafe deserialization and parsing flaws, and the unpinned manifest prevents verification that a safe version is installed. Since the skill depends on config.yaml for cookie-based authentication, any vulnerable YAML handling directly increases the risk to local secrets and execution safety.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
PyYAML>=6.0
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Pillow>=10.0.0
fake-useragent>=1.4.0
Confidence
94% confidence
Finding
beautifulsoup4 is specified with a minimum version only, which makes installs non-deterministic and can introduce vulnerable or incompatible releases later. While this library is lower risk than auth or crypto components, it still processes remote content and should be controlled in a network-scraping skill.

Unpinned Dependencies

Low
Category
Supply Chain
Content
requests>=2.31.0
PyYAML>=6.0
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Pillow>=10.0.0
fake-useragent>=1.4.0
aiohttp>=3.8.6
Confidence
93% confidence
Finding
tqdm is unpinned, so future installs may select versions with known CLI or packaging-related issues. Although its direct security criticality is lower, unpinned tooling still weakens reproducibility and can expand supply-chain exposure.

Unverifiable Dependency: tqdm has 4 known advisory(ies) (CVE-2024-34062 (tqdm CLI arguments injection attack); CVE-2016-10075 (TDQM Arbitrary Code Execution); CVE-2016-10075 (The tqdm._version module in tqdm versions 4.4.1 and 4.10 allows local users to e) +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
88% confidence
Finding
tqdm has published advisories, and without exact pinning there is no assurance that the installed version is unaffected. The practical danger here is lower than for parser or network libraries, but unverifiable versions still weaken supply-chain hygiene.

Unpinned Dependencies

Low
Category
Supply Chain
Content
PyYAML>=6.0
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Pillow>=10.0.0
fake-useragent>=1.4.0
aiohttp>=3.8.6
asyncio>=3.4.3
Confidence
97% confidence
Finding
Pillow is unpinned even though image libraries have a long history of parsing vulnerabilities and resource-consumption issues. Since this skill downloads and processes artwork on demand, dependency drift in an image parser materially increases the attack surface from untrusted remote files.

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +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
97% confidence
Finding
Pillow has many historical vulnerabilities including parsing bugs and resource exhaustion issues, and the unpinned requirement means affected versions cannot be ruled out. Because this skill downloads and handles images from external sources, a vulnerable Pillow release materially increases risk from malicious or malformed image files.

Unpinned Dependencies

Low
Category
Supply Chain
Content
beautifulsoup4>=4.12.2
tqdm>=4.65.0
Pillow>=10.0.0
fake-useragent>=1.4.0
aiohttp>=3.8.6
asyncio>=3.4.3
pycryptodome>=3.19.0
Confidence
90% confidence
Finding
fake-useragent is unpinned, which can lead to non-reproducible builds and supply-chain risk from future releases. The direct exploit impact is lower than core network or parser libraries, but it still introduces avoidable uncertainty in a scraping-oriented skill.

Unpinned Dependencies

Low
Category
Supply Chain
Content
tqdm>=4.65.0
Pillow>=10.0.0
fake-useragent>=1.4.0
aiohttp>=3.8.6
asyncio>=3.4.3
pycryptodome>=3.19.0
qrcode>=7.4.2
Confidence
98% confidence
Finding
aiohttp is unpinned, allowing future installations to resolve to different versions that may contain security flaws in HTTP handling, cookies, or request parsing. Because the skill performs authenticated network requests and likely handles session state, instability in an async HTTP client is security-relevant.

Unverifiable Dependency: aiohttp has 16 known advisory(ies) (CVE-2024-52303 (aiohttp has a memory leak when middleware is enabled when requesting a resource ); CVE-2026-54279 (aiohttp: Host-Only Cookies Become Domain Cookies After CookieJar Persistence); CVE-2026-34514 (AIOHTTP has CRLF injection through multipart part content type header constructi) +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
96% confidence
Finding
aiohttp has multiple advisories and the manifest does not establish a known-safe version, leaving the deployed security state unverifiable. In a skill that performs authenticated HTTP operations and may manage cookies or redirects, this uncertainty is operationally dangerous.

Unpinned Dependencies

Low
Category
Supply Chain
Content
Pillow>=10.0.0
fake-useragent>=1.4.0
aiohttp>=3.8.6
asyncio>=3.4.3
pycryptodome>=3.19.0
qrcode>=7.4.2
Confidence
50% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
fake-useragent>=1.4.0
aiohttp>=3.8.6
asyncio>=3.4.3
pycryptodome>=3.19.0
qrcode>=7.4.2
Confidence
97% confidence
Finding
pycryptodome is unpinned, so deployments may pull different cryptographic library versions with varying security properties and bug exposure. In a skill that includes authenticated operations and potentially token/cookie handling, uncontrolled crypto dependency changes are high-risk from a supply-chain perspective.

Static analysis

Detected: suspicious.exposed_secret_literal, suspicious.install_untrusted_source

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/pixiv.py:24

Install source points to URL shortener or raw IP.

Warn
Code
suspicious.install_untrusted_source
Location
config.example.yaml:13