Back to skill

Security audit

Readwise Reader

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Readwise Reader integration that can read and change your library, with no evidence of hidden exfiltration, persistence, or unrelated behavior.

Install only if you are comfortable giving the skill a Readwise access token that can read and modify your Reader library. Review JSON payloads before running update, bulk update, or delete commands, prefer dry-run where available, avoid --confirm unless you have already checked the IDs, and consider pinning dependencies in a virtual environment before use.

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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bulk_update_documents.py:154
Finding

Bulk Update Requests Falsely Report Success Without Validating the API Response

Content
View full analysis
dict: """Bulk update documents via the API.""" client.patch("/bulk_update/", json=payload) # Format response results = [] for update in payload["updates"]: results.append({"id": update["id"], "updated": True}) return { "count": len(results), "results": results, } ``` ### Technical Analysis The function sends the bulk update request but discards the returned `httpx.Response`. Unlike the other mutation scripts, it does not pass the response to `handle_response()`. Consequently, HTTP failures such as `400`, `401`, `429`, and `5xx` responses are not detected. The function instead constructs a synthetic result that marks every requested document as successfully updated, regardless of the actual server response. This is an integrity and error-handling flaw. It does not directly grant additional system privileges, but it violates the script's documented behavior and can cause agents or automated workflows to operate on an incorrect view of remote state. ### Attack Path 1. A caller submits a validly structured bulk update payload. 2. The Readwise API rejects the request because of an invalid field, expired token, rate limit, authorization failure, or server-side error. 3. `client.patch()` returns the error response without raising an exception for HTTP status codes. 4. The function ignores that response. 5. The function emits `{"updated": true}` for every requested item. 6. Downstream automation trusts the false result and may archive, delete, or otherwise process documents under the incorrect assumption that the update succeeded. ### Impact Assessment The issue can compromise workflow and data integrity within the user's R ...[truncated 370 chars]
Remediation
View remediation
dict: """Bulk update documents via the API.""" response = client.patch("/bulk_update/", json=payload) data = handle_response(response, client) return data ``` Additional hardening should include: 1. Import `handle_response` from `utils`. 2. Derive per-document status from the validated API response rather than synthesizing success values. 3. Preserve authentication, rate-limit, validation, and server-error exit codes. 4. Add tests for `400`, `401`, `429`, and `5xx` responses. 5. Verify that partial-success responses are represented accurately instead of treating the entire batch as successful. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/requirements.txt:1
Finding

Unbounded Dependency Versions Reduce Build Integrity and Reproducibility

Content
View full analysis
=0.27.0 python-dotenv>=1.0.0 ``` ### Technical Analysis Both dependencies use open-ended minimum-version constraints. A future installation may therefore resolve to any later release, including a version that has not been reviewed with this Skill. The package names and configured package source are not suspicious based on the audited files. No dependency confusion or typosquatting was confirmed. The risk arises from non-reproducible resolution and the absence of integrity hashes or a lock file. Because these packages are imported by `scripts/utils.py`, code contained in an installed dependency executes in the context of the Skill. That context can access process environment variables, including `READWISE_ACCESS_TOKEN`, and has the same local permissions as the invoking user. ### Attack Path 1. A user follows the documented command to install `scripts/requirements.txt`. 2. The package resolver selects newer releases satisfying the open-ended constraints. 3. If a selected release is compromised, malicious, or unexpectedly incompatible, its installation or imported runtime code executes locally. 4. Imported dependency code runs with the invoking user's permissions. 5. In a supply-chain compromise scenario, that code could access the Readwise token, local files available to the process, or outbound network connectivity. This is a contingent supply-chain path; the audit found no evidence that the currently named packages are malicious. ### Impact Assessment The potential scope is the invoking user's process privileges and data available to that process, including the `READWISE_ACCESS_TOKEN`. In a successful upstream compromise, the dependency could potentially act against the associated Readwise account or access readable local files. The present risk is Low b ...[truncated 185 chars]
Remediation
View remediation
python-dotenv== ``` 2. Generate and commit a lock file containing transitive dependency versions. 3. Use hash verification, such as a requirements file generated with `pip-compile --generate-hashes`. 4. Install with hash enforcement where practical: ```bash pip install --require-hashes -r requirements.txt ``` 5. Review dependency updates through a controlled process that includes vulnerability scanning and compatibility testing. 6. Use a dedicated virtual environment and a trusted, explicitly configured Python package index. ]]>
Vulnerability Patterns
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The code chunk is specifically a document-listing script for Readwise Reader. It builds query parameters, calls a list endpoint, supports pagination and bulk fetch, and can include HTML content and write results to a file. This matches part of the description related to listing documents and retrieving document content. However, the declared purpose says the skill can also create, update, and delete documents and manage saved articles/reading lists broadly, which this code chunk does not implement. That is a material description-versus-behavior mismatch for this supplied code chunk, even though the implemented behavior is within the same Readwise Reader domain.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/utils.py (reported line 73)May include surrounding context.

python
def get_access_token() -> str:
    """
    Get the Readwise access token from environment.

    Returns:
        The access token string.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/utils.py (reported line 76)May include surrounding context.

python
def get_access_token() -> str:
    """
    Get the Readwise access token from environment.

    Returns:
        The access token string.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/utils.py (reported line 172)May include surrounding context.

python
def get_access_token() -> str:
    """
    Get the Readwise access token from environment.

    Returns:
        The access token string.

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 · scripts/utils.py (reported line 87)May include surrounding context.

python
raise APIError(
            type="authentication_error",
            message="READWISE_ACCESS_TOKEN environment variable not set",
            hint="Set the environment variable or create a .env file with READWISE_ACCESS_TOKEN=your-token",
            exit_code=EXIT_AUTH_ERROR,
        )
    return token

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill exposes access to environment secrets, file operations, and networked Python scripts but does not declare any explicit tool scope or permission boundaries. In an agent setting, this increases the chance the skill can be invoked with broader capabilities than users expect, including use of the Readwise token and local file access without clear restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The documentation advertises destructive operations such as delete, archive, and bulk update without warning users that these actions can permanently modify their Reader library. In an agent-driven workflow, omission of safety prompts or confirmation requirements makes accidental destructive actions more likely, especially for bulk operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown file documents delete_document.py as removing documents from the user's Reader library, but it provides no warning about the destructive nature of the action, confirmation expectations, or recovery limitations. Under the markdown-specific SQP-2 criteria, descriptions of behaviors that can affect user data should include clear warnings.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/delete_document.py (reported line 14)May include surrounding context.

python
OPTIONS:
    --file <path>    Read JSON payload from file (default: stdin)
    --confirm        Skip confirmation prompt
    --help           Show this help message

INPUT PAYLOAD (stdin or --file):

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/delete_document.py (reported line 118)May include surrounding context.

python
OPTIONS:
    --file <path>    Read JSON payload from file (default: stdin)
    --confirm        Skip confirmation prompt
    --help           Show this help message

INPUT PAYLOAD (stdin or --file):

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The guide explains bulk_update_documents.py for updating up to 50 documents in one request, including changing locations, tags, and notes, but does not warn users that it can overwrite metadata across many items at once. This is a user-data-affecting behavior in markdown that merits disclosure so users understand the scope and risk of mistakes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This code issues an HTTP request to the Readwise service using user-supplied filters, and the --with-content option can retrieve full HTML content from the user's library. Although the script's purpose is to list Reader documents, the file does not include an explicit disclosure near the network call that it sends request parameters and accesses remote account data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code writes the retrieved Readwise tag data to a user-specified path when --output is provided. Although file output is implied by the CLI option and examples, there is no runtime confirmation or explicit warning at the write site, so the safety-critical file write lacks direct user disclosure in the code itself.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specification uses a lower-bound only constraint (httpx>=0.27.0), which permits installation of any future release and makes builds non-reproducible. This increases supply-chain risk because a vulnerable or breaking version could be resolved later without review, and the accompanying advisory cannot be ruled out from the manifest alone.

Content

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

text
httpx>=0.27.0
python-dotenv>=1.0.0

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

The manifest references httpx without an exact version, while known advisories exist for some releases. Because the requirement is unpinned, it is impossible to verify from this file whether deployment will resolve to a patched or affected version, leaving potential exposure to known dependency flaws.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency specification uses a lower-bound only constraint (python-dotenv>=1.0.0), so the installed version is not deterministic and could drift to an unreviewed release. That creates a software supply-chain weakness and prevents confirming from the manifest whether a vulnerable version might be installed.

Content

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

text
httpx>=0.27.0
python-dotenv>=1.0.0

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
84% confidence
Finding

The manifest references python-dotenv without pinning, and advisories exist for certain versions of that package. This means the environment could install an affected release, and the risk cannot be disproven from the current requirement entry alone.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.