feat(cluster): implement distributed clustering with etcd coordination
- Integrate etcd-client for distributed coordination and leader election - Add remote client macros with proper formatting for all services - Implement RequestMetrics for tracking RPC performance and errors - Add rate limiting mechanism across all service endpoints - Create ElectionRequest and ElectionResult message types for leader election - Add role management with primary/replica switching capabilities - Implement health checker with automatic failover detection - Add repository count metrics for cluster monitoring - Update Cargo.toml with etcd-client and dashmap dependencies - Modify RepoEntry to include read_only flag for replica handling - Implement should_accept_election logic to prevent duplicate elections - Add RoleChangedEvent handling for cluster role updates
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
//! Hook content sanitization.
|
||||
//!
|
||||
//! Validates custom hook scripts to prevent dangerous commands.
|
||||
|
||||
use crate::error::{GitError, GitResult};
|
||||
|
||||
/// Commands/patterns that are never allowed in custom hook scripts.
|
||||
const FORBIDDEN_PATTERNS: &[&str] = &[
|
||||
"rm -rf",
|
||||
"rm -r /",
|
||||
"chmod 777",
|
||||
"chmod 666",
|
||||
"mkfs",
|
||||
"dd if=",
|
||||
":(){ :|:& };:", // fork bomb
|
||||
"> /dev/sda",
|
||||
"curl -o /",
|
||||
"wget -O /",
|
||||
"/etc/passwd",
|
||||
"/etc/shadow",
|
||||
"shutdown",
|
||||
"reboot",
|
||||
"init 0",
|
||||
"init 6",
|
||||
"poweroff",
|
||||
"halt",
|
||||
];
|
||||
|
||||
/// Maximum hook script size (64KB).
|
||||
const MAX_HOOK_SIZE: usize = 65536;
|
||||
|
||||
/// Validate a custom hook script content for safety.
|
||||
pub fn validate_hook_content(content: &str) -> GitResult<()> {
|
||||
if content.is_empty() {
|
||||
return Err(GitError::InvalidArgument(
|
||||
"hook content cannot be empty".into(),
|
||||
));
|
||||
}
|
||||
if content.len() > MAX_HOOK_SIZE {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"hook content too large (max {} bytes): {} bytes",
|
||||
MAX_HOOK_SIZE,
|
||||
content.len()
|
||||
)));
|
||||
}
|
||||
let content_lower = content.to_lowercase();
|
||||
for pattern in FORBIDDEN_PATTERNS {
|
||||
if content_lower.contains(pattern) {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"hook content contains forbidden pattern: '{pattern}'"
|
||||
)));
|
||||
}
|
||||
}
|
||||
if content.contains('\0') {
|
||||
return Err(GitError::InvalidArgument(
|
||||
"hook content cannot contain null bytes".into(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate a hook name (must be a recognized git hook name).
|
||||
pub fn validate_hook_name(name: &str) -> GitResult<()> {
|
||||
const VALID_HOOK_NAMES: &[&str] = &[
|
||||
"pre-receive",
|
||||
"update",
|
||||
"post-receive",
|
||||
"pre-applypatch",
|
||||
"applypatch-msg",
|
||||
"post-applypatch",
|
||||
"pre-commit",
|
||||
"prepare-commit-msg",
|
||||
"commit-msg",
|
||||
"post-commit",
|
||||
"pre-auto-gc",
|
||||
];
|
||||
if !VALID_HOOK_NAMES.contains(&name) {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"invalid hook name: '{name}'. Must be one of: {}",
|
||||
VALID_HOOK_NAMES.join(", ")
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user