Jun 5, 2026·6 min read·4 visits
A cluster of path-safety flaws in skillctl allows attackers to exfiltrate local files via symbolic links and delete arbitrary directories using manipulated path configurations.
An analysis of four critical vulnerabilities in the skillctl Rust crate (versions 0.1.0 and 0.1.1) that allow arbitrary file exfiltration and directory deletion.
The skillctl command-line utility is a Rust crate designed to manage and synchronize personal agent skill libraries across software engineering projects. In versions 0.1.0 and 0.1.1, the tool suffers from a critical cluster of filesystem path validation and symbolic link handling vulnerabilities. These flaws expose operators to potential arbitrary file disclosure and arbitrary directory deletion.
The primary attack surface resides within the mechanism used to clone, copy, and synchronize skill folders. This process involves copying directory structures from remote or local repositories and reading configuration definitions from user-supplied .skills.toml files. Because the software fails to validate input paths and handle symbolic links securely, malicious repositories can manipulate the host filesystem during standard CLI operations.
This vulnerability cluster is tracked as GHSA-WX3M-WHQV-XV47. The vulnerabilities present a High severity risk due to the potential for unauthorized local file exfiltration and local denial of service through file destruction. The issues are resolved in the 0.1.2 release.
The root cause of these vulnerabilities stems from four distinct programming oversights in skillctl related to path resolution and filesystem operations. The first flaw lies in the recursive copying helper function fs_util::copy_dir_all. When resolving directory entries, the code uses standard Rust filesystem APIs without checking whether a directory entry is a symbolic link. If an entry is a symbolic link to a file, the API evaluates is_dir() to false and falls back to standard file copying, which implicitly dereferences the link and copies the target content.
The second flaw is located in the serialization of the .skills.toml configuration file. The configuration parser deserializes destination and source_path directly into PathBuf structures without enforcing boundaries or performing lexical path validation. In Rust, joining an absolute path to an existing PathBuf completely replaces the base path, allowing an absolute destination path in the configuration file to hijack the entire filesystem path used during execution.
The third and fourth flaws exist in parameter handling. The --target argument in the detect subcommand and the user-defined fork names fail to validate relative path traversal sequences (..). By supplying traversal sequences, an attacker can escape the designated project directory root. When the utility attempts to clean or rename folders during synchronization, it deletes or overwrites directories outside the intended workspace.
An analysis of the vulnerable source code prior to the patch reveals how the recursive directory copying process fails to account for symbolic links. In the fs_util::copy_dir_all implementation, the code performs a standard directory traversal. It evaluates file types and executes a standard file copy operation when the item is not a directory.
// Vulnerable implementation in fs_util.rs (<= 0.1.1)
// If the entry is a symlink to a file, is_dir() evaluates to false,
// and fs::copy will dereference the symlink and copy the target file.
if entry.file_type()?.is_dir() {
copy_dir_all(&from, &to)?;
} else {
fs::copy(&from, &to)?; // Vulnerable: implicit dereference of symlinks
}The patch introduced in commit 827fff5c0698dd9e48e777d5907cf7bc19b91aca resolves this by querying symbolic link metadata explicitly before executing filesystem modifications. The updated implementation replaces the blind directory traversal with checks utilizing symlink_metadata and halts operations if a symbolic link is detected.
// Patched implementation in fs_util.rs (>= 0.1.2)
// The function now explicitly rejects symlinks to prevent dereferencing.
pub fn copy_dir_all(src: &Path, dst: &Path) -> Result<()> {
let src_meta = fs::symlink_metadata(src)?;
if src_meta.file_type().is_symlink() {
return Err(AppError::Config("refusing to copy symlink".into()));
}
// ...
for entry in fs::read_dir(src)? {
let entry = entry?;
let from = entry.path();
let to = dst.join(entry.file_name());
let file_type = entry.file_type()?;
if file_type.is_symlink() {
return Err(AppError::Config("refusing to copy nested symlink".into()));
}
if file_type.is_dir() {
copy_dir_all(&from, &to)?;
} else {
fs::copy(&from, &to)?; // Safe: symlinks have been actively blocked
}
}
Ok(())
}Exploitation of these vulnerabilities requires inducing an operator to run skillctl commands on a project containing a malicious configuration or a compromised skill library. In a file exfiltration scenario, an attacker publishes a repository containing a skill with a symbolic link. This symbolic link is designed to point to a known location of sensitive files on the operator's machine, such as /home/user/.aws/credentials or /etc/hostname.
When the operator adds the skill using the skillctl add command, the utility clones the repository and copies its contents. The application traverses the directories, encounters the symbolic link, treats it as a standard file due to the flawed file-type check, and copies the contents of the target sensitive file into the local project workspace. The next time the user runs a synchronization command like skillctl push, the copied file is uploaded to the remote repository.
In a directory destruction scenario, an attacker can modify the .skills.toml configuration file in a shared repository or via a Pull Request. By setting the destination field to an absolute path such as /home/victim/.ssh or a relative path with directory traversals such as ../../.ssh, the attacker forces the utility to execute filesystem operations outside the project root. When the victim runs skillctl pull, the replace_folder_contents function executes fs::remove_dir_all on the target directory, deleting the victim's local configurations.
The security impact of GHSA-WX3M-WHQV-XV47 is characterized by unauthorized disclosure of sensitive data and localized denial of service through file destruction. Because the tool runs with the privileges of the invoking user, the exploitation range is bounded only by the operating system permissions of the operator running the CLI utility.
The arbitrary file exfiltration vector allows external actors to target high-value assets. This includes configuration profiles, cryptographic keys, authentication tokens, and environment configurations stored on developers' machines. This exfiltration bypasses standard access controls by leveraging the legitimate write-back functionality of the tool during subsequent repository pushes.
The directory deletion vector presents a severe threat to operational integrity. A single automated pull request can cause irreversible loss of key directories on developer workstations. No specific host configurations or administrative privileges are required to exploit these flaws beyond standard command execution permissions.
The primary remediation strategy is the immediate upgrade of all skillctl installations to version 0.1.2 or later. The update introduces robust lexical path safety checks and explicitly rejects symbolic links during cloning and synchronization operations. Users can update their global installation by running cargo install skillctl --force.
If immediate upgrading is not feasible, operators must implement strict manual workarounds. Avoid running skillctl commands within untrusted repositories or merging pull requests that modify .skills.toml configurations without performing a manual security review of the proposed file paths.
Additionally, operators can inspect local systems for indicators of compromise. Search .skills.toml files for absolute paths or directory traversal sequences using text search utilities. Furthermore, search for symbolic links within downloaded skill libraries to ensure no links point to sensitive system folders.
| Product | Affected Versions | Fixed Version |
|---|---|---|
skillctl umanio-agency | >= 0.1.0, <= 0.1.1 | 0.1.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22, CWE-61 |
| Attack Vector | Local, via malicious remote repository or pull request configurations |
| CVSS Severity | High (7.5) |
| Exploit Status | none |
| KEV Status | not listed |
A high-severity stored Cross-Site Scripting (XSS) vulnerability was identified in the TinyMCE rich text editor. The flaw exists in the handling of the 'protect' configuration option, where forged placeholder comments containing malicious payloads bypass the editor's sanitization routines and execute arbitrary JavaScript during serialization and content restoration.
An authorization bypass and client-side property tampering vulnerability (CVE-2026-47742) in the Shopper headless admin panel (built on Laravel and Livewire) allows low-privileged users to modify arbitrary product records (Insecure Direct Object Reference). This occurs due to unlocked public model properties and a complete lack of access control checks on mutating sub-form store methods.
Shopper is an open-source headless e-commerce administration panel built on Laravel, Livewire, and Filament. Prior to version 2.8.0, the admin tables for PaymentMethods, Currencies, and Carriers exposed inline toggles and per-record actions that could be modified by any authenticated user without verifying the corresponding administrative permissions on the backend.
An Insecure Direct Object Reference (IDOR) vulnerability in Bugsink (versions < 2.2.0) allows authenticated users with access to at least one project to view sensitive event details (including stack traces, local/environment variables, and execution breadcrumbs) belonging to other projects, by supplying a known event UUID directly to the issue event URL paths.
Bugsink prior to version 2.2.0 is vulnerable to Broken Object Level Authorization (BOLA). The issue list view authorizes access based on the project in the URL path but applies requested bulk actions to submitted issue UUIDs globally, without verifying project ownership.
A critical authorization bypass vulnerability in Bugsink prior to version 2.2.0 allows authenticated users to access and resolve sourcemaps and debug files belonging to other projects on the same instance.