refactor(bare): enhance security and performance optimizations

- Remove unnecessary sorting in advertise_refs for deterministic output
- Add path traversal detection and validation in bare_dir construction
- Implement symlink resolution checks to prevent security vulnerabilities
- Refactor cache system with CRC validation and improved metrics
- Integrate repo-specific cache invalidation using indexed keys
- Add comprehensive unit tests for commit operations and diff functionality
- Move configuration constants to centralized config module
- Optimize string operations in disk cache random value generation
- Enhance license detection algorithm with cleaner matching logic
- Streamline argument processing in various git operations
- Update dependencies including crc32fast and flate2 for performance
- Add signal handling capability to tokio runtime configuration
This commit is contained in:
zhenyi
2026-06-12 15:04:12 +08:00
parent e386f44ee2
commit 10a4398e81
41 changed files with 1373 additions and 365 deletions
+32 -12
View File
@@ -25,7 +25,6 @@ const FORBIDDEN_PATTERNS: &[&str] = &[
"init 6",
"poweroff",
"halt",
// Additional patterns to catch encoding/obfuscation attempts
"eval ", // eval can execute arbitrary strings
"exec ", // exec can replace process
"$(", // command substitution
@@ -55,8 +54,21 @@ const DANGEROUS_PREFIXES: &[&str] = &[
"rm -rf *", // rm -rf with wildcard
];
/// Maximum hook script size (64KB).
const MAX_HOOK_SIZE: usize = 65536;
/// Pairs of commands that indicate data exfiltration or code execution.
const DANGEROUS_COMMAND_PAIRS: &[(&str, &str)] = &[
("curl", "bash"),
("curl", "sh"),
("wget", "bash"),
("wget", "sh"),
("nc", "-e"),
("ncat", "-e"),
("python", "-c"),
("perl", "-e"),
("ruby", "-e"),
("node", "-e"),
];
use crate::config::MAX_HOOK_SCRIPT_SIZE;
/// Validate a custom hook script content for safety.
pub fn validate_hook_content(content: &str) -> GitResult<()> {
@@ -65,10 +77,10 @@ pub fn validate_hook_content(content: &str) -> GitResult<()> {
"hook content cannot be empty".into(),
));
}
if content.len() > MAX_HOOK_SIZE {
if content.len() > MAX_HOOK_SCRIPT_SIZE {
return Err(GitError::InvalidArgument(format!(
"hook content too large (max {} bytes): {} bytes",
MAX_HOOK_SIZE,
MAX_HOOK_SCRIPT_SIZE,
content.len()
)));
}
@@ -78,7 +90,6 @@ pub fn validate_hook_content(content: &str) -> GitResult<()> {
));
}
// Check for forbidden patterns (case-insensitive where appropriate)
let content_lower = content.to_lowercase();
for pattern in FORBIDDEN_PATTERNS {
if content_lower.contains(&pattern.to_lowercase()) {
@@ -88,7 +99,6 @@ pub fn validate_hook_content(content: &str) -> GitResult<()> {
}
}
// Check for dangerous prefixes (exact case)
for prefix in DANGEROUS_PREFIXES {
if content.contains(prefix) {
return Err(GitError::InvalidArgument(format!(
@@ -97,15 +107,28 @@ pub fn validate_hook_content(content: &str) -> GitResult<()> {
}
}
// Check for obfuscation techniques
check_obfuscation_attempts(content)?;
check_dangerous_pairs(content)?;
Ok(())
}
/// Check for dangerous command pairs that indicate data exfiltration or code execution.
fn check_dangerous_pairs(content: &str) -> GitResult<()> {
let content_lower = content.to_lowercase();
for &(cmd1, cmd2) in DANGEROUS_COMMAND_PAIRS {
if content_lower.contains(cmd1) && content_lower.contains(cmd2) {
return Err(GitError::InvalidArgument(format!(
"hook contains dangerous command combination: '{cmd1}' + '{cmd2}' (possible data exfiltration)"
)));
}
}
Ok(())
}
/// Check for common obfuscation attempts.
fn check_obfuscation_attempts(content: &str) -> GitResult<()> {
// Check for excessive use of special characters that might indicate obfuscation
let special_char_count = content
.chars()
.filter(|c| {
@@ -117,14 +140,12 @@ fn check_obfuscation_attempts(content: &str) -> GitResult<()> {
.count();
let total_chars = content.chars().count();
// If more than 30% of content is special characters, it's suspicious
if total_chars > 0 && (special_char_count * 100 / total_chars) > 30 {
return Err(GitError::InvalidArgument(
"hook content appears obfuscated (too many special characters)".into(),
));
}
// Check for hex encoding attempts (e.g., \x41\x42)
if content.contains("\\x") {
let hex_count = content.matches("\\x").count();
if hex_count > 5 {
@@ -134,7 +155,6 @@ fn check_obfuscation_attempts(content: &str) -> GitResult<()> {
}
}
// Check for unicode escape sequences
if content.contains("\\u") {
let unicode_count = content.matches("\\u").count();
if unicode_count > 5 {