Sprint B.7: secret scan before commit-push (TUI + CLI)

- src/secrets.rs: scans added lines of the staged diff for GitHub/GitLab
  token shapes, AWS access keys, Discord token assignments, private key
  blocks; prefix+tail-length matching (no regex dep), matched values never
  echoed
- a hit blocks the commit and unstages (git reset); CLI override
  --allow-secrets; TUI sync flow shares the same scan
- verified: block+unstage, override commits, prose mentions don't trigger
This commit is contained in:
dualfroz committed 2026-07-06 15:47:32 +02:00
1 parent 8119cdeba7
commit c0b282335d
7 files changed
+135 -9

No files matched your search

+15 -5
View File
@@ -88,10 +88,11 @@ pub fn scan_cmd(cfg: &AppConfig, json: bool) -> Result<()> {
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn sync(
cfg: &AppConfig, guard: super::Guard,
repos: Vec<String>, all: bool, project: Option<String>,
action: &str, message: Option<String>,
action: &str, message: Option<String>, allow_secrets: bool,
) -> Result<()> {
use crate::activity::ActivityKind;
use crate::util::git::{git_run, git_run_ok};
@@ -150,10 +151,19 @@ pub fn sync(
}
"commit_push" => {
git_run(&r.path, &["add", "-A"]);
git_run(&r.path, &["commit", "-m", &msg]);
let ok = git_run_ok(&r.path, &["push"]);
if ok { log_activity(ActivityKind::Push, &r.name, format!("vulp sync (commit+push: {msg})")); }
ok
let hits = if allow_secrets { vec![] } else { crate::secrets::scan_staged(&r.path) };
if !hits.is_empty() {
eprintln!(" SECRETS in staged changes — commit blocked:");
for h in &hits { eprintln!(" {} — {}", h.file, h.pattern); }
eprintln!(" changes unstaged; re-run with --allow-secrets to override");
git_run_ok(&r.path, &["reset", "-q"]);
false
} else {
git_run(&r.path, &["commit", "-m", &msg]);
let ok = git_run_ok(&r.path, &["push"]);
if ok { log_activity(ActivityKind::Push, &r.name, format!("vulp sync (commit+push: {msg})")); }
ok
}
}
"push" => {
let ok = git_run_ok(&r.path, &["push"]);