
Local Git pre-push hook and filesystem scanner that detects likely secret leaks, redacts matched values, and blocks pushes without telemetry or network access.
Push Guard is a local Git pre-push guard for likely secret leaks.
It scans the content being pushed, reports likely secret patterns, redacts all matched values, and exits nonzero so Git blocks the push.
Built and maintained by Dragon Lady - github.com/Dragon-Lady - X: @answerislove2
Version 0.4.3 removes the inline # push-guard: ignore exception. An upgrade
can start blocking pushes and packages that previously passed. Before upgrading
shared hooks or CI installations, inspect each consuming repository:
git grep -n -i 'push-guard:[[:space:]]*ignore'
Review each match. Remove secrets and private material; neither can be exempted.
For legitimate workflow/IOC fixtures, add only the reviewed, exact repository
path and exact workflow.* rule IDs to .push-guard-allow. Inline markers are
ordinary text now, including those in tests or documentation. Do not convert
all marker lines into allowances automatically. Review any new allowance as a
security-sensitive change.
The pre-push hook reads exceptions from the destination's advertised base commit, never the working copy or candidate commits. A new or expanded allowance must be reviewed and established separately before it can exempt a later push. For a multi-ref push, only exceptions shared by every destination base apply. New refs without an advertised base receive no exceptions.
scan likewise reads policy from --base. When auditing a larger historical
range against a separately reviewed policy snapshot, explicitly pass
--allow-list-ref <reviewed-40-character-commit-SHA>. This is an explicit trust
decision by the caller; never substitute the unreviewed candidate's policy.
scan-package continues to use the caller-selected --repo policy, outside
the artifact. Supply a reviewed policy directory when scanning untrusted builds.
Run the new version's push-guard scan --base <trusted-base> --head HEAD and
push-guard scan-package dist/* against the actual outgoing changes and built
artifacts before upgrading the installed hook. Archive member paths may differ
from source paths; review those exact paths separately. Keep the existing
installed version until the affected repositories have completed this migration.
Public landing page: push-guard.netlify.app
The static project landing page lives in docs/ and has no runtime
dependencies, external scripts, analytics, or remote assets. It can be served
with GitHub Pages from the docs/ directory or deployed to Netlify using the
included netlify.toml (no build command; publish directory docs).
The current Netlify project uses manual deploys. A Git push alone does not
update the public page: publish the contents of docs/ after each release and
check the live version, social image, and response headers. The docs/_headers
file keeps the security headers when deploying that directory directly.
--send option enables aggregate
alerts to configured destinations; the Git guard remains offline.git subprocess only to read commit diffs, pushed trees,
configured hook paths, and ignore metadata. The existing hook entry point is
preserved, with raw-content and outgoing-history inspection hardened.$HOME, and executes no child
processes, including Git.$XDG_STATE_HOME/push-guard-sweep/fp.key (default
~/.local/state/push-guard-sweep/fp.key) for stable, non-guessable fingerprints.
This key is the explicit exception to “no data stored by default.” It contains
no scanned data. The state directory is 0700 and files are created 0600.--baseline and --output files contain redacted local metadata;
--send stores keyed deduplication receipts, never alert credentials or secrets.Blocking a push is Git's response to the advisory. Push Guard remains read-only
and does not mutate files. Override is available with git push --no-verify
when the matched value is known not to be a secret.
The push-guard sweep command and push-guard-sweep alias provide the same local
inspection. They require the existing package's Python 3.11 or newer and have no
additional runtime dependencies. Development tests use the optional test extra
(python -m pip install ".[test]", then PYTHONPATH=src python -m pytest -q
from the repository root). The 0.4.0 release adds Sweep. CI installs that
development extra and runs the entire pytest suite, including the legacy
unittest cases. Release tests run in a
separate job; that job never supplies distribution artifacts to publishing. The
artifact-build job does not install test dependencies. Sweep does not install
or enable a scheduler.
push-guard sweep --list-targets
push-guard sweep --roots ~/projects --class stray
push-guard-sweep --format json --output ~/sweep-report.json
push-guard-sweep --baseline ~/sweep-baseline.json
push-guard-sweep report --format html --output ~/sweep-report.html
push-guard-sweep print-timer --interval 7d
Every scan also considers the default history, dotfile, configuration and log
locations below. --roots changes the .env* and custom-include search roots;
use --exclude to narrow the other default sets. Repeated --include GLOB adds
files within the existing bounded searches, and repeated --exclude GLOB removes
paths. Globs match basenames, HOME-relative paths, or absolute paths. Excludes
win; includes cannot override safety exclusions or depth limits. ~ in CLI path
arguments is expanded. Scanned paths must remain inside $HOME.
.lesshst. The SQLite shell history text is separate from database files..bashrc, .zshrc, .profile, .bash_profile, .npmrc, and
.docker/config.json; selected JSON, TOML, YAML, INI and CONF files under
.config with at most four directory levels below it..env* files: under the selected roots (default HOME), at most six directory
levels. Dependency trees, virtual environments, Git metadata and caches are
excluded from this search..log files and files within logs/, at most four directory levels under
.local/state, .cache, .codex, .config/Cursor/logs and configured
log_dirs.Known credential stores (.git-credentials, .aws/credentials, .netrc,
.pypirc, .config/gh/hosts.yml, .config/rclone/rclone.conf, .codex/auth.json)
are metadata-only checks. Their contents are never opened. Each is reported
as expected-store, with info severity for private modes or warn severity for
execute/group/other permissions. line is null and fp_kind=path explicitly
identifies a keyed path fingerprint, not a token fingerprint. Reach Ward owns
credential inventory in these stores. This avoids inventing a secret value or
line number for a mode check. All other matches are classed stray and normally
warn; Google access tokens are low severity and marked short-lived, likely
expired. No token is checked against a provider.