Back to skill

Security audit

Todolist Md Clawdbot Copy

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its todo-file purpose, but it asks for broad Google Drive authority and has under-scoped write-back behavior that users should review before installing.

Install only if you are comfortable granting broad Google Drive access to this workflow. Prefer running it with a dedicated Google account or reduced OAuth scope, review every suggestions JSON before apply mode, and avoid folders containing Markdown files that should not be AI-reviewed until opt-in enforcement and target-file validation are added.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/todolist_agent_entrypoint.mjs:150
Finding

Managed OAuth Requests Unrestricted Google Drive Access

Content
View full analysis
` }; } const tokens = await exchangeAuthCodeForTokens({ clientId, clientSecret, code: authCode }); if (!tokens.refresh_token) { throw new Error('No refresh_token returned. Try again with prompt=consent and ensure you approved access.'); } const toSave = { created_at: new Date().toISOString(), scopes, refresh_token: tokens.refresh_token, }; ``` ```javascript // scripts/todolist_drive_folder_agent.mjs async function ensureManagedOAuth({ refreshTokenFile, clientId, clientSecret, authCode }) { if (fs.existsSync(refreshTokenFile)) { const existing = JSON.parse(fs.readFileSync(refreshTokenFile, 'utf8')); if (existing?.refresh_token) return existing; } const scopes = ['https://www.googleapis.com/auth/drive']; if (!authCode) { const url = buildAuthUrl({ clientId, scopes }); return { needsAuth: true, authUrl: url, refreshTokenFile, scope: scopes, howTo: `Open the URL, approve, then rerun with: --authCode ` }; } const tokens = await exchangeAuthCodeForTokens({ clientId ...[truncated 2281 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/todolist_drive_folder_agent.mjs:347
Finding

Per-File Review Opt-In Policy Is Documented but Not Enforced

Content
View full analysis
(f?.mimeType === 'text/markdown') || (f?.name || '').endsWith('.md')); if (onlyName) mdFiles = mdFiles.filter(f => f.name === onlyName); ``` Every changed selected file is subsequently downloaded and included in the request-generation workflow: ```javascript // Build compact LLM request payload for OpenClaw to process. const items = []; for (const f of changed) { const fileId = f.id; const text = await driveDownloadText({ fileId, accessToken }); const openTasks = extractOpenTasks(text, 120); items.push({ fileId, name: f.name, sectionTitle, openTasks, // helpful for applying gate later hint: { modifiedTime: f.modifiedTime, size: f.size, } }); } ``` ### Technical Analysis The documentation presents two mechanisms for controlling review eligibility: - Include and exclude rules in `.todolist-md.config.json`. - An ` true` marker in an individual Markdown file. The folder agent does not download or parse the configuration file and does not verify the in-file marker. Its actual selection criterion is only the MIME type or `.md` suffix, with an optional exact filename filter supplied at runtime. Consequently, the documented opt-in boundary is not an effective security control. Every changed Markdown file in the selected fol ...[truncated 1389 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/todolist_drive_folder_agent.mjs:421
Finding

Apply Mode Can Modify Arbitrary Drive File IDs from Untrusted Suggestions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (22)

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: todolist-md-clawdbot
description: Operate on todolist-md Markdown todo files. Read + summarize tasks, propose edits, and write outcomes back into Markdown using only <!-- bot: ... --> markers (line-stable write-back).
---

# todolist-md-clawdbot

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 33)May include surrounding context.

md
- Rule: **never insert a new line** once the header exists. Only update the existing header line.
- If the header does not exist, you may insert it at the very top **only if the user explicitly opted into Option B**.
- Canonical format:
  - `<!-- bot: last_review --> 2026-02-04T15:39Z root=<rootFolderId> model=<model>`

6) Never complete tasks without explicit user confirmation.

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 10)May include surrounding context.

js
* Design goals:
 * - Node-only I/O pipeline (unified)
 * - Minimum tokens: do not call LLM unless a file changed; extract only open tasks
 * - Safe write-back: write ONLY into a dedicated bot section using <!-- bot: ... --> markers
 * - Avoid duplicates: overwrite-update via Drive API files.update(fileId)
 * - Avoid overwriting while you edit: revision gate via headRevisionId
 *

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The code accesses a fixed secret file and reads a keyring password into process memory for later use, which is a genuine credential-access behavior. In a skill context this is more dangerous because the skill can leverage host-resident secrets without explicit user action, potentially enabling unauthorized access to Google Drive accounts or other integrated services.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 183)May include surrounding context.

js
const envFile = '/root/clawd/.secrets/gog.env';

  let account = process.env.GOG_ACCOUNT || '';
  let pw = process.env.GOG_KEYRING_PASSWORD || '';

  if ((!account || !pw) && fs.existsSync(envFile)) {
    const text = fs.readFileSync(envFile, 'utf8');

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

This line specifically extracts and assigns the keyring password from the secrets file, confirming the script is parsing and consuming sensitive credentials. Such behavior is high-risk in an agent environment because it allows the skill to operationalize stored secrets automatically and invisibly.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 194)May include surrounding context.

js
let v = m[2];
      v = v.replace(/^"|"$/g, '');
      if (k === 'GOG_ACCOUNT' && !account) account = v;
      if (k === 'GOG_KEYRING_PASSWORD' && !pw) pw = v;
      if (k === 'GOG_BIN' && !process.env.GOG_BIN) process.env.GOG_BIN = v;
    }
  }

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The script passes account identifiers and a keyring password directly into a subprocess environment, increasing exposure of credentials to child processes, debugging tools, process inspection, and logs. Combined with sudo and external Drive operations, this creates a strong capability for the skill to act using privileged stored credentials.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 204)May include surrounding context.

js
const cmd = [
    'sudo','-u','ubuntu','-H','env',
    `GOG_ACCOUNT=${account}`,
    `GOG_KEYRING_PASSWORD=${pw}`,
    gogBin,
    'drive','ls','--parent', folderId,'--json'
  ];

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 292)May include surrounding context.

js
`- [ ] Review open tasks in ${fileName} (generated ${now})`,
  ];
  if (openTasks.trim()) {
    lines.push(`  > <!-- bot: note --> Open tasks sampled: ${openTasks.split(/\r?\n/).length}`);
  } else {
    lines.push(`  > <!-- bot: note --> No open tasks found`);
  }

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/todolist_review_drive.py (reported line 49)May include surrounding context.

python
def gog_env() -> Dict[str, str]:
    # Use same pattern as TOOLS.md: run as ubuntu and pass GOG_ACCOUNT/GOG_KEYRING_PASSWORD.
    # Here we just forward current environment; caller should export vars.
    env = os.environ.copy()
    if not env.get("GOG_BIN"):

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/todolist_review_drive.py (reported line 51)May include surrounding context.

python
def gog_env() -> Dict[str, str]:
    # Use same pattern as TOOLS.md: run as ubuntu and pass GOG_ACCOUNT/GOG_KEYRING_PASSWORD.
    # Here we just forward current environment; caller should export vars.
    env = os.environ.copy()
    if not env.get("GOG_BIN"):
        env["GOG_BIN"] = "/home/linuxbrew/.linuxbrew/bin/gog"
    return env

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

This command explicitly injects GOG_ACCOUNT and GOG_KEYRING_PASSWORD into the environment of a spawned process. Even though that may be needed for authentication, exposing secrets this way can leak them to process metadata, debugging tools, audit logs, or other local observers depending on platform and configuration.

Content

Scanner excerpt · scripts/todolist_review_drive.py (reported line 67)May include surrounding context.

python
"-H",
        "env",
        f"GOG_ACCOUNT={env.get('GOG_ACCOUNT','')}",
        f"GOG_KEYRING_PASSWORD={env.get('GOG_KEYRING_PASSWORD','')}",
        gog,
        "drive",
        "ls",

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

This repeats the same pattern for the download command, passing account and keyring password material into a child process. In a local automation context this increases credential exposure surface without adding compensating protections.

Content

Scanner excerpt · scripts/todolist_review_drive.py (reported line 91)May include surrounding context.

python
"-H",
        "env",
        f"GOG_ACCOUNT={env.get('GOG_ACCOUNT','')}",
        f"GOG_KEYRING_PASSWORD={env.get('GOG_KEYRING_PASSWORD','')}",
        gog,
        "drive",
        "download",

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script requests the full-scope permission https://www.googleapis.com/auth/drive, which grants broad read/write access to all files the user can access, even though the documented function is limited to one todo markdown file. If the token is exposed or the script is modified or misused, the excessive scope enables compromise far beyond the intended single file.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_agent_entrypoint.mjs (reported line 57)May include surrounding context.

js
}

async function oauthAccessTokenFromRefresh({ refreshToken, clientId, clientSecret }) {
  const res = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_agent_entrypoint.mjs (reported line 85)May include surrounding context.

js
}

async function oauthAccessTokenFromRefresh({ refreshToken, clientId, clientSecret }) {
  const res = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 54)May include surrounding context.

js
}

async function oauthAccessTokenFromRefresh({ refreshToken, clientId, clientSecret }) {
  const res = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/todolist_drive_folder_agent.mjs (reported line 82)May include surrounding context.

js
}

async function oauthAccessTokenFromRefresh({ refreshToken, clientId, clientSecret }) {
  const res = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script silently reads credentials from a hard-coded secrets path under /root, creating implicit privilege and secret dependencies that may surprise operators and expand the attack surface. In an agent/skill setting, hidden credential consumption is risky because users may not realize the code can access powerful pre-provisioned secrets and use them against external services.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script launches a subprocess through sudo to run as another user without any user-facing warning, which is a meaningful privilege-boundary crossing in an automation context. Even though the command arguments are fixed and use execFileSync rather than a shell, invoking sudo can expose sensitive environment values and enable broader system interaction than expected.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/todolist_review_drive.py (reported line 44)May include surrounding context.

python
def sh(cmd: List[str], env: Dict[str, str] | None = None) -> str:
    out = subprocess.check_output(cmd, env=env, text=True)
    return out

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script forwards credential-related environment variables to child processes without minimizing exposure or warning the user. While it does not exfiltrate secrets directly, passing secrets through command invocation increases the chance of accidental disclosure through process inspection, logging, crash reports, or misconfigured wrappers.

Content

No source excerpt is available for this finding.

Tainted flow: 'new' from pathlib.Path.read_text (line 187, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/todolist_review_drive.py (reported line 195)May include surrounding context.

python
else:
                # NOTE: actual upload/in-place update should be implemented by the agent using Drive API files.update.
                # We intentionally do not embed secrets/token logic here.
                tmp.write_text(new, encoding="utf-8")
                print(f"[needs-agent-upload] updated local copy for {f.get('name')} ({fid})")

    state["lastScanAtUtc"] = iso_utc_now()

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The header states the script does not call any LLM, which is literally true, but it immediately describes generating a compact LLM request JSON and later the code constructs that payload in prepare mode. This creates intent-level ambiguity in the documentation by presenting the script as non-LLM while implementing direct LLM handoff orchestration.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal (+1 more)

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/todolist_drive_folder_agent.mjs:208

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/todolist_agent_entrypoint.mjs:192

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/todolist_drive_folder_agent.mjs:179

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/todolist_agent_entrypoint.mjs:198

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/todolist_drive_folder_agent.mjs:315

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/todolist_agent_entrypoint.mjs:157

Sensitive-looking file read is paired with a network send.

Warn
Code
suspicious.potential_exfiltration
Location
scripts/todolist_drive_folder_agent.mjs:100