Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 54 additions & 15 deletions sentinelops-backend/app/services/local_git_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import logging
import os
import subprocess
import tempfile
from datetime import datetime
from typing import Any, Dict, List, NewType, Optional

Expand Down Expand Up @@ -157,21 +158,46 @@ def _run_git(self, repo_path: str, args: List[str]) -> str:
return ""

args_key = tuple(args)
is_commit_with_message = len(args) == 3 and args[0] == "commit" and args[1] == "-m"
if args_key not in self._ALLOWED_GIT_ARGS and not is_commit_with_message:
is_commit_with_file = (
len(args) == 3 and args[0] == "commit" and args[1] == "--file"
)
if args_key not in self._ALLOWED_GIT_ARGS and not is_commit_with_file:
logger.warning(f"Blocked git cmd with disallowed args in {repo_path_str}: {args}")
return ""
if is_commit_with_message:
safe_commit_message = self._sanitize_commit_message(args[2])
if not safe_commit_message or safe_commit_message != args[2]:

cmd: List[str]
if args_key == ("status", "--short"):
cmd = ["git", "-C", repo_path_str, "status", "--short"]
elif args_key == ("diff", "--cached", "--name-only"):
cmd = ["git", "-C", repo_path_str, "diff", "--cached", "--name-only"]
elif args_key == ("diff", "--name-only"):
cmd = ["git", "-C", repo_path_str, "diff", "--name-only"]
elif args_key == ("add", "."):
cmd = ["git", "-C", repo_path_str, "add", "."]
elif args_key == ("push",):
cmd = ["git", "-C", repo_path_str, "push"]
elif is_commit_with_file:
message_file = args[2]
if (
not isinstance(message_file, str)
or not message_file
or message_file.startswith("-")
or "\x00" in message_file
or not os.path.isabs(message_file)
or not os.path.isfile(message_file)
):
logger.warning(
f"Blocked git cmd with invalid commit message in {repo_path_str}"
f"Blocked git cmd with invalid commit message file in {repo_path_str}"
)
return ""
cmd = ["git", "-C", repo_path_str, "commit", "--file", message_file]
else:
logger.warning(f"Blocked git cmd with disallowed args in {repo_path_str}: {args}")
return ""

try:
result = subprocess.run(
["git", "-C", repo_path_str] + args,
cmd,
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
capture_output=True,
text=True,
check=True,
Expand Down Expand Up @@ -533,14 +559,27 @@ def commit_and_push(self, repo_path: str, message: str) -> Dict[str, Any]:
# Stage everything
self.stage_all(repo_path)

# Commit
commit_out = self._run_git(repo_path, ["commit", "-m", safe_message])
if not commit_out:
return {"success": False, "error": "Nothing to commit or commit failed"}

# Push
self._run_git(repo_path, ["push"])
return {"success": True, "message": "Changes committed and pushed to GitHub"}
temp_message_file = None
try:
with tempfile.NamedTemporaryFile(mode="w", delete=False, encoding="utf-8") as tmp:
tmp.write(safe_message)
tmp.flush()
temp_message_file = tmp.name

# Commit
commit_out = self._run_git(repo_path, ["commit", "--file", temp_message_file])
if not commit_out:
return {"success": False, "error": "Nothing to commit or commit failed"}

# Push
self._run_git(repo_path, ["push"])
return {"success": True, "message": "Changes committed and pushed to GitHub"}
finally:
if temp_message_file and os.path.exists(temp_message_file):
try:
os.remove(temp_message_file)
except OSError:
logger.warning("Failed to remove temporary commit message file.")


local_git = LocalGitService()
Loading