feat(git): add size limits for git operations
- Added MAX_CHERRY_PICK_PATCH_BYTES limit of 100MB for cherry-pick operations - Added MAX_ACTION_CONTENT_BYTES limit of 100MB for commit action content - Added MAX_COMMIT_MESSAGE_BYTES limit of 10MB for commit messages - Added MAX_CHECK_REVISIONS limit of 10,000 for revision checks - Added MAX_REBASE_COMMITS limit of 10,000 for rebase operations - Added MAX_REBASE_PATCH_BYTES limit of 100MB for rebase patches - Added MAX_RESOLUTION_CONTENT_BYTES limit of 100MB for merge conflict resolutions - Added MAX_REVERT_PATCH_BYTES limit of 100MB for revert operations - Return InvalidArgument error when size limits are exceeded with descriptive messages
This commit is contained in:
@@ -3,6 +3,8 @@ use crate::commit::create_commit::command_ok;
|
||||
use crate::error::{GitError, GitResult};
|
||||
use crate::pb::{CherryPickCommitRequest, CreateCommitResponse, GetCommitRequest};
|
||||
|
||||
const MAX_CHERRY_PICK_PATCH_BYTES: usize = 100 * 1024 * 1024;
|
||||
|
||||
impl GitBare {
|
||||
pub fn cherry_pick_commit(
|
||||
&self,
|
||||
@@ -77,6 +79,12 @@ impl GitBare {
|
||||
.unchecked()
|
||||
.run()?;
|
||||
let patch_data = command_ok(diff)?;
|
||||
if patch_data.len() > MAX_CHERRY_PICK_PATCH_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"cherry-pick patch too large ({} bytes, max {MAX_CHERRY_PICK_PATCH_BYTES})",
|
||||
patch_data.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let apply = duct::cmd(
|
||||
"git",
|
||||
|
||||
@@ -23,6 +23,14 @@ impl GitBare {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_ACTIONS_PER_COMMIT: usize = 10_000;
|
||||
if request.actions.len() > MAX_ACTIONS_PER_COMMIT {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"too many commit actions ({} > max {MAX_ACTIONS_PER_COMMIT})",
|
||||
request.actions.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let repo = self.gix_repo()?;
|
||||
let branch = request.branch.clone();
|
||||
tracing::debug!(
|
||||
@@ -160,6 +168,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 {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"action content too large ({} bytes, max {MAX_ACTION_CONTENT_BYTES})",
|
||||
action.content.len()
|
||||
)));
|
||||
}
|
||||
|
||||
// Validate file paths to prevent command injection / traversal
|
||||
if !action.file_path.is_empty() {
|
||||
crate::sanitize::validate_file_path(&action.file_path)?;
|
||||
@@ -325,6 +341,14 @@ 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 {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"commit message too large ({} bytes, max {MAX_COMMIT_MESSAGE_BYTES})",
|
||||
message.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut args = vec![
|
||||
"--git-dir".to_string(),
|
||||
self.bare_dir.to_string_lossy().into_owned(),
|
||||
|
||||
@@ -83,6 +83,13 @@ impl GitBare {
|
||||
&self,
|
||||
request: CheckObjectsExistRequest,
|
||||
) -> GitResult<CheckObjectsExistResponse> {
|
||||
const MAX_CHECK_REVISIONS: usize = 10_000;
|
||||
if request.revisions.len() > MAX_CHECK_REVISIONS {
|
||||
return Err(crate::error::GitError::InvalidArgument(format!(
|
||||
"too many revisions (max {MAX_CHECK_REVISIONS})"
|
||||
)));
|
||||
}
|
||||
|
||||
let repo = self.gix_repo()?;
|
||||
let mut revisions = Vec::new();
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::commit::create_commit::command_ok;
|
||||
use crate::error::{GitError, GitResult};
|
||||
use crate::pb::{CreateCommitResponse, GetCommitRequest, RevertCommitRequest};
|
||||
|
||||
const MAX_REVERT_PATCH_BYTES: usize = 100 * 1024 * 1024;
|
||||
|
||||
impl GitBare {
|
||||
pub fn revert_commit(&self, request: RevertCommitRequest) -> GitResult<CreateCommitResponse> {
|
||||
let target_branch = request.branch.clone();
|
||||
@@ -79,6 +81,12 @@ impl GitBare {
|
||||
.unchecked()
|
||||
.run()?;
|
||||
let patch_data = command_ok(diff)?;
|
||||
if patch_data.len() > MAX_REVERT_PATCH_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"revert patch too large ({} bytes, max {MAX_REVERT_PATCH_BYTES})",
|
||||
patch_data.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let apply = duct::cmd(
|
||||
"git",
|
||||
|
||||
@@ -3,6 +3,9 @@ use crate::commit::create_commit::command_ok;
|
||||
use crate::error::{GitError, GitResult};
|
||||
use crate::pb::{RebaseRequest, RebaseResult, rebase_result};
|
||||
|
||||
const MAX_REBASE_COMMITS: usize = 10_000;
|
||||
const MAX_REBASE_PATCH_BYTES: usize = 100 * 1024 * 1024;
|
||||
|
||||
impl GitBare {
|
||||
pub fn rebase(&self, request: RebaseRequest) -> GitResult<RebaseResult> {
|
||||
let branch = request.branch.clone();
|
||||
@@ -68,6 +71,13 @@ impl GitBare {
|
||||
.map(String::from)
|
||||
.collect();
|
||||
|
||||
if commits.len() > MAX_REBASE_COMMITS {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"too many commits to rebase ({} > max {MAX_REBASE_COMMITS})",
|
||||
commits.len()
|
||||
)));
|
||||
}
|
||||
|
||||
if commits.is_empty() {
|
||||
return Ok(RebaseResult {
|
||||
status: rebase_result::Status::RebaseResultStatusAlreadyUpToDate as i32,
|
||||
@@ -150,6 +160,12 @@ impl GitBare {
|
||||
.unchecked()
|
||||
.run()?;
|
||||
let patch_data = command_ok(diff)?;
|
||||
if patch_data.len() > MAX_REBASE_PATCH_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"rebase patch too large for {commit_hex} ({} bytes, max {MAX_REBASE_PATCH_BYTES})",
|
||||
patch_data.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let apply = duct::cmd(
|
||||
"git",
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::commit::create_commit::command_ok;
|
||||
use crate::error::{GitError, GitResult};
|
||||
use crate::pb::{MergeResult, ResolveMergeConflictsRequest, merge_result};
|
||||
|
||||
const MAX_RESOLUTION_CONTENT_BYTES: usize = 100 * 1024 * 1024;
|
||||
|
||||
impl GitBare {
|
||||
pub fn resolve_merge_conflicts(
|
||||
&self,
|
||||
@@ -50,8 +52,21 @@ impl GitBare {
|
||||
.run()?;
|
||||
command_ok(read_tree)?;
|
||||
|
||||
const MAX_RESOLUTIONS_COUNT: usize = 10_000;
|
||||
if request.resolutions.len() > MAX_RESOLUTIONS_COUNT {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"too many resolutions (max {MAX_RESOLUTIONS_COUNT})"
|
||||
)));
|
||||
}
|
||||
|
||||
for resolution in &request.resolutions {
|
||||
crate::sanitize::validate_file_path(&resolution.path)?;
|
||||
if resolution.content.len() > MAX_RESOLUTION_CONTENT_BYTES {
|
||||
return Err(GitError::InvalidArgument(format!(
|
||||
"resolution content too large ({} bytes, max {MAX_RESOLUTION_CONTENT_BYTES})",
|
||||
resolution.content.len()
|
||||
)));
|
||||
}
|
||||
let hash = duct::cmd(
|
||||
"git",
|
||||
["--git-dir", bare.as_str(), "hash-object", "-w", "--stdin"],
|
||||
|
||||
Reference in New Issue
Block a user