audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

v1.0.0: tui/tests/test_gitlab_runner.py · raw

  1"""Tests for the TUI's GitLab audit orchestration.
  2
  3Stub the network-bound collectors and verify run_audit's wiring: base vs
  4project-scoped dispatch, the shared project cache, per-check error handling,
  5and that the CSV package (per-check files + summary) is written.
  6"""
  7
  8import csv
  9import os
 10
 11import pytest
 12
 13from tui import gitlab_runner as r
 14
 15
 16@pytest.fixture
 17def fake_checks(monkeypatch):
 18    calls = {}
 19
 20    def base_fn(group, cfg):
 21        calls["base"] = (group, cfg)
 22        return [{"username": "alice"}, {"username": "bob"}]
 23
 24    def projects_fn(group, cfg, projects):
 25        calls["projects"] = (group, cfg, projects)
 26        return [{"project": p["path_with_namespace"]} for p in projects]
 27
 28    def boom_fn(group, cfg):
 29        raise RuntimeError("kaboom")
 30
 31    checks = [
 32        r.Check("base", "Base", base_fn, "base.csv"),
 33        r.Check("projects", "Projects", projects_fn, "projects.csv", arg="projects"),
 34        r.Check("boom", "Boom", boom_fn, "boom.csv"),
 35    ]
 36    monkeypatch.setattr(r, "CHECKS", checks)
 37
 38    fetch_calls = []
 39
 40    def fake_fetch(group, cfg):
 41        fetch_calls.append(group)
 42        return [{"id": 1, "path_with_namespace": "grp/proj"}]
 43
 44    monkeypatch.setattr(r.projects, "fetch_projects", fake_fetch)
 45
 46    return calls, fetch_calls
 47
 48
 49def run(tmp_path, keys, base_url="https://gitlab.com/api/v4"):
 50    events = []
 51    sections = r.run_audit(
 52        group="grp",
 53        token="tok",
 54        base_url=base_url,
 55        output_dir=str(tmp_path),
 56        selected_keys=keys,
 57        on_event=events.append,
 58    )
 59    return events, sections
 60
 61
 62def test_argument_dispatch_and_files(tmp_path, fake_checks):
 63    calls, _ = fake_checks
 64    run(tmp_path, ["base", "projects"])
 65
 66    assert calls["base"][0] == "grp"
 67    assert calls["projects"][2] == [{"id": 1, "path_with_namespace": "grp/proj"}]
 68
 69    for name in ("base.csv", "projects.csv", "summary.txt"):
 70        assert os.path.exists(tmp_path / name), name
 71
 72    with open(tmp_path / "projects.csv", newline="") as f:
 73        rows = list(csv.DictReader(f))
 74    assert rows == [{"project": "grp/proj"}]
 75
 76
 77def test_project_cache_fetched_once(tmp_path, fake_checks):
 78    _, fetch_calls = fake_checks
 79    run(tmp_path, ["projects", "base"])
 80    assert fetch_calls == ["grp"]
 81
 82
 83def test_project_cache_skipped_when_not_needed(tmp_path, fake_checks):
 84    _, fetch_calls = fake_checks
 85    run(tmp_path, ["base"])
 86    assert fetch_calls == []
 87
 88
 89def test_failing_check_does_not_abort_run(tmp_path, fake_checks):
 90    events, sections = run(tmp_path, ["boom", "base"])
 91
 92    kinds = [(e.kind, e.label) for e in events]
 93    assert ("error", "Boom") in kinds
 94    assert ("done", "Base") in kinds
 95
 96    labels = dict(sections)
 97    assert labels["Boom"] == 0
 98    assert labels["Base"] == 2
 99
100
101def test_base_url_reaches_cfg(tmp_path, fake_checks):
102    calls, _ = fake_checks
103    run(tmp_path, ["base"], base_url="https://gitlab.example.com/api/v4/")
104    # build_cfg strips the trailing slash.
105    assert calls["base"][1]["base_url"] == "https://gitlab.example.com/api/v4"
106    assert calls["base"][1]["headers"]["PRIVATE-TOKEN"] == "tok"
107
108
109def test_premium_checks_off_by_default():
110    assert "approval_rules" not in r.DEFAULT_SELECTION
111    assert "audit_events" not in r.DEFAULT_SELECTION
112    assert "password_policy" not in r.DEFAULT_SELECTION
113    assert "group_members" in r.DEFAULT_SELECTION