T09 · Insecure Skill Coding Practices
- Location
scripts/sync-google-tasks.py:40- Finding
OAuth Refresh Token Is Stored Without Enforced Restrictive Permissions
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/sync-google-tasks.py:40OAuth Refresh Token Is Stored Without Enforced Restrictive Permissions
scripts/sync-google-tasks.py:71Private Google Tasks Data Is Persisted as Plaintext Without Enforced Access Controls
scripts/sync-stripe-arr.py:103Stripe Customer Metadata Is Persisted Without Restrictive Permissions or Data Minimization
SKILL.md:48Third-Party Python Dependencies Are Installed Without Version or Integrity Pinning
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.
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
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.
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.
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.
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.
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.
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.
## 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`.
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.
## Troubleshooting
**Google Tasks not syncing?**
- Verify `credentials.json` exists
- Check Tasks API is enabled
- Run script manually to see errors
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""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):
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""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):
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""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):
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
"""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):
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.
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.
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.
- Reflect on the week
- Celebrate wins
- Identify blockers
- Create tasks for the week ahead
## Quick Start
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.
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.
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.
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.
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.
### 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`
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.
Option A: System Cron (Traditional)
crontab -e
# Add these lines:
0 7 * * * cd /path/to/workspace && python3 skills/daily-rhythm/scripts/sync-stripe-arr.py
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.
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.
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.
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.
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`
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.
No suspicious patterns detected.