audit(docs/28): Git 同步审计二轮——修复选项注入校验/默认 .gitignore/设置弹窗陈旧守卫,4 低项记录待办

This commit is contained in:
Muse
2026-11-14 17:40:09 +08:00
parent fadee6aacf
commit 527ca5ecc5
4 changed files with 126 additions and 6 deletions

View File

@@ -649,6 +649,38 @@ pub struct GitSyncOutcome {
pub summary: String,
}
/// 分支名校验(简化版 check-ref-format):用户输入会作为 git 命令参数,
/// 以 - 开头会被解析成选项(选项注入),其余非法形态 git 自己也会拒绝。
fn validate_branch(branch: &str) -> Result<String, String> {
let b = branch.trim();
if b.is_empty() {
return Ok("main".to_string());
}
let bad = b.len() > 100
|| b.starts_with('-')
|| b.starts_with('.')
|| b.ends_with('/')
|| b.ends_with(".lock")
|| b.contains("..")
|| b.chars().any(|c| c.is_whitespace() || c.is_control() || "~^:?*[\\]".contains(c));
if bad {
return Err(format!("分支名不合法:{b}"));
}
Ok(b.to_string())
}
/// 远程地址校验:不得以 - 开头(选项注入)、不得含空白/控制字符。
fn validate_remote_url(url: &str) -> Result<String, String> {
let u = url.trim();
if u.is_empty() {
return Ok(String::new());
}
if u.len() > 2000 || u.starts_with('-') || u.chars().any(|c| c.is_whitespace() || c.is_control()) {
return Err("远程仓库地址不合法".to_string());
}
Ok(u.to_string())
}
fn run_git(dir: &Path, args: &[&str]) -> Result<String, String> {
let mut cmd = std::process::Command::new("git");
cmd.args(args)
@@ -738,10 +770,20 @@ pub async fn git_status(root: String) -> GitStatusInfo {
pub async fn git_init_repo(root: String, remote_url: String, branch: String) -> Result<GitStatusInfo, String> {
tokio::task::spawn_blocking(move || {
let dir = Path::new(&root);
let b = if branch.trim().is_empty() { "main" } else { branch.trim() };
run_git(dir, &["init", "-b", b])?;
if !remote_url.trim().is_empty() {
run_git(dir, &["remote", "add", "origin", remote_url.trim()])?;
let b = validate_branch(&branch)?;
let url = validate_remote_url(&remote_url)?;
run_git(dir, &["init", "-b", &b])?;
if !url.is_empty() {
run_git(dir, &["remote", "add", "origin", &url])?;
}
// 同步会提交文件夹内全部未忽略文件:新建仓库时种一份保守的
// .gitignore(系统垃圾、临时文件、.env),避免敏感/无用文件被推走
let gitignore = dir.join(".gitignore");
if !gitignore.exists() {
let _ = std::fs::write(
&gitignore,
".DS_Store\nThumbs.db\ndesktop.ini\n*.tmp\n~$*\n.env\n.env.*\n",
);
}
Ok(git_status_inner(&root))
})
@@ -753,7 +795,8 @@ pub async fn git_init_repo(root: String, remote_url: String, branch: String) ->
pub async fn git_set_remote(root: String, remote_url: String) -> Result<GitStatusInfo, String> {
tokio::task::spawn_blocking(move || {
let dir = Path::new(&root);
let url = remote_url.trim();
let url_owned = validate_remote_url(&remote_url)?;
let url = url_owned.as_str();
let has_origin = run_git(dir, &["remote"]).map(|r| r.lines().any(|l| l.trim() == "origin")).unwrap_or(false);
if url.is_empty() {
if has_origin {