Back to skill

Security audit

Daily Rhythm

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed daily-planning automation, but it asks users to connect Google Tasks and optional live Stripe billing data, persist that data locally, and run it on cron without adequate security guardrails.

Review before installing. Use a restricted Stripe key if enabling ARR, avoid production credentials unless necessary, keep credentials and generated memory files out of shared folders and repositories, lock down file permissions, and only enable cron jobs you understand how to disable. The skill does not show clear malicious behavior, but it handles sensitive personal and business data with weak scoping and storage guidance.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sync-google-tasks.py:40
Finding
OAuth Refresh Token Is Stored Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync-google-tasks.py`, lines 40–41 **Vulnerability Type**: Insecure local credential storage **Risk Level**: Medium ### Vulnerable Code ```python with open(token_path, 'w') as token: token.write(creds.to_json()) ``` ### Technical Analysis The script stores Google OAuth credentials in `~/.openclaw/google-tasks/token.json` using the process's default file-creation permissions. The serialized credentials may contain an access token and a reusable refresh token. Because the script does not explicitly create the file with mode `0600` or validate the containing directory's permissions, the effective access controls depend on the user's `umask` and existing filesystem configuration. On a shared system or one with permissive defaults, another local account or process may be able to read the token. The Google scope is appropriately limited to `https://www.googleapis.com/auth/tasks.readonly`, so the token does not grant task modification privileges. Nevertheless, it can expose private task titles, notes, links, and metadata. ### Attack Path 1. A user authenticates the Skill with Google Tasks. 2. The script serializes the OAuth credentials to `~/.openclaw/google-tasks/token.json`. 3. The file is created under a permissive `umask`, or its parent directory permits access by another local user. 4. A local attacker or compromised process reads `token.json`. 5. The attacker extracts the refresh token and exchanges it for a new Google access token. 6. The attacker uses the read-only Google Tasks API scope to retrieve the victim's task data. This attack requires local filesystem access or execution in another process that can read the affected path. ### Impact Assessment Successful exploitation can disclose the victim's Google Tasks data, including task titles, notes, due dates, links, and list metadata. The obtained privileges are limited by the configured read-only Tasks scope; no evidence indicates ...[truncated 66 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create `~/.openclaw/google-tasks` with mode `0700`. - Create or replace `token.json` atomically with mode `0600`. - Validate existing token and directory permissions before reading credentials. - Refuse to load a token file that is owned by another user or is group/world-readable. - Avoid relying solely on the process-wide `umask`. Example hardened approach: ```python os.makedirs(creds_dir, mode=0o700, exist_ok=True) os.chmod(creds_dir, 0o700) flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC fd = os.open(token_path, flags, 0o600) with os.fdopen(fd, 'w') as token: token.write(creds.to_json()) os.chmod(token_path, 0o600) ``` For stronger crash safety, write to a securely created temporary file in the same directory, apply mode `0600`, flush and synchronize it, and atomically replace the destination. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sync-google-tasks.py:71
Finding
Private Google Tasks Data Is Persisted as Plaintext Without Enforced Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync-google-tasks.py`, lines 71–93 **Vulnerability Type**: Insecure storage of sensitive synchronized data **Risk Level**: Medium ### Vulnerable Code ```python for task in tasks: task_data = { 'id': task['id'], 'title': task['title'], 'notes': task.get('notes', ''), 'due': task.get('due'), 'updated': task.get('updated'), 'position': task.get('position'), 'parent': task.get('parent'), 'links': task.get('links', []) } tasklist_data['tasks'].append(task_data) all_data['tasklists'].append(tasklist_data) # Save to memory file output_dir = '/Users/tom/.openclaw/workspace/memory' os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, 'google-tasks.json') with open(output_path, 'w') as f: json.dump(all_data, f, indent=2) ``` ### Technical Analysis The synchronization process copies complete active-task details—including titles, free-form notes, due dates, links, identifiers, and hierarchy metadata—into a plaintext JSON file. The output directory and file are created without explicit restrictive modes. The effective permissions therefore depend on the existing directory state and process `umask`. This can expose substantially more information than the morning brief requires, especially because free-form task notes and links may contain personal, operational, or confidential information. The hardcoded `/Users/tom/.openclaw/workspace/memory` path also makes isolation and ownership assumptions that may not hold on another installation. ### Attack Path 1. The scheduled or manually invoked synchronization retrieves active tasks from Google. 2. The script writes task titles, notes, links, and metadata to `google-tasks.json`. 3. The workspace or generated file has permissive local permissions, is included in an unprotected backup, or is accessible to another workspace tool. 4. An attacker with access t ...[truncated 625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the memory directory with mode `0700` and verify its owner. - Write `google-tasks.json` atomically with mode `0600`. - Replace the hardcoded user path with a validated configuration value or a path derived from the current user's home directory. - Minimize the synchronized data. If notes, links, internal IDs, positions, or parent metadata are not required by the brief, do not persist them. - Document the sensitivity and retention period of the local task cache. - Exclude the memory directory from source control, shared synchronization, and unencrypted backups where appropriate. - Consider encrypting the cache at rest when the threat model includes other local users or shared storage. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sync-stripe-arr.py:103
Finding
Stripe Customer Metadata Is Persisted Without Restrictive Permissions or Data Minimization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/sync-stripe-arr.py`, lines 103–120 **Vulnerability Type**: Insecure storage and unnecessary retention of customer identifiers **Risk Level**: Medium ### Vulnerable Code ```python # Save detailed data output_dir = '/Users/tom/.openclaw/workspace/memory' os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, 'stripe-data.json') data = { 'synced_at': datetime.now().isoformat(), 'arr': arr, 'customer_count': customer_count, 'customer_ids': customer_ids, 'subscription_count': len(subscriptions), 'method': 'active_subscriptions_only' } with open(output_path, 'w') as f: json.dump(data, f, indent=2) ``` ### Technical Analysis The script stores Stripe customer identifiers in plaintext alongside financial metrics. The advertised morning-brief functionality requires aggregate ARR and customer counts, but it does not require retaining the individual customer IDs after deduplication and aggregation. The output file is created with default permissions rather than an explicitly restrictive mode. Consequently, local exposure depends on the user's `umask` and workspace permissions. Although Stripe customer IDs are not authentication secrets, they are customer-linked business metadata and can facilitate account correlation, targeted social engineering, or further enumeration if combined with other access. The network interaction itself is consistent with the documented feature: the Stripe API key is assigned to the official Stripe SDK and used to query active subscriptions. No transmission to an unknown host was identified. ### Attack Path 1. The user configures a Stripe API key and invokes the synchronization manually or through cron. 2. The script queries active Stripe subscriptions. 3. It computes aggregate ARR but also retains every deduplicated customer identifier. 4. The identifiers are written to `memory/stripe-data.json` without enforced restrictiv ...[truncated 632 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `customer_ids` from the persisted output unless a documented feature explicitly requires it. - Retain only aggregate values needed by the morning brief, such as ARR and customer count. - Create the memory directory with mode `0700` and output files with mode `0600`. - Use atomic file replacement to avoid partial or unexpectedly permissioned files. - Replace the hardcoded `/Users/tom/...` output path with a validated, user-relative configuration. - Define a retention policy and exclude the generated file from public repositories and unprotected backups. - Prefer a restricted Stripe key with only the permissions required to read subscription information, rather than an unrestricted live secret key. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:48
Finding
Third-Party Python Dependencies Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, line 48 **Vulnerability Type**: Unpinned and integrity-unverified dependencies **Risk Level**: Low ### Vulnerable Code ```bash pip install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client stripe ``` ### Technical Analysis The installation instructions fetch mutable latest versions of several packages without exact version constraints, hashes, or a lock file. Installation behavior can therefore change after the Skill has been reviewed. This is a supply-chain hardening weakness rather than evidence that the named packages are malicious. Exploitation would require compromise of an upstream package or package index, use of a maliciously configured alternate index, or a future compromised dependency release. Python packages may execute installation-time code and will execute imported code when the scripts run. ### Attack Path 1. A user follows the documented installation command. 2. `pip` resolves package versions from the active package index configuration at installation time. 3. An attacker compromises an upstream release, controls a configured package index or mirror, or otherwise causes a malicious version to be selected. 4. The malicious package executes during installation or when imported by the synchronization scripts. 5. The package gains the privileges of the user running `pip` or the scheduled scripts and may access locally available OAuth tokens, Stripe credentials, task data, and workspace files. ### Impact Assessment If the dependency supply chain is compromised, attacker code can execute with the installing user's privileges. It may read or modify any files available to that user, including the Google OAuth token, Stripe API key, synchronized data, and cron-accessible workspace content. No current malicious dependency or dependency-confusion package was identified in the audited files. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Provide a reviewed `requirements.txt` or lock file with exact dependency versions. - Include package hashes and install with integrity enforcement: ```bash python3 -m pip install --require-hashes -r requirements.txt ``` - Install dependencies in an isolated virtual environment rather than the user's global Python environment. - Use the official Python Package Index over TLS and audit any configured extra indexes or mirrors. - Regularly scan and deliberately update pinned dependencies instead of automatically accepting the latest versions. - Consider generating the lock file with a tool that records transitive dependencies and hashes. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (35)

Credential Access

High
Category
Privilege Escalation
Content
1. **Install** the skill
2. **Configure Google Tasks** (required)
   - Get API credentials from Google Cloud
   - Place `credentials.json` in `~/.openclaw/google-tasks/`
3. **Optional**: Add Stripe API key for ARR tracking
4. **Optional**: Add calendar ICS URL
5. **Set up cron jobs** or use OpenClaw's cron system
Confidence
92% confidence
Finding
The explicit reference to credentials.json in a fixed user directory indicates the skill depends on access to sensitive Google API credentials. In the context of an agent skill that may automate scripts and cron jobs, encouraging local placement of raw credential files expands the attack surface if the host, logs, backups, or repository hygiene are weak.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill’s stated purpose omits that it accesses Stripe billing/subscription data, reads API credentials from local files, calculates ARR, and persists financial state locally. This is a significant expansion of scope from personal routine support into business-financial processing, increasing privacy and confidentiality risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill’s stated purpose omits that it accesses Stripe billing/subscription data, reads API credentials from local files, calculates ARR, and persists financial state locally. This is a significant expansion of scope from personal routine support into business-financial processing, increasing privacy and confidentiality risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill’s stated purpose omits that it accesses Stripe billing/subscription data, reads API credentials from local files, calculates ARR, and persists financial state locally. This is a significant expansion of scope from personal routine support into business-financial processing, increasing privacy and confidentiality risk.

Credential Access

High
Category
Privilege Escalation
Content
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create project → Enable **Tasks API**
3. Create OAuth 2.0 credentials (Desktop app)
4. Download `credentials.json` to `~/.openclaw/google-tasks/`
5. Run once to authenticate: `python3 scripts/sync-google-tasks.py`

See [CONFIGURATION.md](references/CONFIGURATION.md) for detailed steps.
Confidence
88% confidence
Finding
The skill instructs users to place OAuth credential material locally and run a script that authenticates against Google Tasks. While credential use is expected for this integration, the skill provides no guardrails around secure storage, file permissions, or limiting token exposure, which can lead to credential compromise if the workspace or host is shared.

Credential Access

High
Category
Privilege Escalation
Content
## Scripts Reference

### sync-google-tasks.py
Syncs Google Tasks to local JSON. Requires `credentials.json`.

### sync-stripe-arr.py
Calculates ARR from active Stripe subscriptions. Requires `.env.stripe`.
Confidence
87% confidence
Finding
The scripts reference section confirms reliance on credentials.json and .env.stripe for privileged external access, but does not specify safe handling practices. This creates a predictable pathway for sensitive files to be mishandled, copied into insecure locations, or left readable to other local users/processes.

Credential Access

High
Category
Privilege Escalation
Content
## Troubleshooting

**Google Tasks not syncing?**
- Verify `credentials.json` exists
- Check Tasks API is enabled
- Run script manually to see errors
Confidence
85% confidence
Finding
Troubleshooting tells users to verify the presence of credentials.json, again normalizing local secret-file handling without warning about exposure risk. Repeated credential references without security instructions increase the chance of unsafe support practices such as sharing screenshots, logs, or directory listings containing secret locations.

Credential Access

High
Category
Privilege Escalation
Content
"""Get or refresh Google credentials."""
    creds_dir = os.path.expanduser('~/.openclaw/google-tasks')
    token_path = os.path.join(creds_dir, 'token.json')
    creds_path = os.path.join(creds_dir, 'credentials.json')
    
    creds = None
    if os.path.exists(token_path):
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
"""Get or refresh Google credentials."""
    creds_dir = os.path.expanduser('~/.openclaw/google-tasks')
    token_path = os.path.join(creds_dir, 'token.json')
    creds_path = os.path.join(creds_dir, 'credentials.json')
    
    creds = None
    if os.path.exists(token_path):
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
"""Get or refresh Google credentials."""
    creds_dir = os.path.expanduser('~/.openclaw/google-tasks')
    token_path = os.path.join(creds_dir, 'token.json')
    creds_path = os.path.join(creds_dir, 'credentials.json')
    
    creds = None
    if os.path.exists(token_path):
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
"""Get or refresh Google credentials."""
    creds_dir = os.path.expanduser('~/.openclaw/google-tasks')
    token_path = os.path.join(creds_dir, 'token.json')
    creds_path = os.path.join(creds_dir, 'credentials.json')
    
    creds = None
    if os.path.exists(token_path):
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This file performs Stripe subscription retrieval, revenue calculation, and local export of billing-derived data, which is unrelated to the declared daily-planning skill. That mismatch is dangerous because it creates undisclosed access to financial systems and quietly expands the skill’s privileges and data handling beyond user expectations.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The code loads a Stripe API key, queries active subscriptions, computes ARR, and updates stored state with revenue metrics. In the context of a daily-routine skill, this is an unjustified finance-platform integration that could be used for covert business data access or exfiltration under the guise of benign automation.

Session Persistence

Medium
Category
Rogue Agent
Content
- Reflect on the week
- Celebrate wins
- Identify blockers
- Create tasks for the week ahead

## Quick Start
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README instructs users to place Google API credentials in a predictable local path but provides no guidance on securing the file, restricting permissions, or avoiding accidental disclosure. While documentation alone is not credential theft, normalizing unsafe credential handling increases the chance of local compromise, backup leakage, or accidental commits.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill describes behaviors that require network, file read/write, environment/credential handling, and scheduled execution, but it does not declare any explicit tool scope or permissions boundary. That creates an authorization and review gap: operators and users cannot clearly see or constrain what the skill is allowed to access before installation.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The manifest says to use the skill for requests including 'productivity systems' and 'daily planning automation,' alongside a broad 'triggers include' list. These phrases are generic enough to match many ordinary planning conversations, and the file does not provide exclusion conditions or negative examples to narrow when the skill should or should not activate.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The setup requires external services and sensitive credentials, including Google OAuth credentials and a Stripe API key, but the skill text does not provide an upfront security warning about handling secrets or the implications of granting third-party access. Users may expose credentials or connect sensitive accounts without understanding the risk.

Session Persistence

Medium
Category
Rogue Agent
Content
### Step 2: Configure Google Tasks

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create project → Enable **Tasks API**
3. Create OAuth 2.0 credentials (Desktop app)
4. Download `credentials.json` to `~/.openclaw/google-tasks/`
5. Run once to authenticate: `python3 scripts/sync-google-tasks.py`
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
Option A: System Cron (Traditional)
```bash
crontab -e

# Add these lines:
0 7 * * * cd /path/to/workspace && python3 skills/daily-rhythm/scripts/sync-stripe-arr.py
Confidence
85% confidence
Finding
The skill directs users to create cron jobs that execute automatically and persistently, including a Stripe sync against potentially sensitive business data. Scheduled autonomous execution increases blast radius because once installed, external access and local writes continue without per-run user review.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill stores wind-down responses, synced tasks, Stripe data, and state files under local memory paths, but the description does not clearly warn users that routine and productivity data will be persisted locally. Hidden persistence increases privacy risk, especially for reflective or sensitive personal notes.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The template instructs sending calendar data, tasks, priorities, open loops, weather/location-derived context, and possibly financial ARR information through third-party messaging channels like Telegram, WhatsApp, or Signal, but gives no privacy warning or sensitivity check. This is dangerous because it encourages transmission of aggregated personal and business data over external services where metadata exposure, misdelivery, shared-device access, or account compromise could leak sensitive information.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The template explicitly directs the system to persist a user's wind-down response to a dated file in memory without telling the user that their reflective, potentially sensitive personal data will be stored. In a routine-tracking skill, these responses can reveal goals, habits, stressors, and schedules, so silent retention creates a real privacy risk and expands the blast radius if local storage or synced memory is later accessed.

Session Persistence

Medium
Category
Rogue Agent
Content
Required for task syncing:

1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Create a new project or select existing
3. Enable the **Tasks API**
4. Create OAuth 2.0 credentials (Desktop application)
5. Download `credentials.json`
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The guide instructs users to place a live Stripe secret key in a workspace file (`.env.stripe`) but does not warn that this credential grants API access and must be kept out of version control, logs, and shared folders. In a skill/workspace context, users may accidentally commit or expose the key, enabling unauthorized access to billing data or Stripe operations permitted by the key.

Static analysis

No suspicious patterns detected.