Metadata-Version: 2.5
Name: gforge-python
Version: 0.1.2
Summary: A git firewall: a global pre-commit hook that blocks commits containing secrets (passwords, keys, tokens, .env files) across macOS, Linux, and Windows.
Project-URL: Homepage, https://github.com/mithilai/gforge-python
Project-URL: Repository, https://github.com/mithilai/gforge-python
Project-URL: Issues, https://github.com/mithilai/gforge-python/issues
Project-URL: Changelog, https://github.com/mithilai/gforge-python/releases
Project-URL: Upstream project (original), https://github.com/psspl-gaurang/gforge
Author: Gaurang Joshi, Shrey Tandel, Dwij Acharya, Mithil Maske
Maintainer-email: Mithil Maske <mithilm21@gmail.com>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: cli,core.hookspath,credentials,devsecops,git,git-hooks,githooks,global-hooks,hooks,pre-commit,secret-detection,secret-scanning,secrets,security
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: tomli>=2.0.1; (python_version < '3.11') and extra == 'dev'
Requires-Dist: twine>=5.1; extra == 'dev'
Description-Content-Type: text/markdown

# GForge (Python)

> **Governance Forge** — an engineering governance platform that helps teams forge
> consistent development standards through Git automation, quality gates, and
> developer tooling.

[![PyPI version](https://img.shields.io/pypi/v/gforge-python.svg)](https://pypi.org/project/gforge-python/)
[![Python versions](https://img.shields.io/pypi/pyversions/gforge-python.svg)](https://pypi.org/project/gforge-python/)
[![platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-informational)](#cross-platform-support)
[![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

GForge brings engineering standards to the one place every change passes through:
the commit. Its first governance capability is a **secret firewall** — a managed
global Git hook that stops credentials from ever entering your history, on every
repository, across your whole team.

> **This is the Python port of [GForge][upstream].** The idea, the product design,
> and the detection engine are the work of **Gaurang Joshi**, **Shrey Tandel**, and
> **Dwij Acharya**, who built the original together.
> See [Credits and attribution](#credits-and-attribution).

---

## Why GForge

A single leaked API key, database password, or private key in a commit can mean a
production incident, a costly rotation, and a permanent entry in Git history.
Per-project hooks drift, get skipped, or are never installed. GForge makes the
guardrail **global, automatic, and uniform** for every developer and every repo —
so the standard is enforced by default, not by discipline.

## Features

- **Install once, protected everywhere.** Configures Git's global `core.hooksPath`,
  so the firewall applies to every repository on the machine.
- **Deep, layered detection**
  - **`.env` cross-reference** — blocks any staged file that hardcodes a real value
    from your git-ignored `.env` files (the classic "pasted a token out of `.env`").
  - **Provider rules** — 25+ credential shapes: AWS, GitHub/GitLab, Google, Slack,
    Stripe, Twilio, SendGrid, npm, PyPI, OpenAI/Anthropic, PEM private keys, JWTs,
    database URLs, and more.
  - **Generic secrets** — any credential keyword assigned to a hardcoded value
    (`DB_PASSWORD=…`, `password: "…"`, `api_key = "…"`).
  - **Entropy** — high-entropy strings that carry no recognizable name.
- **Low noise by design.** References like `os.environ["DB_PASSWORD"]`,
  f-string interpolation, placeholders, and env templates (`.env.example`) are not
  flagged, so correct code keeps flowing.
- **Never leaks the secret.** Reports only file paths, line numbers, and rule names —
  the matched value is never printed.
- **Encoding-aware.** Handles UTF‑8, UTF‑16, and BOM-prefixed files (e.g. those
  written by PowerShell) so nothing slips through as "binary".
- **gitleaks turbo (optional).** If [gitleaks](https://github.com/gitleaks/gitleaks)
  is on `PATH`, GForge runs it too and merges the findings.
- **Cross-platform, zero runtime dependencies.** Standard library only.

## Requirements

- **Python** 3.9 or newer
- **Git**

## Installation

```bash
pipx install gforge-python   # or: pip install --user gforge-python
gforge install
```

The PyPI project is `gforge-python`; the command it installs is `gforge`.

`gforge install` is the one-time setup step: it writes the managed hook and points
your global `core.hooksPath` at it. From the next commit onward, changes are
scanned for secrets on every repository. Confirm anytime with:

```bash
gforge verify
```

> **Why two steps, unlike the npm original?** Python wheels have no `postinstall`
> hook — pip deliberately runs no package code at install time. Rather than smuggle
> setup into an import side effect, GForge asks for one explicit command. It is also
> the honest boundary: reconfiguring your global Git config is a decision, not a
> side effect of `pip install`.

## Quick start

```bash
# See the current status of your workstation
gforge verify

# Try it — a hardcoded secret is blocked before it can be committed
echo 'DB_PASSWORD=S3cr3t-Value-123' > config.txt
git add config.txt
git commit -m "add config"
# → GForge blocks the commit and names config.txt (the value is never printed)
```

## Commands

```bash
gforge <command> [--force]
```

| Command | Description |
| --- | --- |
| `gforge install` | Upgrade to the latest version (if any) and install the global hooks. |
| `gforge verify` | Read-only health check of the environment and installed hooks. |
| `gforge update` | Upgrade to the latest version (if any) and refresh the hooks. |
| `gforge uninstall` | Remove GForge-owned hooks and restore your previous Git config. |
| `gforge version` | Print the installed version. |
| `gforge help` | Print usage. |

`--force` (with `install`/`update`) reinstalls the latest release even if you are
already on it. GForge never downgrades below your installed version.

## How detection works

The `pre-commit` hook scans only the files staged for the current commit — not the
whole repository — and blocks the commit if any appear to contain a secret. It
reports file paths, line numbers, and rule names, and **never prints the matched
value**. Detection runs several layers in order:

1. **`.env` cross-reference** — the highest-precision signal: values read (in
   memory only) from your git-ignored `.env` files, matched verbatim in staged code.
2. **Provider rules** — fixed credential shapes for the major cloud and SaaS providers.
3. **Generic secrets** — credential keywords assigned to a hardcoded value; smart
   enough to ignore `os.environ[...]`, function calls, f-strings and `${VAR}`
   interpolation, and obvious placeholders.
4. **Entropy** — unnamed high-entropy strings, tuned to skip Git SHAs, UUIDs,
   lockfiles, and file paths (a path is scored per segment, so a long import path
   is not mistaken for a base64 blob).
5. **Secret files** — `.env` (and `.env.*` except templates), `id_rsa`, `*.p12`/`*.pfx`,
   keystores, `.git-credentials`, `.netrc`, and more.

Detection is best-effort and complements — not replaces — good secret hygiene.

## Managing false positives

Maximum coverage occasionally flags something safe. Three escape hatches:

- **Inline:** add a `gforge:allow` (or `gitleaks:allow`) comment on the line.
- **Per-repo:** add a path or pattern to a `.gforgeignore` file at the repo root
  (a `.gitleaksignore` is also honored):

  ```gitignore
  # .gforgeignore
  tests/fixtures/
  ^docs/sample-config\.md$
  ```

- **One-off:** bypass a single commit with `git commit --no-verify`.

## Staying up to date

`gforge update` upgrades the package to the latest published release and refreshes
the hook. GForge also keeps itself current on its own: at most once a day it checks
PyPI in a detached background process (it never delays or blocks a commit),
installs the update, and prints a one-line notice on commit:

```
gforge: v1.2.0 is available (you have v1.1.0). Run: gforge update
```

The background upgrade only runs when the hook's interpreter is the one GForge is
installed into, so it can never install into the wrong environment.

## Configuration

Behavior is controlled entirely through environment variables — there is no config
file to manage.

| Variable | Effect |
| --- | --- |
| `GFORGE_AUTO_UPDATE=0` | Notify only; do not auto-install new versions (default: auto-install on). |
| `GFORGE_NO_SELF_UPDATE=1` | Skip the pip self-upgrade in `install`/`update` (CI / air-gapped). |
| `GFORGE_PYTHON=/path/to/python` | Pin the Python runtime the hook uses. |
| `NO_COLOR=1` | Disable ANSI colour in all output. |

If a repository or the system already defines its own `core.hooksPath` (e.g. Husky,
lefthook, or `pre-commit`), that value shadows GForge in the affected repository;
`gforge verify` warns when it detects this. Your previous global value is recorded
at install time and restored by `gforge uninstall`.

## Cross-platform support

| Platform | Shells |
| --- | --- |
| macOS | Bash, Zsh |
| Linux | Bash |
| Windows | Git Bash, WSL, PowerShell (via Git for Windows) |

The scanner runs on Python; the hook is a small POSIX shell shim that locates a
Python interpreter robustly (including on Git for Windows) and fails closed if it
cannot — a commit is never allowed through unscanned.

## What GForge changes on your machine

GForge is transparent and fully reversible. It touches only your **global** Git
config and a single directory in your home folder:

- `~/.gforge/hooks/` — the managed hook and scanner (`core.hooksPath` points here).
- `~/.gforge/state.json` — records your previous `core.hooksPath` so `uninstall`
  can restore it.
- `~/.gforge/update-check.json` — the once-a-day version-check cache.

`gforge uninstall` removes GForge-owned files and restores your prior configuration.

## Programmatic use

GForge is primarily a CLI, but both the command runner and the detection engine are
importable:

```python
import sys
from gforge import run_cli

result = run_cli(["verify"], sys.stdout, sys.stderr)
sys.exit(result.exit_code)
```

```python
from gforge.scanner import scan_text

findings = scan_text("config.py", 'API_KEY = "s3cr3tValue123"')
for finding in findings:
    print(finding.file, finding.line, finding.rule_id)  # the value is never exposed
```

## Development

```bash
python -m venv .venv
source .venv/bin/activate          # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

pytest                             # run the test suite
ruff check . && ruff format --check .
mypy                               # strict type checking
python -m build                    # build the sdist and wheel
```

## Differences from the Node original

The behaviour of the detection engine is intentionally identical; the differences
are the ones Python's packaging model forces:

| | Node (`npm i -g gforge`) | Python (this port) |
| --- | --- | --- |
| Setup | Automatic via `postinstall` | Explicit `gforge install` |
| Self-upgrade | `npm install -g gforge@latest` | `pip install --upgrade gforge-python` |
| Version source | `package.json` | Installed distribution metadata |
| Package name | `gforge` | `gforge-python` on PyPI, `gforge` to import and run |
| Hook engine | `~/.gforge/hooks/gforge-scan.mjs` | `~/.gforge/hooks/gforge_scan.py` |
| Interpreter preference | `node` from `PATH`, then baked path | Baked path first, then `PATH` |

The interpreter preference is inverted deliberately: any `node` runs the scanner
equally well, but on Python the interpreter recorded at install time is the one
GForge lives in, which keeps self-upgrade targeting the right environment.

## Roadmap

The secret firewall is the first governance capability. Planned directions:

- Additional commit-time quality gates (commit message and branch conventions,
  large-file and merge-conflict guards).
- Shareable, versioned org policy packs.
- Reporting and audit for governance coverage across a team.

## Credits and attribution

**GForge was created by Gaurang Joshi, Shrey Tandel, and Dwij Acharya**, who
contributed to it equally. The original Node.js implementation lives at
**[github.com/psspl-gaurang/gforge][upstream]** and is the source of everything
that makes this tool what it is: the idea, the product design, the command
surface, the layered detection strategy, and the carefully tuned false-positive
heuristics that the test suite here still encodes.

| | |
| --- | --- |
| **Gaurang Joshi** | Original author — [@psspl-gaurang][gaurang] |
| **Shrey Tandel** | Original author |
| **Dwij Acharya** | Original author |

This package is a Python translation of their work, published under the same
Apache-2.0 licence, with the original `NOTICE` reproduced verbatim. It is not a
fork that claims originality — if GForge is useful to you, the credit belongs
upstream.

Thanks to everyone who has contributed to GForge:

<a href="https://github.com/psspl-gaurang/gforge/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=psspl-gaurang/gforge" alt="GForge contributors" />
</a>

## Contributing

Issues and pull requests are welcome. Please run the checks below before
submitting, keep changes focused, and preserve the Apache-2.0 licence header and
the `NOTICE` file.

```bash
pytest
ruff check .
mypy
```

Changes to the detection engine should stay behaviour-compatible with the upstream
Node implementation wherever practical, so a fix in one can be carried to the other.

## Security

To report a vulnerability, follow the process in [SECURITY.md](SECURITY.md). Do not
open a public issue for security reports, and never include real secrets in a report.

## Licence

Licensed under the [Apache License 2.0](LICENSE), the same licence as the upstream
project. Please preserve the [`NOTICE`](NOTICE) file when redistributing.

[upstream]: https://github.com/psspl-gaurang/gforge
[gaurang]: https://github.com/psspl-gaurang
