Back to skill

Security audit

TTC CLI

Security checks for vulnerabilities and agentic risk

Overview

The TTC skill mostly matches its transit purpose, but its automatic installer can replace or delete an existing Claude skill path and it uses macOS location access, so it needs review before install.

Review this before installing globally. Back up or check ~/.claude/skills/ttc first, because installation may replace or delete that path. On macOS, use ttc nearby only if you are comfortable granting location permission; otherwise pass coordinates manually. Prefer an updated release that removes destructive postinstall replacement, uses safer temporary files, and refreshes vulnerable dependencies.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/postinstall.js:27
Finding
Destructive Replacement of an Existing Claude Skill Directory## Vulnerability Details **File Location**: `scripts/postinstall.js:27-41` **Vulnerability Type**: Destructive installation behavior and unauthorized replacement of user-controlled files **Risk Level**: High ### Vulnerable Code ```js if (existsSync(SKILL_LINK)) { try { const stats = lstatSync(SKILL_LINK); if (stats.isSymbolicLink()) { const currentTarget = readlinkSync(SKILL_LINK); if (currentTarget === PACKAGE_ROOT) { console.log('[ttc] Claude Code skill already configured.'); return true; } unlinkSync(SKILL_LINK); } else { rmSync(SKILL_LINK, { recursive: true }); } } catch (err) { console.log(`[ttc] Warning: ${err.message}`); } } symlinkSync(PACKAGE_ROOT, SKILL_LINK); ``` The vulnerable function is invoked automatically by the npm lifecycle configuration in `package.json:28`: ```json "postinstall": "node scripts/postinstall.js" ``` ### Technical Analysis During package installation, the script unconditionally takes control of the fixed path `~/.claude/skills/ttc`. If that path is a symbolic link to another target, the script removes the link. More seriously, if it is an ordinary file or directory, `rmSync(SKILL_LINK, { recursive: true })` recursively deletes it. The script does not establish that the existing path was created by this package, does not inspect or preserve its contents, and does not request confirmation. Installation of a transit CLI does not require deletion of pre-existing user-controlled data. This violates least-privilege and safe installer design principles. The subsequent `symlinkSync` call replaces the removed path with a link to the package root, causing this package to supersede any existing Skill registered under the same name. ### Attack Path 1. The user already has a file, directory, or different symbolic link at `~/.claude/skills/ttc`. 2. The user installs `@lucasygu/ttc` through npm without disabling lifecycle scripts. 3. npm automatically executes ...[truncated 982 chars]
Remediation
## Remediation Suggestions - If `~/.claude/skills/ttc` exists and is not the exact symlink previously created by this package, stop installation without modifying it. - Never recursively delete a pre-existing Skill directory during an automatic package lifecycle hook. - Require an explicit, separately invoked command and clear confirmation before replacing an existing Skill. - If replacement is requested, atomically rename the old path to a timestamped backup rather than deleting it. - Record installation ownership in package-specific metadata and only remove resources that can be reliably attributed to this package. - Consider making Claude Skill registration opt-in rather than executing it automatically during `postinstall`. - Use atomic link creation and report name conflicts with actionable manual instructions.

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/location.ts:46
Finding
Predictable Temporary Location File Allows Symlink-Based File Truncation## Vulnerability Details **File Location**: `src/lib/location.ts:46-49` **Vulnerability Type**: Predictable temporary file and symlink-following file creation **Risk Level**: Medium ### Vulnerable Code ```ts const tmpFile = join(tmpdir(), `ttc-location-${process.pid}.txt`); // Create empty file so we can detect if it was written to writeFileSync(tmpFile, ""); ``` The file is subsequently passed to the location helper and later read and removed: ```ts execFile("open", ["-W", HELPER_APP, "--args", tmpFile], { timeout: 20000 }, (err) => { try { const result = existsSync(tmpFile) ? readFileSync(tmpFile, "utf-8").trim() : ""; // Clean up try { unlinkSync(tmpFile); } catch {} ``` ### Technical Analysis The temporary filename contains only the process ID and is created in the shared operating-system temporary directory. Process IDs are observable or guessable, so another local user or process can pre-create candidate paths. `writeFileSync(tmpFile, "")` uses normal path-based file creation without exclusive-create semantics or explicit symbolic-link protection. If an attacker has already placed a symbolic link at the predicted path, Node.js follows that link and opens the target for writing. Because an empty string is written using the default truncating mode, the linked target can be reduced to zero bytes. There is also a race between path checks and subsequent operations. The code does not create a private temporary directory, verify file ownership and type using a safely held descriptor, or enforce exclusive creation. The helper itself writes to the supplied path, further extending the period in which path replacement could affect file operations. ### Attack Path 1. A local attacker determines that the victim may invoke `ttc nearby` on macOS. 2. The attacker predicts or sprays likely process-ID-based names such as `/tmp/ttc-location-<pid>.txt`. 3. For each candidate, the attacker creates a symbolic link to a file that the v ...[truncated 1116 chars]
Remediation
## Remediation Suggestions - Create a private temporary directory using `fs.mkdtemp()` under `tmpdir()` and restrict its permissions to the current user. - Generate the output file inside that private directory using a cryptographically unpredictable name. - Create the file with exclusive semantics, such as `openSync(path, "wx", 0o600)`, and fail if it already exists. - Where supported, use no-follow protections and verify with `lstat` that the path is a regular file owned by the current user. - Avoid closing and reopening a security-sensitive temporary pathname. Prefer communication through a pipe or inherited file descriptor if compatible with the macOS helper-launch mechanism. - Ensure the Swift helper does not follow attacker-controlled symbolic links when writing output. - Remove the private temporary directory recursively in a `finally` block after the helper exits or times out.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (33)

Known Vulnerable Dependency: protobufjs==7.5.4 — 12 advisory(ies): CVE-2026-44294 (protobuf.js: Denial of service from crafted field names in generated code); CVE-2026-44293 (protobuf.js: Code injection through bytes field defaults in generated toObject c); CVE-2026-44289 (protobuf.js: Denial of service through unbounded protobuf recursion) +9 more

Critical
Category
Supply Chain
Confidence
96% confidence
Finding
protobufjs is a direct runtime dependency path via gtfs-realtime-bindings, and this skill’s purpose involves consuming real-time transit protobuf feeds. Multiple listed issues include denial of service and code-generation/code-injection classes; in this context, parsing attacker-controlled or tampered feed data makes the dependency materially relevant and more dangerous than a dormant dev-only package.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documentation explicitly advertises `ttc nearby` auto-detecting location on macOS, but it does not clearly disclose the privacy implications, OS permission prompt, or the fact that location data may be accessed locally. In a transit skill, location access is contextually relevant, but silent or underexplained location collection is still sensitive because users may invoke the command without understanding it will request precise device location.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documentation explicitly advertises `ttc nearby` auto-detecting location on macOS, but it does not clearly disclose the privacy implications, OS permission prompt, or the fact that location data may be accessed locally. In a transit skill, location access is contextually relevant, but silent or underexplained location collection is still sensitive because users may invoke the command without understanding it will request precise device location.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documentation explicitly advertises `ttc nearby` auto-detecting location on macOS, but it does not clearly disclose the privacy implications, OS permission prompt, or the fact that location data may be accessed locally. In a transit skill, location access is contextually relevant, but silent or underexplained location collection is still sensitive because users may invoke the command without understanding it will request precise device location.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The documentation explicitly advertises `ttc nearby` auto-detecting location on macOS, but it does not clearly disclose the privacy implications, OS permission prompt, or the fact that location data may be accessed locally. In a transit skill, location access is contextually relevant, but silent or underexplained location collection is still sensitive because users may invoke the command without understanding it will request precise device location.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documentation explicitly advertises `ttc nearby` auto-detecting location on macOS, but it does not clearly disclose the privacy implications, OS permission prompt, or the fact that location data may be accessed locally. In a transit skill, location access is contextually relevant, but silent or underexplained location collection is still sensitive because users may invoke the command without understanding it will request precise device location.

Known Vulnerable Dependency: brace-expansion==2.0.2 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: linkify-it==5.0.0 — 2 advisory(ies): CVE-2026-48801 (LinkifyIt#match scan loop has quadratic algorithmic complexity); CVE-2026-59887 (linkify-it: Quadratic-complexity DoS via the `mailto:` validator scan-loop on at)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: lodash==4.17.23 — 2 advisory(ies): CVE-2025-13465 (lodash vulnerable to Prototype Pollution via array path bypass in `_.unset` and ); CVE-2021-23337 (lodash vulnerable to Code Injection via `_.template` imports key names)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: protobufjs-cli==1.2.0 — 4 advisory(ies): CVE-2026-44295 (protobuf.js: Code injection in pbjs static output from crafted schema names); CVE-2026-54269 (protobufjs : Schema-derived names can shadow runtime-significant properties); CVE-2026-42290 (protobuf.js is Vulnerable to OS Command Injection in the CLI) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: tmp==0.2.5 — 1 advisory(ies): CVE-2026-44705 (tmp has Path Traversal via unsanitized prefix/postfix that enables directory esc)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding
The skill uses 'network' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
# System status
ttc status

# Live monitoring — re-run any command on an interval
ttc loop 3m next "king spadina"        # watch arrivals every 3 min
ttc loop 5m alerts                     # monitor disruptions
ttc loop 2m vehicles 504              # track vehicles approaching
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This markdown file documents that `ttc nearby` automatically detects the user's location on macOS, but it only mentions the OS permission prompt and installation requirement. It does not explicitly warn users that the skill will access and use precise location data, which is a privacy-relevant behavior covered by missing user warnings for markdown files.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The script uses `npx tsx` without pinning an exact package version, which can cause execution of whatever `tsx` version is resolved at runtime. In a supply-chain compromise or unexpected registry update scenario, this can execute unreviewed code during maintenance workflows and makes builds less reproducible.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The manifest describes a TTC skill for arrivals, vehicle tracking, alerts, and stop search. This file obtains the user's current latitude/longitude via CoreLocation and writes it out, which is a separate capability not explicitly covered by that description and goes beyond obvious transit-data retrieval behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script collects precise latitude/longitude and may write it to an arbitrary file path supplied on the command line, but it provides no in-script disclosure, consent flow, or restrictions on where that sensitive data is stored. This creates a privacy risk because location data can persist on disk unintentionally and be accessed by other local processes, logs, or users depending on the environment.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The post-install script compiles and installs a macOS CoreLocation helper, expanding the package's effective capabilities beyond simple transit lookups into device-location access. Even if used for a legitimate 'nearby stops' feature, doing this automatically at install time increases trust and privacy risk because users may not expect native helper creation and permission-enabling behavior from the package description alone.

Session Persistence

Medium
Category
Rogue Agent
Content
const appDir = join(PACKAGE_ROOT, 'helpers', 'TTC Location.app', 'Contents');
  const macosDir = join(appDir, 'MacOS');
  const swiftSrc = join(PACKAGE_ROOT, 'scripts', 'get-location.swift');
  const plistSrc = join(PACKAGE_ROOT, 'scripts', 'Info.plist');
  const binary = join(macosDir, 'ttc-location');

  try {
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.

Session Persistence

Medium
Category
Rogue Agent
Content
const appDir = join(PACKAGE_ROOT, 'helpers', 'TTC Location.app', 'Contents');
  const macosDir = join(appDir, 'MacOS');
  const swiftSrc = join(PACKAGE_ROOT, 'scripts', 'get-location.swift');
  const plistSrc = join(PACKAGE_ROOT, 'scripts', 'Info.plist');
  const binary = join(macosDir, 'ttc-location');

  try {
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.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The shebang uses `#!/usr/bin/env npx tsx`, which can cause `npx` to resolve and execute an unpinned `tsx` package at runtime if it is not already installed locally. That creates a supply-chain risk: a malicious or compromised package version could be fetched and run with the developer's privileges when this script is invoked. In this context, the script is a maintenance utility that downloads and processes transit data, so it is not inherently suspicious, but the dynamic package execution still makes the finding valid.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The `nearby` command automatically attempts device location detection when no coordinates are supplied, and the only notice is embedded in the command description/help text. This can expose precise location data without a clear just-in-time privacy prompt or explicit opt-in, which is sensitive information even in a transit-focused CLI.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
This code collects precise device geolocation, which is sensitive personal data. In the stated transit-focused skill context, location use may be functionally relevant, but the metadata/description shown does not clearly disclose that precise device location is accessed, creating a transparency and privacy-consent problem if users are not adequately informed.

Context-Inappropriate Capability

Medium
Confidence
77% confidence
Finding
For a transit-information skill, making transit API calls is expected, but spawning a platform-specific .app bundle through the `open` command is an additional capability not justified by the manifest text. This introduces local process-execution behavior and OS permission interaction beyond the stated transit lookup functionality.

Missing User Warnings

Low
Confidence
95% confidence
Finding
The skill says `ttc nearby` can auto-detect location on macOS without an explicit privacy warning or consent-oriented explanation. While this is not inherently malicious and is relevant to nearby-transit functionality, geolocation is sensitive data and should be clearly disclosed before use.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/postinstall.js:65

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/cli.ts:746