Back to skill

Security audit

TaskFlow

Security checks for vulnerabilities and agentic risk

Overview

TaskFlow fits its task-management purpose, but it needs review because the optional Apple Notes and background sync features use weakly validated local configuration and can create recurring user-level execution.

Use this only with a trusted local workspace. Do not run taskflow notes, install-daemon, or --yes setup against shared or downloaded project folders until the AppleScript/config handling and daemon template escaping are fixed. Review any exported dashboard JSON or Apple Notes output for sensitive project details before syncing or sharing it.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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

Error
Location
scripts/apple-notes-export.mjs:193
Finding

AppleScript Injection Through Unvalidated Apple Notes Configuration

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/apple-notes-export.mjs:185
Finding

Predictable Shared Temporary File Enables Symlink Clobbering and Data Disclosure

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/taskflow-cli.mjs:244
Finding

Daemon Installer Interpolates Environment-Derived Paths Into Shell Commands and XML

Content
View full analysis
\/usr\/local\/bin\/node<\/string>/, `${nodeBin}`) writeFileSync(plistDest, plistContent, 'utf8') const { execSync } = await import('node:child_process') try { execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' }) console.log(` ${c.green}loaded${c.reset} com.taskflow.sync LaunchAgent (sync every 60s)`) } catch { console.log(` ${c.yellow}!${c.reset} Could not load LaunchAgent automatically. Run manually:`) console.log(` launchctl load "${plistDest}"`) } ``` The explicit `install-daemon` path repeats the unsafe behavior: ```js const plistContent = readFileSync(tmplPath, 'utf8') .replace(/\{\{workspace\}\}/g, workspace) .replace(/\{\{node\}\}/g, nodeBin) writeFileSync(plistDest, plistContent, 'utf8') try { execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' }) ``` ### Technical Analysis The daemon installer places `plistDest` into a shell command using string interpolation. Double quotes are not sufficient when the interpolated value can itself contain quotes, command substitutions, backticks, or shell metacharacters. `plistDest` is derived from `HOME`. Separately, `workspace` and `process.execPath` are inserted directly into an XML property list without XML encoding. Characters such as `&`, `<`, and `>` can invalidate the plist. A deliberately crafted workspace path containing XML closing tags could inject additional plist elements and alter the persisted job's arguments or behavior. The Skill documentation labels `OPENCLAW_WORKSPAC ...[truncated 1690 chars]
Remediation
View remediation
/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') } ``` 5. Canonicalize the workspace with `realpathSync()` or `path.resolve()` and require an absolute local path. 6. Validate the generated plist with `plutil -lint` before writing it to `~/Library/LaunchAgents`. 7. Write the plist atomically with restrictive permissions and do not load it if validation fails. 8. Apply the same changes to both `offerLaunchAgent()` and `cmdInstallDaemon()`. ]]>

T06 · System Persistence

Note
Location
scripts/taskflow-cli.mjs:256
Finding

Default-Accepted LaunchAgent Creates Persistent Execution of Mutable Workspace Code

Content
View full analysis
\/usr\/local\/bin\/node<\/string>/, `${nodeBin}`) writeFileSync(plistDest, plistContent, 'utf8') const { execSync } = await import('node:child_process') try { execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' }) ``` The installed template persists across sessions: ```xml ProgramArguments {{node}} {{workspace}}/taskflow/scripts/task-sync.mjs files-to-db StartInterval 60 RunAtLoad ``` ### Technical Analysis Automatic synchronization is a declared feature, and the installed job is user-level rather than system-level. Therefore, the persistence mechanism is not hidden and is functionally related to TaskFlow. However, persistence is not required for the core markdown/SQLite task-management functionality. During interactive setup, the prompt is default-accept (`[Y/n]`), so pressing Enter installs and immediately loads the LaunchAgent. The `--yes` option also enables installation without a dedicated daemon-specific confirmation. The LaunchAgent runs a JavaScript file from the mutable workspace every 60 seconds and at load. If that script is later replaced or modified, the persistent service executes the replacement automatically. This converts write acc ...[truncated 1605 chars]
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (62)

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 · SKILL.md (reported line 60)May include surrounding context.

const safeWorkspace = path.resolve(workspace)

text

3. **Do not construct paths from untrusted input.** Even with a valid `OPENCLAW_WORKSPACE`, never concatenate unvalidated user input onto it (e.g. `path.join(workspace, userSlug, '../../../etc/passwd')`). Use `path.resolve()` and check that the resolved path starts with the workspace root:

```js
function safeJoin(base, ...parts) {

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

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

Uninstall:

bash
systemctl --user disable --now taskflow-sync.timer
rm ~/.config/systemd/user/taskflow-sync.{service,timer}
systemctl --user daemon-reload

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

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

md
UNINSTALL:
    launchctl unload ~/Library/LaunchAgents/com.taskflow.sync.plist
    rm ~/Library/LaunchAgents/com.taskflow.sync.plist
-->
<plist version="1.0">
<dict>

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · system/com.taskflow.sync.plist.xml (reported line 32)May include surrounding context.

text
UNINSTALL:
    launchctl unload ~/Library/LaunchAgents/com.taskflow.sync.plist
    rm ~/Library/LaunchAgents/com.taskflow.sync.plist
-->
<plist version="1.0">
<dict>

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 · templates/plan-template.md (reported line 3)May include surrounding context.

md
# {{Project Name}} — Plan

<!-- ============================================================
  PLAN FILE
  ============================================================
  File name:  plans/<slug>-plan.md

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README promotes automatic 60-second background sync and workspace setup that modifies local markdown and SQLite state, but it does not clearly warn users that local files will be created, regenerated, and continuously updated. In an agent-oriented tool, this omission can cause unintentional data overwrites or silent propagation of sensitive task content, especially when users assume the tool is passive documentation rather than an active sync system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Apple Notes feature is described as pushing a live project status summary to Apple Notes, but the README does not clearly disclose the privacy implications of exporting project metadata into an external application and potentially across synced Apple devices. For a task-management skill used by agents, project names, statuses, and summaries may contain sensitive operational or business information, so silent external publishing increases data exposure risk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill uses a high-trust environment variable (OPENCLAW_WORKSPACE) extensively and includes operational guidance that can trigger filesystem and daemon-management actions, but it does not declare an explicit tool scope such as permissions or allowed-tools. In an agent setting, that omission weakens policy enforcement and increases the chance an agent will use environment access and shell actions more broadly than intended.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
89% confidence
Finding

The skill recommends installing a cron job for recurring execution of apple-notes-export.mjs, which establishes persistence on the host. While this is a legitimate product feature, persistence mechanisms are sensitive in agent-executed environments because they can outlive the session and continue accessing workspace data without fresh user approval.

Content

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

For hourly auto-refresh, add a cron entry:

bash
# Run: crontab -e
0 * * * * OPENCLAW_WORKSPACE=/path/to/workspace /path/to/node /path/to/taskflow/scripts/apple-notes-export.mjs

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

The taskflow install-daemon flow installs and enables a LaunchAgent or systemd user timer, creating background persistence. In a normal local app this may be expected, but in an agent skill it is risky because it extends execution beyond the interactive task and may be abused if invoked without fully informed consent.

Content

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

taskflow install-daemon

text

This detects your platform and installs the appropriate unit. On macOS it installs and loads the LaunchAgent; on Linux it writes systemd user units and enables the timer.

### macOS — LaunchAgent (manual steps)

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

Referencing the LaunchAgent plist template in the manual setup section is part of a workflow that installs persistent background execution on macOS. In skill form, that is security-relevant because it can lead agents or users toward host-level persistence changes.

Content

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

md
### macOS — LaunchAgent (manual steps)

Templates: `taskflow/system/com.taskflow.sync.plist.xml`

1. Copy `taskflow/system/com.taskflow.sync.plist.xml` → `~/Library/LaunchAgents/com.taskflow.sync.plist`
2. Replace `{{workspace}}` with the absolute path to your workspace (no trailing slash)

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The step instructing users to copy a plist into ~/Library/LaunchAgents is a concrete persistence action on macOS. LaunchAgents start automatically and therefore create enduring background execution beyond the current session.

Content

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

md
Templates: `taskflow/system/com.taskflow.sync.plist.xml`

1. Copy `taskflow/system/com.taskflow.sync.plist.xml` → `~/Library/LaunchAgents/com.taskflow.sync.plist`
2. Replace `{{workspace}}` with the absolute path to your workspace (no trailing slash)
3. Replace `{{node}}` with the path to your `node` binary (`which node`)
4. Load: `launchctl load ~/Library/LaunchAgents/com.taskflow.sync.plist`

Session Persistence

Medium
Category
Rogue Agent
Confidence
83% confidence
Finding

The same location also matches plist, but the actual risk is not the file extension itself; it is the activation of the LaunchAgent from that plist. In context, this contributes to a true persistence concern because it is part of the install-and-load sequence.

Content

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

md
1. Copy `taskflow/system/com.taskflow.sync.plist.xml` → `~/Library/LaunchAgents/com.taskflow.sync.plist`
2. Replace `{{workspace}}` with the absolute path to your workspace (no trailing slash)
3. Replace `{{node}}` with the path to your `node` binary (`which node`)
4. Load: `launchctl load ~/Library/LaunchAgents/com.taskflow.sync.plist`
5. Verify: `launchctl list | grep taskflow`

Uninstall:

Session Persistence

Medium
Category
Rogue Agent
Confidence
83% confidence
Finding

The same location also matches plist, but the actual risk is not the file extension itself; it is the activation of the LaunchAgent from that plist. In context, this contributes to a true persistence concern because it is part of the install-and-load sequence.

Content

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

md
1. Copy `taskflow/system/com.taskflow.sync.plist.xml` → `~/Library/LaunchAgents/com.taskflow.sync.plist`
2. Replace `{{workspace}}` with the absolute path to your workspace (no trailing slash)
3. Replace `{{node}}` with the path to your `node` binary (`which node`)
4. Load: `launchctl load ~/Library/LaunchAgents/com.taskflow.sync.plist`
5. Verify: `launchctl list | grep taskflow`

Uninstall:

Session Persistence

Medium
Category
Rogue Agent
Confidence
86% confidence
Finding

The instructions to create ~/.config/systemd/user and place unit files there are part of setting up user-level persistent services. Creating service definitions in autoloaded user-unit directories is a recognized persistence mechanism and is sensitive in automated agent contexts.

Content

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

Templates: taskflow/system/taskflow-sync.service and taskflow/system/taskflow-sync.timer

bash
# Create the user unit directory
mkdir -p ~/.config/systemd/user

# Copy templates, replacing placeholders

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The documented systemctl --user enable --now taskflow-sync.timer command enables a persistent user timer immediately. This is a real persistence mechanism and should be treated as sensitive because it creates ongoing background execution tied to the user's account.

Content

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

Enable and start

systemctl --user daemon-reload systemctl --user enable --now taskflow-sync.timer

text

Verify:

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

The guidance to use loginctl enable-linger $USER increases persistence further by allowing user services to run without an interactive login session. That materially expands the lifetime and autonomy of the background process and raises the risk profile in shared or server environments.

Content

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

systemctl --user daemon-reload

text

> **Note:** systemd user units require a login session. To run them without an interactive session (e.g. on a server), enable lingering: `loginctl enable-linger $USER`

---

Session Persistence

Medium
Category
Rogue Agent
Confidence
84% confidence
Finding

This line reiterates support for installing a periodic sync daemon. It is not independently exploitative, but it contributes to a pattern of encouraging persistent background execution from within the skill documentation.

Content

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

md
- Notes are one-way (markdown → DB). Removing a note in markdown does not clear it in DB.
- `db-to-files` rewrites all project task files, even unchanged ones.
- One task file per project (1:1 mapping). Multiple files per project is post-MVP.
- Periodic sync daemon: macOS (LaunchAgent) and Linux (systemd user timer) are supported. Run `taskflow install-daemon` to install.
- Node.js 22.5+ required (`node:sqlite`). No Python fallback in v1.

---

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The guide recommends exporting TaskFlow data into a public app directory and even serving it from an S3/CDN path, but it does not warn that project names, descriptions, task states, and activity history may contain sensitive internal information. In this skill context, the documentation could lead operators to unintentionally publish private operational metadata to broader audiences than intended.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

The same line was also flagged for plist-related persistence, but the real issue is unsafe shell construction around a path influenced by environment variables. In agent contexts where environment can be attacker-controlled, this can be more dangerous than ordinary CLI use because setup/install steps may run unattended.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 285)May include surrounding context.

js
// Load the agent
    const { execSync } = await import('node:child_process')
    try {
      execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' })
      console.log(`  ${c.green}loaded${c.reset}  com.taskflow.sync LaunchAgent (sync every 60s)`)
    } catch {
      console.log(`  ${c.yellow}!${c.reset} Could not load LaunchAgent automatically. Run manually:`)

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

The same line was also flagged for plist-related persistence, but the real issue is unsafe shell construction around a path influenced by environment variables. In agent contexts where environment can be attacker-controlled, this can be more dangerous than ordinary CLI use because setup/install steps may run unattended.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 285)May include surrounding context.

js
// Load the agent
    const { execSync } = await import('node:child_process')
    try {
      execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' })
      console.log(`  ${c.green}loaded${c.reset}  com.taskflow.sync LaunchAgent (sync every 60s)`)
    } catch {
      console.log(`  ${c.yellow}!${c.reset} Could not load LaunchAgent automatically. Run manually:`)

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 289)May include surrounding context.

js
console.log(`  ${c.green}loaded${c.reset}  com.taskflow.sync LaunchAgent (sync every 60s)`)
    } catch {
      console.log(`  ${c.yellow}!${c.reset} Could not load LaunchAgent automatically. Run manually:`)
      console.log(`    launchctl load "${plistDest}"`)
    }
  } catch (e) {
    console.log(`  ${c.yellow}!${c.reset} LaunchAgent install failed: ${e.message}`)

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 350)May include surrounding context.

js
console.log(`  ${c.green}loaded${c.reset}  com.taskflow.sync LaunchAgent (sync every 60s)`)
    } catch {
      console.log(`  ${c.yellow}!${c.reset} Could not load LaunchAgent automatically. Run manually:`)
      console.log(`    launchctl load "${plistDest}"`)
    }
  } catch (e) {
    console.log(`  ${c.yellow}!${c.reset} LaunchAgent install failed: ${e.message}`)

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Although flagged as persistence, the material risk here is command injection through shell-based process creation. In an agent skill, environment manipulation may be easier than in normal local CLI use, increasing the practical danger if this installer is invoked automatically or by higher-level tooling.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 346)May include surrounding context.

js
console.log(`  ${c.green}created${c.reset}  ${plistDest}`)

    try {
      execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' })
      console.log(`  ${c.green}loaded${c.reset}   com.taskflow.sync (syncs every 60s)`)
    } catch (e) {
      console.log(`  ${c.yellow}!${c.reset} Could not load automatically. Run:`)

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Although flagged as persistence, the material risk here is command injection through shell-based process creation. In an agent skill, environment manipulation may be easier than in normal local CLI use, increasing the practical danger if this installer is invoked automatically or by higher-level tooling.

Content

Scanner excerpt · scripts/taskflow-cli.mjs (reported line 346)May include surrounding context.

js
console.log(`  ${c.green}created${c.reset}  ${plistDest}`)

    try {
      execSync(`launchctl load "${plistDest}"`, { stdio: 'pipe' })
      console.log(`  ${c.green}loaded${c.reset}   com.taskflow.sync (syncs every 60s)`)
    } catch (e) {
      console.log(`  ${c.yellow}!${c.reset} Could not load automatically. Run:`)