gitward is a command-line git repository scanner built for OSINT, forensics, and supply-chain audits. Point it at any local repo and it pulls out everything buried in the history: secrets, author identities, IP addresses, suspicious hooks, dangling commits, timezone shifts, and more. Then it scores the repo's overall risk.
It runs entirely through git subprocesses. No libraries, no runtime dependencies, no network calls. Just a single binary and a copy of git.
- Why gitward
- What It Finds
- Risk Scoring
- Installation
- Usage
- Commands
- Output Format
- Secret Patterns
- Suspicious Patterns
- Contributing
- License
Git history is permanent and honest. Developers rotate secrets in the working tree but forget the commit from three months ago that introduced them. Authors use personal emails on work machines. Internal hostnames leak through CI configs. Hooks get added without anyone reviewing them.
gitward looks at the whole picture: every commit across every ref, the reflog, stashes, notes, submodules, unreachable blobs, and hook scripts. Then it surfaces what actually matters.
It's useful for:
- Red teams auditing a target's leaked or public repositories
- Blue teams running post-incident forensics on internal repos
- Security engineers adding a pre-push or CI gate to catch secrets before they leave
- Developers who want to know what they've actually committed over the lifetime of a project
gitward collects 15 clue types:
| Type | Description |
|---|---|
email |
Author, committer, config, and Signed-off-by emails across all commits |
username |
Extracted from emails, .git/config, GPG keys, and Signed-off-by trailers |
author |
Unique author and committer names from the full log |
commit |
Every reachable commit hash with subject and timestamp |
refname |
Branches, remotes, tags, reflog entries, and notes |
tag |
Annotated and lightweight tags |
stash |
All stash entries |
url |
Remote URLs, config URLs, and URLs found in diff content |
domain |
Domains extracted from URLs, plus internal .corp/.local/.internal hostnames |
ip |
IPv4 and IPv6 addresses found anywhere in the diff history |
filepath |
Tracked files, with alerts on sensitive filenames like .env, id_rsa, .npmrc |
secret |
Credential patterns matched against 30+ regex rules (see below) |
suspicious |
Dangerous shell patterns, force-push indicators, dangling commits, hook risk, odd-hour commits, timezone shifts, high-churn deletions, Docker directives, VS Code autorun config |
relation |
Submodule paths and GPG-signed commit metadata |
timestamp |
Commit timestamps, used for behavioral analysis |
Beyond collection, gitward also inspects:
- Git hooks in
.git/hooks,.githooks, and.husky: each one is scored for network calls,base64 -d, pipe-to-shell patterns, and other signs of malicious intent - CI configs: flags
.github/workflows,.gitlab-ci.yml,.travis.yml, andJenkinsfile - Dangling blobs: runs
git fsckand scans unreachable objects for credential patterns that a normalgit logwould never show - VS Code workspace config: catches
runOn: folderOpenandallowAutomaticTaskssettings that execute code the moment a repo is opened - Timezone shifts: flags when a named author commits from multiple timezones across the history
- Odd-hour commits: flags commits made between midnight and 6 AM local time
Every scan produces a numeric risk score:
| Score | Meaning |
|---|---|
| 0–29 | Low risk |
| 30–99 | Worth reviewing |
| 100+ | High risk, take action |
Scores are additive. Highlights:
- Secret in a reachable commit: +20
- Secret in an unreachable blob (deleted, thought to be gone): +80
- Force-push indicator in reflog: +30
- Dangling commit: +10
- Hook with score ≥ 50 (pipe-to-shell,
base64 -d, network calls): +100 - Hook with score ≥ 30: +30
- High-churn deletion commit: +15
- File deleted then re-added: +20
- Odd-hour commits accumulate per commit
- Three or more timezone shifts for the same author: +10
Requirements: a C99 compiler, make, and git in $PATH.
git clone https://github.com/tinyopsec/gitward
cd gitward
make
sudo make clean installOr build without installing:
make
./gitward helpTo build with explicit flags:
cc -std=c99 -Wall -Wextra -pedantic -O2 -o gitward main.c scan.c clue.cNo external libraries needed. The binary links only against the C standard library.
gitward <command> [args]
All commands accept either the full name or a single-letter alias.
gitward scan [path] scan a local git repository (default: .)
gitward inspect [path] scan and print full report
gitward search <path> <query> search clues by keyword
gitward save <path> [outfile] save results to file
gitward version print version
gitward help print help
aliases: s=scan i=inspect f=search o=save
Quick examples:
# Scan the current directory
gitward scan
# Scan a specific repo
gitward scan /path/to/repo
# Search for a domain or string
gitward search /path/to/repo corp.internal
# Save results as plain text
gitward save /path/to/repo results.txt
# Save results as JSON (detected by .json extension)
gitward save /path/to/repo results.jsonScans the repository at path (defaults to .) and prints the full inspection report to stdout. Runs all collectors: commits, authors, refs, tags, stashes, reflog, notes, files, remotes, git config, URLs, IPs, internal domains, submodules, GPG signatures, hooks, VS Code config, CI configs, Docker directives, dangling commits and blobs, timezone anomalies, odd-hour commits, churn analysis, signed-off-by trailers, and force-push indicators.
Same as scan. Both commands scan the repo and print the report; inspect exists for cases where you want the intent to read explicitly in a script.
Scans the repository and then filters all collected clues against query (case-insensitive substring match against value, author, path, and extra fields). Useful for pivoting: find every clue touching a specific email, hostname, or commit hash.
gitward search . alice@example.com
gitward search /repos/target sk_live_Scans the repository and writes results to outfile (defaults to gitward.out). If outfile ends in .json, results are written in JSON format with full field metadata. Otherwise gitward uses a flat key=value text format, suitable for grep pipelines.
The JSON output includes: gitward_version, repo, scanned (UTC timestamp), risk_score, clue_count, and a clues array with type, value, commit, author, path, extra, and utc per entry.
The terminal report uses color when stdout is a TTY (green for clean, yellow for warnings, red for secrets and high risk). Sections:
── REPOSITORY ─────────────────────────────────────────────
path: /path/to/repo
scanned: 2025-04-12T14:23:01Z
git-size: 42M
pack-size: 38M
commits: 1847
authors: 12
refs: 34
...
secrets: 2
suspicious: 7
risk-score: 145
── SECRETS ─────────────────────────────────────────────────
[CRIT] AKIA... commit=a3f9c1 path=config/deploy.sh utc=2024-11-03T02:14:00Z
── SUSPICIOUS ──────────────────────────────────────────────
[WARN] hook:.git/hooks/post-checkout score=75
[WARN] force-push-indicator:refs/heads/main reset: moving...
gitward matches 30+ patterns across current working files and unreachable history:
- AWS access keys (
AKIA…,ASIA…) - GitHub tokens (
ghp_,ghs_,github_pat_, classic fine-grained) - GitLab tokens (
glpat-) - Slack tokens (
xox[baprs]-) - Private keys (RSA, EC, DSA, OpenSSH, PGP)
- Google API keys (
AIza…) and OAuth tokens (ya29.) - JWTs (
eyJ…) - MongoDB connection strings
- Stripe live keys (
sk_live_,rk_live_) - SendGrid API keys (
SG.) - Twilio credentials (
AC…,SK…) - Square tokens (
sq0atp-,sq0csp-) - Generic 64-character hex strings
- Password/secret/token/API key assignment patterns (single- and double-quoted)
- Database password environment variables
- NPM tokens
- Docker and Heroku environment variable assignments
- AWS metadata endpoint (
169.254.169.254)
Beyond credential patterns, gitward flags shell and behavioral indicators:
curl … | sh,wget … | shbase64 -dorbase64 --decodeeval $(base64 …)- Access to
/etc/passwd,/etc/shadow,/etc/cron chmod 777/chmod 666rm -rf /.onionaddresses- VS Code
runOn: folderOpen/allowAutomaticTasks
Hook scripts get scored separately with a finer-grained model that looks at curl, wget, base64, /dev/tcp, nc, netcat, bash -i, python -c, perl -e, ruby -e, ncat, socat, exec, pipe-to-shell, and comment density.
Bug reports and patches are welcome. Keep changes to plain C99, POSIX-compatible, and dependency-free. Run make before submitting; the build should produce zero warnings with -Wall -Wextra -pedantic.
