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:
@@ -27,7 +27,6 @@ impl GitBare {
|
||||
format!("{base}...{head}")
|
||||
};
|
||||
|
||||
// Build base rev-list args
|
||||
let mut base_args = vec![
|
||||
"--git-dir".to_string(),
|
||||
self.bare_dir.to_string_lossy().into_owned(),
|
||||
@@ -38,10 +37,8 @@ impl GitBare {
|
||||
}
|
||||
base_args.push(range);
|
||||
|
||||
// 1. Total count
|
||||
let total = {
|
||||
let mut args = base_args.clone();
|
||||
// Insert after "rev-list" (index 2)
|
||||
args.insert(3, "--count".into());
|
||||
let result = duct::cmd("git", &args)
|
||||
.stdout_capture()
|
||||
@@ -60,7 +57,6 @@ impl GitBare {
|
||||
.unwrap_or(0)
|
||||
};
|
||||
|
||||
// 2. Git-side pagination
|
||||
let page_size = request
|
||||
.pagination
|
||||
.as_ref()
|
||||
@@ -81,7 +77,6 @@ impl GitBare {
|
||||
.min(total);
|
||||
|
||||
let mut fetch_args = base_args;
|
||||
// Insert after "rev-list" (index 2)
|
||||
fetch_args.insert(3, format!("--skip={start_offset}"));
|
||||
fetch_args.insert(4, format!("-n{page_size}"));
|
||||
|
||||
@@ -104,7 +99,6 @@ impl GitBare {
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
|
||||
// 3. Batch-read commits via gix (one repo open, no subprocess per commit)
|
||||
let mut commits = Vec::with_capacity(page_ids.len());
|
||||
for id in &page_ids {
|
||||
commits.push(read_commit_from_repo(self, &repo, id)?);
|
||||
|
||||
+12
-15
@@ -8,9 +8,7 @@ use crate::pb::{
|
||||
|
||||
impl GitBare {
|
||||
pub fn create_commit(&self, request: CreateCommitRequest) -> GitResult<CreateCommitResponse> {
|
||||
// Validate branch name to prevent command injection
|
||||
crate::sanitize::validate_ref_name(&request.branch)?;
|
||||
// Validate start_revision if provided
|
||||
if let Some(rev) = request.start_revision.as_ref() {
|
||||
match rev.selector.as_ref() {
|
||||
Some(object_selector::Selector::Revision(name)) => {
|
||||
@@ -23,11 +21,11 @@ impl GitBare {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_ACTIONS_PER_COMMIT: usize = 10_000;
|
||||
if request.actions.len() > MAX_ACTIONS_PER_COMMIT {
|
||||
if request.actions.len() > crate::config::MAX_ACTIONS_PER_COMMIT {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"too many commit actions ({} > max {MAX_ACTIONS_PER_COMMIT})",
|
||||
request.actions.len()
|
||||
"too many commit actions ({} > max {})",
|
||||
request.actions.len(),
|
||||
crate::config::MAX_ACTIONS_PER_COMMIT,
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -168,15 +166,14 @@ impl GitBare {
|
||||
index_path: &str,
|
||||
action: &crate::pb::CreateCommitAction,
|
||||
) -> GitResult<()> {
|
||||
const MAX_ACTION_CONTENT_BYTES: usize = 100 * 1024 * 1024;
|
||||
if action.content.len() > MAX_ACTION_CONTENT_BYTES {
|
||||
if action.content.len() > crate::config::MAX_ACTION_CONTENT_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"action content too large ({} bytes, max {MAX_ACTION_CONTENT_BYTES})",
|
||||
action.content.len()
|
||||
"action content too large ({} bytes, max {})",
|
||||
action.content.len(),
|
||||
crate::config::MAX_ACTION_CONTENT_BYTES,
|
||||
)));
|
||||
}
|
||||
|
||||
// Validate file paths to prevent command injection / traversal
|
||||
if !action.file_path.is_empty() {
|
||||
crate::sanitize::validate_file_path(&action.file_path)?;
|
||||
}
|
||||
@@ -341,11 +338,11 @@ impl GitBare {
|
||||
author: Option<&crate::pb::Signature>,
|
||||
committer: Option<&crate::pb::Signature>,
|
||||
) -> GitResult<String> {
|
||||
const MAX_COMMIT_MESSAGE_BYTES: usize = 10 * 1024 * 1024;
|
||||
if message.len() > MAX_COMMIT_MESSAGE_BYTES {
|
||||
if message.len() > crate::config::MAX_COMMIT_MESSAGE_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"commit message too large ({} bytes, max {MAX_COMMIT_MESSAGE_BYTES})",
|
||||
message.len()
|
||||
"commit message too large ({} bytes, max {})",
|
||||
message.len(),
|
||||
crate::config::MAX_COMMIT_MESSAGE_BYTES,
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,8 @@ impl GitBare {
|
||||
|
||||
let base_args = build_rev_list_args(self, &request, &revision)?;
|
||||
|
||||
// 1. Get total count via rev-list --count (lightweight, no object parsing)
|
||||
let total = {
|
||||
let mut args = base_args.clone();
|
||||
// Insert after "rev-list" (index 2) so it's a rev-list flag, not a git flag
|
||||
args.insert(3, "--count".into());
|
||||
let result = duct::cmd("git", &args)
|
||||
.stdout_capture()
|
||||
@@ -31,7 +29,6 @@ impl GitBare {
|
||||
.unwrap_or(0)
|
||||
};
|
||||
|
||||
// 2. Apply git-side pagination: --skip + -n to only fetch the page
|
||||
let page_size = request
|
||||
.pagination
|
||||
.as_ref()
|
||||
@@ -52,7 +49,6 @@ impl GitBare {
|
||||
.min(total);
|
||||
|
||||
let mut fetch_args = base_args;
|
||||
// Insert after "rev-list" (index 2) so they are rev-list flags, not git flags
|
||||
fetch_args.insert(3, format!("--skip={start_offset}"));
|
||||
fetch_args.insert(4, format!("-n{page_size}"));
|
||||
|
||||
@@ -75,7 +71,6 @@ impl GitBare {
|
||||
.map(ToOwned::to_owned)
|
||||
.collect();
|
||||
|
||||
// 3. Batch-read commits via gix (one repo open, zero subprocess per commit)
|
||||
let commits = if page_ids.is_empty() {
|
||||
Vec::new()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user