mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(pre-push-safety-net): preserve existing hooks during explicit integration
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
"""Real local Git pushes; never install in the project being developed."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from test_push_runner import history
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prior_exit", [0, 7])
|
||||
def test_existing_hook_stdin_remote_exit_and_removal(tmp_path, prior_exit):
|
||||
root, base, head, git = history(tmp_path)
|
||||
remote = tmp_path / "remote.git"
|
||||
subprocess.run(["git", "init", "--bare", str(remote)], check=True, capture_output=True)
|
||||
git("remote", "add", "origin", str(remote))
|
||||
hooks = root / ".custom-hooks"
|
||||
hooks.mkdir()
|
||||
git("config", "core.hooksPath", ".custom-hooks")
|
||||
hook = hooks / "pre-push"
|
||||
prior = hooks / "pre-push.before-ousast"
|
||||
log = tmp_path / "prior-input"
|
||||
args = tmp_path / "prior-args"
|
||||
prior.write_text(f'#!/bin/sh\nprintf "%s\\n" "$@" > "{args}"\ncat > "{log}"\nexit {prior_exit}\n')
|
||||
prior.chmod(0o755)
|
||||
shutil.copyfile(Path(__file__).resolve().parents[1] / "ops/pre-push", hook)
|
||||
hook.chmod(0o755)
|
||||
before = (root / ".git/index").read_bytes(), (root / "api.js").read_bytes(), git("rev-parse", "HEAD")
|
||||
env = {
|
||||
**os.environ,
|
||||
"PATH": str(Path(".venv/bin").resolve()) + os.pathsep + os.environ["PATH"],
|
||||
"OUSAST_ARTIFACT_DIR": str(tmp_path / "artifacts"),
|
||||
"OUSAST_PUSH_DEADLINE": "0.1",
|
||||
}
|
||||
result = subprocess.run(["git", "-C", str(root), "push", "origin", "HEAD:refs/heads/test"], env=env, capture_output=True)
|
||||
assert (result.returncode == 0) == (prior_exit == 0), result.stderr
|
||||
assert log.read_text() == f"HEAD {head} refs/heads/test {'0' * 40}\n"
|
||||
assert args.read_text().splitlines() == ["origin", str(remote)]
|
||||
assert ((root / ".git/index").read_bytes(), (root / "api.js").read_bytes(), git("rev-parse", "HEAD")) == before
|
||||
assert git("config", "core.hooksPath") == ".custom-hooks"
|
||||
assert list((tmp_path / "artifacts").glob("*.json"))
|
||||
# Explicit removal restores the prior hook byte-for-byte, without config/source/ref edits.
|
||||
original = prior.read_bytes()
|
||||
hook.unlink()
|
||||
prior.rename(hook)
|
||||
assert hook.read_bytes() == original
|
||||
result = subprocess.run(["git", "-C", str(root), "push", "origin", "HEAD:refs/heads/after-removal"], env=env, capture_output=True)
|
||||
assert (result.returncode == 0) == (prior_exit == 0)
|
||||
assert "refs/heads/after-removal" in log.read_text()
|
||||
|
||||
|
||||
def test_install_refuses_existing_and_honors_hooks_path(tmp_path):
|
||||
root, _, _, git = history(tmp_path)
|
||||
git("config", "core.hooksPath", ".custom-hooks")
|
||||
installer = Path(__file__).resolve().parents[1] / "ops/install-pre-push"
|
||||
first = subprocess.run([str(installer), str(root)], capture_output=True)
|
||||
assert first.returncode == 0, first.stderr
|
||||
hook = root / ".custom-hooks/pre-push"
|
||||
hook.write_text("#!/bin/sh\nexit 23\n")
|
||||
before = hook.read_bytes()
|
||||
second = subprocess.run([str(installer), str(root)], capture_output=True)
|
||||
assert second.returncode != 0
|
||||
assert hook.read_bytes() == before
|
||||
assert git("config", "core.hooksPath") == ".custom-hooks"
|
||||
|
||||
|
||||
def test_chain_preserves_exact_nonzero_even_for_advisory(tmp_path):
|
||||
import sys
|
||||
|
||||
root, _, _, _ = history(tmp_path)
|
||||
prior = tmp_path / "prior"
|
||||
prior.write_text("#!/bin/sh\ncat >/dev/null\nexit 23\n")
|
||||
prior.chmod(0o755)
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"openultrasast.cli",
|
||||
"pre-push",
|
||||
str(root),
|
||||
"--artifact",
|
||||
str(tmp_path / "result.json"),
|
||||
"--prior-hook",
|
||||
str(prior),
|
||||
"--remote",
|
||||
"origin",
|
||||
"/tmp/remote",
|
||||
],
|
||||
input=b"",
|
||||
capture_output=True,
|
||||
timeout=10,
|
||||
)
|
||||
assert result.returncode == 23
|
||||
|
||||
|
||||
def test_install_refuses_dangling_symlink(tmp_path):
|
||||
root, _, _, git = history(tmp_path)
|
||||
git("config", "core.hooksPath", ".custom-hooks")
|
||||
hooks = root / ".custom-hooks"
|
||||
hooks.mkdir()
|
||||
target = tmp_path / "must-not-create"
|
||||
(hooks / "pre-push").symlink_to(target)
|
||||
installer = Path(__file__).resolve().parents[1] / "ops/install-pre-push"
|
||||
result = subprocess.run([str(installer), str(root)], capture_output=True)
|
||||
assert result.returncode != 0
|
||||
assert not target.exists()
|
||||
Reference in New Issue
Block a user