Back to skill

Security audit

org-cli

Security checks for vulnerabilities and agentic risk

Overview

This org-mode helper is mostly coherent, but a confirmed path-handling flaw can let note and TODO writes escape the configured org workspace.

Install only if you are comfortable letting the agent modify your org files and org-roam database. Until the path handling is fixed, avoid using custom file or inboxFile values with path components, keep ORG_CLI_DIR pointed at a dedicated workspace, and prefer previews or explicit confirmation for schedule, state, append, and batch changes.

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

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
plugin/lib.ts:128
Finding

Workspace Escape Through Unvalidated File Paths

Content
View full analysis

Vulnerability Details

File Location: plugin/lib.ts:128-168
Vulnerability Type: Path traversal and workspace authorization bypass
Risk Level: Medium

The file parameter exposed by the TODO and note creation tools is documented as relative to the configured workspace, but the implementation does not enforce that boundary.

Vulnerable Code

ts
export function buildAddTodoArgs(
  cfg: OrgCliConfig,
  params: {
    title: string;
    scheduled?: string;
    deadline?: string;
    file?: string;
  },
): string[] {
  const filePath = join(cfg.dir, params.file ?? cfg.inboxFile);
  const args = [
    "add",
    filePath,
    params.title,
    "--todo",
    "TODO",
    "--db",
    cfg.db,
    "-f",
    "json",
  ];
  if (params.scheduled) {
    args.push("--scheduled", params.scheduled);
  }
  if (params.deadline) {
    args.push("--deadline", params.deadline);
  }
  return args;
}

export function buildAddNoteArgs(
  cfg: OrgCliConfig,
  params: {
    text: string;
    file?: string;
  },
): string[] {
  const filePath = join(cfg.dir, params.file ?? cfg.inboxFile);
  return ["add", filePath, params.text, "--db", cfg.db, "-f", "json"];
}

These argument builders are reachable from the org_add_todo and org_add_note tools in plugin/index.ts:211-229 and plugin/index.ts:257-274.

Technical Analysis

join(cfg.dir, userControlledFile) normalizes path traversal components but does not verify that the resulting path remains inside cfg.dir. For example, with a workspace of /home/user/org, a value such as ../../tmp/outside.org resolves outside the declared workspace.

The normalized path is subsequently passed to the configured org executable through execFile. The use of execFile prevents shell metacharacter expansion and therefore avoids direct shell injection, but it does not provide filesystem confinement or path authorizat ...[truncated 1464 chars]

Remediation
View remediation

Remediation Suggestions

  1. Resolve and validate every destination against the canonical workspace root before invoking org:

    ts
    import { isAbsolute, relative, resolve } from "node:path";
    
    function resolveWorkspaceFile(workspace: string, input: string): string {
      if (isAbsolute(input)) {
        throw new Error("Absolute file paths are not permitted");
      }
    
      const root = resolve(workspace);
      const candidate = resolve(root, input);
      const rel = relative(root, candidate);
    
      if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
        throw new Error("File path must remain inside the org workspace");
      }
    
      return candidate;
    }
    
  2. Use this validation in both buildAddTodoArgs and buildAddNoteArgs, including when the destination comes from cfg.inboxFile.

  3. If only top-level org filenames are required, apply a stricter allowlist: reject directory separators and accept only filenames matching a pattern such as ^[A-Za-z0-9._-]+\.org$.

  4. Where nested workspace files are supported, account for symbolic-link escapes by resolving the nearest existing parent with realpath() and confirming it remains under the canonical workspace directory.

  5. Add tests covering:

    • ../outside.org
    • ../../outside.org
    • absolute paths
    • nested valid paths
    • configured inboxFile traversal
    • symbolic links pointing outside the workspace
  6. Reject invalid paths before spawning the external executable and return a structured authorization error to the tool caller.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (13)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares extensive read/write scope in metadata and instructs the agent to perform persistent mutations, but it does not declare an explicit tool permission boundary such as allowed-tools/permissions. That increases the chance an agent runtime will overgrant execution or environment access, making file mutation behavior less constrained and less auditable.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill frames persistence as the default behavior with directives like 'Everything persistent goes in org' and 'Never reply "got it" without a corresponding write,' without an upfront user-facing warning that routine conversation may modify files. In context, this is more dangerous because the skill has direct write access to the user's workspace and knowledge base, so normal interactions can lead to unexpected durable changes.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The ambient capture guidance tells the agent to 'offer to save' durable facts and says to 'bias toward capturing,' which can push the agent toward unsolicited persistence of sensitive personal information. Even though it says to wait for confirmation after the offer, the policy normalizes aggressive note-taking and raises the risk of collecting more personal data than the user intended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The before_agent_start hook injects instructions telling the agent to act on quick-capture prefixes immediately and confirms only briefly, which encourages writes to the user's org workspace without an explicit consent gate at execution time. In an agent setting, this increases the risk of unintended file mutation from ambiguous input, prompt injection, or casual conversation being interpreted as a write command.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

org_mark_done performs a state-changing write directly against org files using only a provided CUSTOM_ID, with no built-in confirmation, dry-run mode, or user-notice mechanism. If the agent is manipulated or selects the wrong item, tasks can be silently marked complete, corrupting the user's task state and trust in the system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

org_set_state allows arbitrary TODO state transitions with no validation against user intent beyond the raw parameters and no confirmation barrier. That makes it easy for an injected instruction or mistaken match to alter workflow-critical metadata across the user's notes and tasks.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

org_reschedule modifies scheduling metadata, including clearing dates, without any user-facing warning or confirmation path in code. In a productivity skill, schedule integrity is important, so accidental or adversarial changes can meaningfully disrupt reminders, planning, and deadlines.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

org_append writes arbitrary text into an existing headline body with no confirmation or content review step. Because the tool can persist any supplied text, an agent influenced by prompt injection or misunderstanding could insert misleading, sensitive, or unwanted content into the user's long-term notes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

org_roam_upsert can both create new roam nodes and append facts automatically, which turns conversationally derived data into durable knowledge-base writes without a consistent consent gate. In this skill's context, that is especially sensitive because the plugin explicitly encourages ambient capture of durable facts, increasing the chance of storing inaccurate, private, or user-unapproved information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This section provides direct write-capable commands (add, todo set, schedule, note, append, refile) against the user's org files without instructing the agent to obtain confirmation before making persistent changes. In an agent setting, that omission can lead to unintended file modification from ambiguous or casually phrased user requests, especially because the commands operate on the user's task store and notes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The batch operations section enables multiple sequential mutations with atomic writeback but does not warn that a single invocation may modify several tasks or files at once. That increases the blast radius of mistakes or prompt manipulation, because an agent could apply broad unintended changes in one operation without giving the user a clear preview of all pending modifications.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · plugin/package.json (reported line 32)May include surrounding context.

json
"test": "node --test --experimental-strip-types index.test.ts"
  },
  "dependencies": {
    "@sinclair/typebox": "^0.34.48"
  },
  "devDependencies": {
    "@types/node": "^22.10.0"

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · plugin/package.json (reported line 35)May include surrounding context.

json
"@sinclair/typebox": "^0.34.48"
  },
  "devDependencies": {
    "@types/node": "^22.10.0"
  }
}

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
plugin/lib.ts:179