Jun 23, 2026·6 min read·6 visits
Multiple critical vulnerabilities in skillctl allow malicious repositories to perform argument injection, system-wide directory deletion, persistent denial of service, commit forgery, and credential exfiltration when processed by AI developer agents.
An in-depth security audit of the skillctl command-line package manager revealed five critical and high-severity security vulnerabilities. The identified flaws span parameter-level command argument injection via the source_sha parameter, uncontrolled resource consumption (Denial of Service) through unnamed UNIX FIFOs and character devices, directory path traversal in the destination argument, commit-message trailer forgery via newline injection in skill names, and local credential exfiltration leveraging UNIX hardlinks. These vulnerabilities represent significant vectors for workstation compromise when executing agentic tasks in repositories containing untrusted files or pull requests. Remediation was introduced in version v0.1.3.
The command-line package manager skillctl coordinates, installs, and updates 'skills' across multiple AI-assisted coding environments, including Claude Code, Cursor, and Codex. In these automated spaces, LLM agents translate natural language instructions or untrusted context files into local shell configurations and software dependencies. Because an agent executes commands based on the contents of open repositories, any input boundaries processed by skillctl represent a high-priority attack surface.
A technical security review of the tool identified five severe design flaws: parameter-level command argument injection, resource exhaustion leading to denial of service, directory path traversal, metadata injection, and hardlink file replication. These vulnerabilities bypass standard path protections and validation routines to target the local operator's workspace environment.
The first flaw lies within InstalledSkill.source_sha, which is parsed from the localized configuration file .skills.toml. Because this file resides inside a project workspace, an attacker can modify its keys via a standard commit or pull request. The application passes the parameter directly to a subprocess running git ls-tree -r -z <refspec> -- <path>. By setting the refspec to a flag (such as --name-only), an attacker corrupts the downstream diff execution path, forcing local destructive actions.
The second vulnerability resides in the fs_util::copy_dir_all recursive copy utility. When copying directories, the tool historically validated directory entries with is_dir() and is_symlink(). Any other file descriptor, such as a FIFO named pipe, a character device (/dev/zero), or a block device, was passed directly to std::fs::copy. A FIFO blocks the execution routine indefinitely waiting for a writer, while a character device streams infinite bytes, resulting in disk space exhaustion or an Out-Of-Memory (OOM) error.
The third vulnerability is a path traversal in the --dest argument of the add subcommand. Because the CLI accepted relative parent indicators (..) and absolute paths without sanitization, an LLM agent instructed by adversarial prompt injections could be manipulated into launching a recursive deletion command against arbitrary directories, such as ~/.ssh, using the --on-conflict overwrite flag.
The fourth vulnerability, CRLF metadata injection, stems from writing unescaped skill names directly to git commit arguments (e.g., git commit -m "update skill: <name>"). Including newlines inside a skill's name allows an attacker to forge commit-message trailers (such as Co-Authored-By: ...), which automated release tools and CI/CD pipelines use for authentication and logging. Finally, the fifth vulnerability involves hardlinks. While symbolic links were historically restricted, hardlinks share the target file's inode and are evaluated as normal files by std::fs::symlink_metadata. A hardlinked credential file placed in a skill folder would be fully copied into the public workspace directory by the application and subsequently exfiltrated via skillctl push.
To analyze the remediation, we inspect the changes in commit 28dfce3865c10bf2072081ee621d1adbcc2f19e1. The maintainer hardened the parameter resolution and introduced strict path checks.
In src/commands/add.rs, absolute path validation was restricted when running in agent or non-interactive environments, and directory traversal characters are now rejected unconditionally:
for component in dest.components() {
if matches!(component, std::path::Component::ParentDir) {
return Err(AppError::Config(format!(
"invalid --dest `{}`: parent traversal (`..`) is not allowed",
dest.display()
))
.into());
}
}
if dest.is_absolute() && !ctx.interactive {
return Err(AppError::Config(format!(
"invalid --dest `{}`: absolute paths are not allowed in non-interactive mode",
dest.display()
))
.into());
}In src/fs_util.rs, the copier was updated to enforce strict file type assertions and unix link-count checks (nlink() > 1) to block both hardlink resolution and special device file-type handling:
if file_type.is_dir() {
copy_dir_all(&from, &to)?;
} else if file_type.is_file() {
reject_hardlink(&from)?;
fs::copy(&from, &to)?;
} else {
return Err(AppError::Config(format!(
"refusing to copy `{}`: not a regular file or directory (FIFO, socket, device, or other special file)",
from.display()
))
.into());
}Under UNIX platforms, the hardlink validation routine inspects file metadata to count active inodes:
#[cfg(unix)]
fn reject_hardlink(path: &Path) -> Result<()> {
use std::os::unix::fs::MetadataExt;
let meta = fs::metadata(path)?;
if meta.nlink() > 1 {
return Err(AppError::Config(format!(
"refusing to copy `{}`: file has {} hardlinks",
path.display(),
meta.nlink()
))
.into());
}
Ok(())
}Finally, in src/project_config.rs, the source_sha parameter is explicitly restricted to a strict hexadecimal length-checked regular set to prevent parameter injection:
if !is_hex_sha(&self.source_sha) {
return Err(AppError::Config(format!(
"invalid source_sha: expected 40-64 hex characters, got `{}`",
self.source_sha
)));
}Exploitation of these flaws requires an attacker to control a workspace processed by skillctl. For example, in an argument injection attack, a user reviews or checks out an untrusted pull request containing a malicious .skills.toml file. When the agent synchronizes the project state, the argument is integrated into the downstream git ls-tree CLI execution, manipulating its runtime flags.
For hardlink-based credential exfiltration, the attacker commits a hardlinked file pointing to a highly sensitive file path, such as ~/.ssh/id_rsa. When the victim clones the repository and runs skillctl add, the copy routine resolving the duplicate inode writes the private key file directly into the local repository folder. The file is subsequently committed and pushed back to the attacker's server during synchronization.
To prevent regressions, the developer introduced a comprehensive suite of automated integration tests verifying each failure path:
#[test]
fn copy_dir_all_rejects_fifo_inside_source() {
let src = TempDir::new().unwrap();
let dst = TempDir::new().unwrap();
let fifo_path = src.path().join("evil_pipe");
std::process::Command::new("mkfifo")
.arg(&fifo_path)
.status()
.unwrap();
let result = copy_dir_all(src.path(), &dst.path().join("out"));
assert!(result.is_err());
}These vulnerabilities expose developer environments to significant risks. Since skillctl runs locally within the developer's shell session and with user-level privileges, successful exploitation allows an attacker to manipulate Git configurations, delete system configurations, and exfiltrate private authentication keys. Because NVD has not assigned a CVE ID, these risks may remain undetected by standard software composition analysis (SCA) scanners, which typically rely on official NVD feeds rather than GHSA advisories.
Although the flaws are primarily triggered locally, the vulnerability context is highly amplified because the target application is specifically integrated into LLM-driven autonomous workflows. Autonomous agents frequently lack the context or guardrails necessary to identify subtle path manipulations or command injection attempts in untrusted files, converting passive file reading into active system compromises.
A technical review of the remediation commit reveals two security limitations. First, the hardlink protection mechanism is gated behind a UNIX compilation flag (#[cfg(unix)]). On Windows compilation systems, the application falls back to an empty stub function:
#[cfg(not(unix))]
fn reject_hardlink(_path: &Path) -> Result<()> { Ok(()) }Because Windows NTFS natively supports hardlinks, a developer running the application on Windows remains vulnerable to local file exfiltration using the exact same hardlink copying attack vector.
Second, the implementation introduces a Time-of-Check to Time-of-Use (TOCTOU) race condition. There is a temporal gap between verifying file metadata via reject_hardlink or symlink_metadata and executing the subsequent copy operation with fs::copy. A highly synchronized local exploit process could swap a standard file with a hardlink, pipe, or symbolic link inside this execution window, bypassing the runtime security checks.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
skillctl umanio-agency | < v0.1.3 | v0.1.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-88, CWE-400, CWE-22, CWE-93, CWE-59 |
| Attack Vector | Local / Social Engineering via Malicious Repository Configuration |
| CVSS v3.1 | 8.6 (High) |
| Exploit Status | Proof of Concept (PoC) available in official test suite |
| Impact | Command Execution, Directory Deletion, Denial of Service, Local Secret Exfiltration |
| Remediation Status | Patched in version v0.1.3 |
The software constructs a command string or command array using external input but does not neutralize or validate arguments before execution, allowing attackers to pass unexpected arguments to downstream command-line processes.
An observable timing discrepancy vulnerability (CWE-208) in Filament's administrative login page allows unauthenticated remote attackers to determine the existence of registered email addresses. This timing side-channel arises from short-circuiting logic that skips expensive password hashing checks when a queried email address is not found in the database. Attackers can execute statistical timing attacks to map active administrator accounts, facilitating subsequent targeted brute-force or credential-stuffing campaigns.
Filament's ImageColumn (used in tables) and ImageEntry (used in infolists) components render database values inside HTML attributes without validation or sanitization. This allows an attacker to inject arbitrary HTML attributes, leading to Stored Cross-Site Scripting (XSS).
The Netty incubator codec for Oblivious HTTP (OHTTP) fails to verify that a cryptographically signed final chunk is received before the outer HTTP body terminates. This missing validation allows an on-path adversary to truncate chunked-OHTTP messages cleanly at a non-final chunk boundary, leading to undetected data truncation and compromising message integrity. The vulnerability affects multiple versions of the maven package io.netty.incubator:netty-incubator-codec-ohttp prior to 0.0.22.Final.
Prior to version 4.1.4, phpMyFAQ used the cryptographically broken SHA-1 algorithm to hash custom attachment encryption keys stored in the database. Attackers with database access can recover these plaintext keys through offline brute-force attacks and subsequently decrypt sensitive file attachments.
A privilege escalation vulnerability in Snipe-IT versions prior to 8.6.0 allows authenticated users with profile-editing capabilities to elevate their own permissions by performing a PATCH request on their own user endpoint.
CVE-2026-48500 is an authorization bypass vulnerability within Filament, a full-stack Laravel administration panel suite. The flaw arises from the unauthenticated exposure of Livewire's file upload RPC endpoints on guest-facing pages, allowing remote actors to upload arbitrary files to temporary storage, potentially leading to storage exhaustion and service disruption.