#!/usr/bin/env python3 """Exercise real HTTP pushes against a new private test repository.""" import base64 import json import os from pathlib import Path import secrets import subprocess import tempfile import time import urllib.request base = Path(__file__).resolve().parent.parent credentials = json.loads((base / 'credentials.json').read_text()) username = credentials['username'] repo = 'gitleaks-acceptance-' + str(time.time_ns()) url = 'http://127.0.0.1:3000' auth = base64.b64encode(f"{username}:{credentials['password']}".encode()).decode() request = urllib.request.Request(url + '/api/v1/user/repos', data=json.dumps({'name': repo, 'private': True, 'auto_init': False}).encode(), headers={'Authorization': 'Basic ' + auth, 'Content-Type': 'application/json'}) with urllib.request.urlopen(request) as response: assert response.status == 201 env = os.environ.copy() env.update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL='/dev/null', GIT_TERMINAL_PROMPT='0', GIT_USER=username, GIT_PASS=credentials['password']) log = [f'Repository: {url}/{username}/{repo}'] with tempfile.TemporaryDirectory(prefix='gitea-acceptance-') as directory: work = Path(directory) askpass = work / 'askpass.sh' askpass.write_text('#!/bin/sh\ncase "$1" in *Username*) printf "%s\\n" "$GIT_USER";; *) printf "%s\\n" "$GIT_PASS";; esac\n') askpass.chmod(0o700) env['GIT_ASKPASS'] = str(askpass) source = work / 'source' source.mkdir() def git(*args, success=True, rejection=False): result = subprocess.run(['git', *args], cwd=source, env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if success and result.returncode: raise RuntimeError(result.stdout) if rejection: assert result.returncode != 0, 'Secret push unexpectedly accepted' assert 'Push rejected: Gitleaks' in result.stdout, result.stdout assert 'pre-receive hook declined' in result.stdout, result.stdout if args[0] == 'push': log.append(result.stdout) return result.stdout.strip() git('init', '-b', 'main') git('config', 'user.name', 'Offline acceptance test') git('config', 'user.email', 'test@localhost.localdomain') git('remote', 'add', 'origin', f'{url}/{username}/{repo}.git') (source / 'README.md').write_text('Clean repository for Gitleaks acceptance tests.\n') git('add', '.') git('commit', '-m', 'Clean initial commit') clean = git('rev-parse', 'HEAD') git('push', 'origin', 'main') log.append('PASS: clean initial push; repository template installed hook automatically.') # A synthetic token, never issued by GitHub or usable for authentication. token = 'ghp_' + ''.join(secrets.choice('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') for _ in range(36)) (source / 'fixture.txt').write_text('github_token = "' + token + '" # gitleaks:allow\n') (source / '.gitleaks.toml').write_text('title = "Attempted repository override"\n') git('add', '.') git('commit', '-m', 'Synthetic secret fixture') dirty = git('rev-parse', 'HEAD') git('push', 'origin', 'main', success=False, rejection=True) log.append('PASS: secret rejected despite inline allow comment and repository config.') git('rm', 'fixture.txt') git('commit', '-m', 'Remove fixture from tip') git('push', 'origin', 'HEAD:refs/heads/history-test', success=False, rejection=True) log.append('PASS: secret in earlier commit rejected on a new branch.') git('tag', '-a', 'secret-tag', dirty, '-m', 'Synthetic tag test') git('push', 'origin', 'secret-tag', success=False, rejection=True) log.append('PASS: annotated tag pointing at secret history rejected.') git('push', 'origin', f'{clean}:refs/heads/atomic-clean', 'HEAD:refs/heads/atomic-secret', success=False, rejection=True) refs = git('ls-remote', 'origin') assert 'atomic-clean' not in refs and 'atomic-secret' not in refs assert 'history-test' not in refs and 'secret-tag' not in refs assert refs.split()[0] == clean, refs log.append('PASS: mixed multi-ref push rejected without updating clean ref.') git('reset', '--hard', clean) (source / 'README.md').write_text('Clean follow-up commit.\n') git('add', '.') git('commit', '-m', 'Clean follow-up') git('push', 'origin', 'main') git('push', 'origin', 'HEAD:refs/heads/delete-test') git('push', 'origin', '--delete', 'delete-test') log.append('PASS: clean follow-up, branch creation and branch deletion accepted.') assert token not in '\n'.join(log), 'Synthetic token was not redacted' log.append('PASS: scanner output redacted the synthetic token.') (base / 'evidence' / 'acceptance.log').write_text('\n'.join(log) + '\n') print('\n'.join(line for line in log if line.startswith(('Repository:', 'PASS:'))))