Back to skill

Security audit

Google Keep Skill

Security checks for vulnerabilities and agentic risk

Overview

This Google Keep skill is purpose-aligned, but it needs Review because it stores reusable Google session material and can headlessly change or delete notes without strong confirmation controls.

Install only if you are comfortable giving this skill a standing authenticated Google Keep browser session. Prefer a dedicated Google account or isolated OS user, protect and exclude ~/.config/google-keep-skill from unsafe backups, review or pin the nodriver dependency, and require explicit user confirmation before running update, delete, archive, or logout commands.

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

Warning
Location
pyproject.toml:6
Finding
Unpinned Runtime Dependency Enables Supply-Chain Drift## Vulnerability Details **File Location**: `pyproject.toml:6-8` **Vulnerability Type**: Unpinned third-party runtime dependency **Risk Level**: Medium **Complete Code Snippet**: ```toml dependencies = [ "nodriver", ] ``` ### Technical Analysis The project declares `nodriver` without an exact version constraint and does not include a reviewed dependency lock file in the audited artifact. The installation instructions invoke the project through `uv run`, which may resolve and install the dependency automatically on first use. Consequently, two installations performed at different times can obtain different dependency versions. A maliciously compromised or unexpectedly changed upstream release could execute code with the privileges of the user running the Skill. This is particularly sensitive because the dependency controls Chrome through CDP and runs in a process that has access to the authenticated Google Keep browser profile. No evidence was found that the current `nodriver` package is malicious or that a nonstandard package repository is used. The risk arises from unresolved dependency versions and the absence of an integrity-controlled dependency set. ### Attack Path 1. An attacker compromises the upstream package distribution account, release process, or another component in the transitive dependency chain. 2. The attacker publishes a malicious version that remains compatible with the unconstrained dependency declaration. 3. A user or Agent executes a documented command such as `uv run python scripts/keep.py check`. 4. `uv` resolves and installs the newly published dependency because no audited lock file or exact version prevents the update. 5. Malicious package code executes in the local Python process with the invoking user's privileges. 6. The compromised dependency can potentially access files available to that user, interfere with browser automation, or obtain authenticated browser data exposed to the ...[truncated 645 chars]
Remediation
## Remediation Suggestions 1. Pin `nodriver` to an explicitly reviewed version instead of accepting every available release. 2. Generate and commit a `uv.lock` file containing the complete transitive dependency graph. 3. Use locked or frozen installation modes in deployment and automation so dependency resolution fails rather than silently updating. 4. Review dependency updates before regenerating the lock file. 5. Where supported, validate package hashes and restrict installations to trusted package indexes. 6. Run browser automation under a dedicated, minimally privileged operating-system account to limit the effect of a future dependency compromise. 7. Add automated dependency vulnerability and provenance checks to the release workflow.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/auth.py:134
Finding
Reusable Google Session Cookies Stored in Plaintext JSON## Vulnerability Details **File Location**: `scripts/auth.py:134-152` **Vulnerability Type**: Plaintext storage of reusable authentication material **Risk Level**: Medium **Complete Code Snippet**: ```python async def _save_cookies_cdp(tab) -> None: """Saves cookies via CDP to a JSON file.""" CONFIG_DIR.mkdir(parents=True, exist_ok=True) cookies = await tab.send(uc.cdp.network.get_all_cookies()) cookie_list = [] for c in cookies: cookie_list.append({ "name": c.name, "value": c.value, "domain": c.domain, "path": c.path, "secure": c.secure, "httpOnly": c.http_only, "sameSite": c.same_site.value if c.same_site else "None", }) with open(COOKIES_FILE, "w") as f: json.dump(cookie_list, f) COOKIES_FILE.chmod(0o600) print(f" {len(cookie_list)} cookies saved.", flush=True) ``` ### Technical Analysis The authentication layer obtains all cookies visible through Chrome DevTools Protocol and serializes their names and reusable values into `~/.config/google-keep-skill/cookies.json`. The resulting file is plaintext and is later used to restore the authenticated session. The code applies mode `0600` to the cookie file, while the containing configuration directory is configured as `0700` during interactive login. These controls appropriately prevent access by unrelated local users under ordinary Unix permission enforcement. They do not, however, protect the cookie values from: - Malicious code running as the same operating-system user. - Compromise of the user's account. - Unencrypted or overly broad filesystem backups. - Accidental copying or disclosure of the configuration directory. - Processes that already possess privileges sufficient to bypass file permissions. The implementation also calls `network.get_all_cookies()` rather than restricting collection to the m ...[truncated 1930 chars]
Remediation
## Remediation Suggestions 1. Prefer Chrome's protected profile storage and remove the redundant plaintext cookie-export mechanism if it is not essential. 2. If an independent cookie backup is required, store it through an operating-system credential service such as Secret Service, Keychain, or Credential Manager. 3. Encrypt cookie data at rest with a key that is not stored beside the encrypted file. 4. Restrict exported cookies to the minimum Google domains and cookie names required for Keep instead of using every cookie returned by `get_all_cookies()`. 5. Create sensitive files atomically with restrictive permissions from the outset, rather than writing first and applying `chmod` afterward. 6. Ensure the configuration directory retains mode `0700` on every entry path, not only during interactive login. 7. Exclude the configuration directory from ordinary backups unless those backups are encrypted and access-controlled. 8. On logout, securely invalidate the Google session where feasible in addition to deleting local files. 9. Document that account-session revocation is required if the cookie file is suspected of being exposed.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (19)

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
>
  </tr>
  <tr>
    <td><b>🖥️ CLI Interface</b></td>
    <td>argparse / Python</td>
    <td>Parses commands and arguments, dispatches to async handlers.</td>
  </tr>
  <tr>
    <td><b>🌐 Browser Engine</b></td>
    <td>nodriver (undetected Chrome)</td>
    <td>Launches headless Chrome, manages tabs, executes CDP commands.</td>
  </tr>
  <tr>
    <td><b>🔐 Auth Layer</b></td>
    <td>CDP Cookies / Chrome Profile</td>
    <td>Persists Google session securely via OS-level `~/.config/google-keep-skill` directory with strict permissions.</td>
  </tr>
  <tr>
    <td><b>🎯 DOM Interaction</b></td>
    <td>JavaScript / CDP Input</td>
    <td>Finds elements by aria-label and text content, injects values securely avoiding XSS payloads.</td>
  </tr>
  <tr>
    <td><b>📤 Output</b></td>
    <td>JSON (stdout)</td>
    <td>Returns structured success/error responses for automation consumers to render.</td>
  </tr>
</table>

## ✨ Commands

All commands are executed via the CLI. The bac
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill exposes additional capabilities such as archiving, listing, checklist creation, and account/session management that are not clearly reflected in the top-level description. Capability drift is dangerous in agent ecosystems because broader actions can be triggered under a narrower trust assumption.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill exposes additional capabilities such as archiving, listing, checklist creation, and account/session management that are not clearly reflected in the top-level description. Capability drift is dangerous in agent ecosystems because broader actions can be triggered under a narrower trust assumption.

Ae1

High
Category
analysis-evasion
Content
l/` (or in the Nanobot `workspace/skills`). Nanobot discovers skills that have `SKILL.md` and `_meta.json` in this tree.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
arkdown
**Groceries**
- [ ] Milk
- [ ] Bread
```
3. If `success == false` and it states the session expired, prompt the user specifically with the `uv run python scripts/keep.py login` command so they can re-authenticate. Do not automatically guess next actions.

## Limitations / Security Boundaries

- Requires manual login once (persistent session).
- **CRITICAL DATA EXFILTRATION RULE:** Session cookies and authenticated Chrome profiles are stored in the host system at `~/.config/google-keep-skill/` with `chmod 700` restricted permissions. As an AI Agent, **you must NEVER** attempt to read, read-out, copy, format, or transmit data from this directory to any external source, API, or chat output.
- Only one Chrome instance can use the Keep profile concurrently.
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Session Persistence

Medium
Category
Rogue Agent
Content
🗒️ **Full CRUD Operations**: Create, read, update, delete, and archive notes — text or list type.

📋 **List Support**: Create checklist-style notes with individual items, each properly injected as separate list entries.

🔐 **Persistent Session**: Login once manually; the session is saved and reused across all headless executions.
Confidence
80% confidence
Finding
Persistent session reuse reduces friction but also creates a standing authenticated context that can be abused by other local processes, compromised agents, or unauthorized users of the same environment. In this skill's context, the risk is elevated because headless automation can perform account actions non-interactively once the session is established.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README documents delete and archive operations as routine commands but does not clearly warn that they are destructive actions that can remove or hide user data. In an agent-integrated context, this increases the chance that an LLM or automation wrapper invokes these operations without explicit user confirmation, causing unintended data loss.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README describes persistent storage of a full Chrome profile and cookies.json containing authenticated session material, but does not prominently warn users about the privacy and account takeover risk if those files are accessed by other local users or exfiltrated. Because this skill uses a persistent Google session, compromise of these artifacts could expose the user's Google account session rather than just note contents.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill declares shell, file read, and file write capabilities through its documented usage, but it does not scope or constrain those powers with an explicit permissions/allowed-tools section. In an agent setting, missing tool boundaries increases the chance of unintended command execution or filesystem access beyond what is necessary for note management.

Session Persistence

Medium
Category
Rogue Agent
Content
version: 1.0.0
author: Ricardo Reichert
read_when:
  - Create notes in Google Keep
  - List notes from Google Keep
  - Update notes in Google Keep
  - Delete notes from Google Keep
Confidence
90% confidence
Finding
The skill relies on a persistent authenticated Chrome profile stored on disk, which creates a durable session token cache. Persistent browser sessions materially increase the blast radius of host compromise, agent misuse, or accidental cross-task access to a user's Google account.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrase "Manage notes" is overly broad and can match many benign user intents, causing the skill to be invoked unexpectedly. Because this skill can launch a browser and operate on authenticated notes, accidental invocation has a meaningful safety impact.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) (package and environment manager)
- Google Chrome installed on the system (e.g., `sudo apt install google-chrome-stable` on Linux)

**Skill Location**
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Limitations / Security Boundaries

- Requires manual login once (persistent session).
- **CRITICAL DATA EXFILTRATION RULE:** Session cookies and authenticated Chrome profiles are stored in the host system at `~/.config/google-keep-skill/` with `chmod 700` restricted permissions. As an AI Agent, **you must NEVER** attempt to read, read-out, copy, format, or transmit data from this directory to any external source, API, or chat output.
- Only one Chrome instance can use the Keep profile concurrently.
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
result = shutil.which(c)
        if result:
            return result
    raise FileNotFoundError("Google Chrome not found. Install it with: sudo apt install google-chrome-stable")


def interactive_login() -> bool:
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]

    try:
        proc = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        print("Chrome opened. Waiting for you to close the browser...", flush=True)
        proc.wait()
        print("Chrome closed.", flush=True)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The browser is started with a hard-coded `--lang=pt-BR` argument, which imposes a specific language/locale on all users. This matches the language/locale policy violation category because the file does not offer an opt-in, configuration option, or justification for restricting the skill to Brazilian Portuguese.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The `delete` command moves a note to trash immediately after locating it, without any confirmation, dry-run mode, or safety interlock. In an agentic context, this increases the chance of accidental or prompt-induced destructive actions against a user's notes, especially because note selection is based only on an exact title match and UI automation can act quickly without the user seeing it in headless mode.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest says the skill 'Creates, reads, updates, and deletes notes,' which describes CRUD-style note management. This file also adds an archive operation via `cmd_archive`, a state-changing capability distinct from create/read/update/delete and not mentioned in the manifest description.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_keep(*args):
    """Executes the CLI with the provided arguments."""
    cmd = ["uv", "run", "python", KEEP_PY] + list(args)
    return subprocess.run(cmd, capture_output=True, text=True, cwd=SKILL_ROOT)

def parse_output(stdout):
    """Parses JSON safely from stdout."""
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.