Back to skill

Security audit

Productivity Tracker

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent local productivity tracker, but its report-writing code can overwrite Markdown files outside the intended logs folder.

Review this skill before installing. Its local-only productivity tracking is broadly disclosed and purpose-aligned, but activity names, timestamps, and reports may reveal sensitive routines, and the report generation bug should be fixed before trusting it with write access beyond its own data directory.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
src/index.ts:175
Finding

Path Traversal Allows Arbitrary Markdown File Overwrite

Content
View full analysis

Vulnerability Details

File Location: src/index.ts, lines 175–176 and 219
Vulnerability Type: Path traversal and uncontrolled file write
Risk Level: High

Vulnerable Code

ts
export function generateReport(date?: string): string {
  const targetDate = date || new Date().toISOString().split('T')[0];
ts
  fs.writeFileSync(path.join(LOGS_DIR, `${targetDate}.md`), report);
  return report;
}

Technical Analysis

The exported generateReport() function accepts a caller-controlled date string and inserts it directly into an output filename. The code does not enforce the expected YYYY-MM-DD format, reject path separators, or verify that the resolved destination remains inside LOGS_DIR.

path.join() normalizes traversal components such as ../, but it does not constrain the resulting path to the intended directory. Consequently, a value such as ../../SKILL can cause the destination path to escape data/logs:

text
data/logs/../../SKILL.md

This normalizes to the project-level SKILL.md. Because fs.writeFileSync() replaces an existing file by default, the destination can be overwritten if the process has filesystem permission to write it.

The .md suffix limits direct targeting to paths ending in .md, but it does not prevent traversal or protect Markdown files outside the report directory.

Attack Path

  1. An attacker gains control of, or influences, the argument passed to generateReport().

  2. The attacker supplies a traversal value, for example:

    ts
    generateReport("../../SKILL");
    
  3. The function uses the unvalidated value as both report content metadata and part of the filename.

  4. path.join(LOGS_DIR, "../../SKILL.md") resolves outside LOGS_DIR.

  5. fs.writeFileSync() overwrites the resolved Markdown file.

  6. If the overwritten file is subsequently loaded as agent instructions, the generated content may alter or break Skill behavior. Even without subsequent loading, the overwr ...[truncated 845 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce an exact ISO date format before using the value:

    ts
    function validateDate(value: string): string {
      if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
        throw new Error('Date must use YYYY-MM-DD format');
      }
    
      const parsed = new Date(`${value}T00:00:00.000Z`);
      if (
        Number.isNaN(parsed.getTime()) ||
        parsed.toISOString().slice(0, 10) !== value
      ) {
        throw new Error('Invalid calendar date');
      }
    
      return value;
    }
    
  2. Resolve the final path and verify that it remains directly under the expected logs directory:

    ts
    const safeDate = validateDate(
      date || new Date().toISOString().slice(0, 10),
    );
    
    const logsRoot = path.resolve(LOGS_DIR);
    const outputPath = path.resolve(logsRoot, `${safeDate}.md`);
    
    if (path.dirname(outputPath) !== logsRoot) {
      throw new Error('Invalid report path');
    }
    
    fs.writeFileSync(outputPath, report, {
      encoding: 'utf8',
      flag: 'w',
      mode: 0o600,
    });
    
  3. Never construct filesystem paths from raw caller input. Derive the filename from a successfully parsed date or another strict allowlisted identifier.

  4. Add tests covering traversal and malformed input, including ../../SKILL, ../report, absolute paths, encoded separators, invalid dates, and platform-specific separators.

  5. Run the Skill with least-privilege filesystem permissions so the process can write only to its intended data and log directories.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Using a single broad command like "complete" as a control phrase can easily collide with normal conversation and cause unintended state changes, such as stopping the currently tracked activity. In a voice-driven workflow, accidental activation can corrupt logs, durations, and derived productivity reports without the user intending to issue a command.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Examples like "what did I do today" and "analyze my productivity this week" are broad natural-language phrases without clear trigger boundaries, increasing the chance the skill interprets ordinary conversation as commands. This is especially risky in always-listening or multi-agent contexts, where unintended invocation could expose summaries or alter records unexpectedly.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill states that all data is stored locally and that there is no cloud sync by default, but it also advertises integrations with calendar events, health apps, and a custom API. This creates a misleading privacy boundary: users may provide sensitive activity, health, or schedule data believing it never leaves the device when optional integrations could transmit it externally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill persistently writes user activity data to disk in a local JSON file without any consent prompt, retention notice, or indication that the data will be stored. Because activity names and timestamps can reveal sensitive routines, work habits, health activities, or personal life details, this creates a privacy risk if users assume the tool operates only on transient input.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Report generation writes a markdown file containing dated activity summaries and named activities to disk without notifying the user. These reports can aggregate sensitive behavioral data into easily readable files, increasing exposure to other local users, backups, or unintended sharing compared with ephemeral output shown only in-session.

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 · package.json (reported line 28)May include surrounding context.

json
"test": "tsc --noEmit"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.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 · package.json (reported line 29)May include surrounding context.

json
},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}

Static analysis

No suspicious patterns detected.