git-security-tool 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/scan.reusable.yml +8 -9
- git_security_tool-0.1.2/PKG-INFO +226 -0
- git_security_tool-0.1.2/README.md +197 -0
- git_security_tool-0.1.2/scripts/probe.sh +113 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/__init__.py +1 -1
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/providers.py +3 -2
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_providers.py +3 -3
- git_security_tool-0.1.0/PKG-INFO +0 -149
- git_security_tool-0.1.0/README.md +0 -120
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/ci.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/release.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.gitignore +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/ARCHITECTURE.md +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/CLAUDE.md +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/LICENSE +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/pyproject.toml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/__main__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/baseline.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/cli.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/config/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/config/loader.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/diff.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/hooks.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/repository.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/ignore.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/dependencies.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/git_hook.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/models/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/models/finding.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/policy/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/policy/engine.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/sarif.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/terminal.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/crypto_tls.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/deserialization.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/filesystem_net.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/injection.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/web.yml +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scan.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/base.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/gitleaks.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/ruff.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/semgrep.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/__init__.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/llm.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_baseline.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_cli.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_config.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_diff.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_ignore.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_installer.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_policy.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_rules.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_sarif.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_scan.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_scanners.py +0 -0
- {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_suggestions.py +0 -0
|
@@ -4,18 +4,19 @@ name: git-security-tool scan
|
|
|
4
4
|
#
|
|
5
5
|
# jobs:
|
|
6
6
|
# security:
|
|
7
|
-
# uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@
|
|
7
|
+
# uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@v0.1.0
|
|
8
8
|
#
|
|
9
|
-
# Pin @v0.1.0
|
|
10
|
-
#
|
|
9
|
+
# Pin the caller to a tag (@v0.1.0), not @main, so a change to this workflow
|
|
10
|
+
# can't unexpectedly break a downstream project's CI. Bump `version` here when
|
|
11
|
+
# a new git-security-tool release is out.
|
|
11
12
|
|
|
12
13
|
on:
|
|
13
14
|
workflow_call:
|
|
14
15
|
inputs:
|
|
15
|
-
|
|
16
|
-
description: "git-security-tool version
|
|
16
|
+
version:
|
|
17
|
+
description: "git-security-tool version to install from PyPI"
|
|
17
18
|
type: string
|
|
18
|
-
default:
|
|
19
|
+
default: "0.1.0"
|
|
19
20
|
python-version:
|
|
20
21
|
type: string
|
|
21
22
|
default: "3.12"
|
|
@@ -31,9 +32,7 @@ jobs:
|
|
|
31
32
|
python-version: ${{ inputs.python-version }}
|
|
32
33
|
|
|
33
34
|
- name: Install git-security-tool
|
|
34
|
-
run:
|
|
35
|
-
pip install "git-security-tool[scanners] @
|
|
36
|
-
git+https://github.com/MustafaBasit521/commit-guard.git@${{ inputs.ref }}"
|
|
35
|
+
run: pip install "git-security-tool[scanners]==${{ inputs.version }}"
|
|
37
36
|
|
|
38
37
|
- name: Install gitleaks
|
|
39
38
|
run: |
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: git-security-tool
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Local Git security and code-quality gate that runs on pre-commit.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MustafaBasit521/commit-guard
|
|
6
|
+
Project-URL: Issues, https://github.com/MustafaBasit521/commit-guard/issues
|
|
7
|
+
Author: Muhammad Mustafa Basit
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: git,gitleaks,pre-commit,sast,secrets,security,semgrep
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Provides-Extra: ai
|
|
21
|
+
Requires-Dist: anthropic; extra == 'ai'
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
24
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
25
|
+
Provides-Extra: scanners
|
|
26
|
+
Requires-Dist: ruff; extra == 'scanners'
|
|
27
|
+
Requires-Dist: semgrep; extra == 'scanners'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# git-security-tool
|
|
31
|
+
|
|
32
|
+
[](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
|
|
33
|
+
[](https://pypi.org/project/git-security-tool/)
|
|
34
|
+
[](https://pypi.org/project/git-security-tool/)
|
|
35
|
+
[](LICENSE)
|
|
36
|
+
|
|
37
|
+
**Stop secrets and dangerous code from reaching a commit.** A local Git
|
|
38
|
+
pre-commit gate (Linux) that scans your *staged* changes and blocks the
|
|
39
|
+
commit when it finds something serious — leaked credentials, `eval()`,
|
|
40
|
+
`pickle.loads`, disabled TLS verification — while surfacing lint and
|
|
41
|
+
formatting issues as non-blocking warnings. The same engine runs in CI over
|
|
42
|
+
the whole repository.
|
|
43
|
+
|
|
44
|
+
It is an **orchestration layer**, not a new scanner: it runs
|
|
45
|
+
[Gitleaks](https://github.com/gitleaks/gitleaks),
|
|
46
|
+
[Semgrep](https://semgrep.dev/), and [Ruff](https://docs.astral.sh/ruff/),
|
|
47
|
+
normalizes their output into one model, applies your policy, and decides
|
|
48
|
+
pass / block.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
git commit
|
|
52
|
+
└─► .git/hooks/pre-commit
|
|
53
|
+
└─► git-security-tool scan
|
|
54
|
+
├─ gitleaks → secrets (CRITICAL → blocks)
|
|
55
|
+
├─ semgrep + 17 rules → insecure code (HIGH → blocks)
|
|
56
|
+
├─ ruff check → lint (LOW → warns)
|
|
57
|
+
└─ ruff format --check → formatting (LOW → warns)
|
|
58
|
+
└─► policy → exit 0 (allow) | exit 1 (block)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### What a blocked commit looks like
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
$ git commit -m "add config loader"
|
|
65
|
+
[git-security-tool] pre-commit security scan
|
|
66
|
+
[git-security-tool] 2 file(s) to scan
|
|
67
|
+
[git-security-tool] 1 blocking finding(s):
|
|
68
|
+
[HIGH] semgrep:python-yaml-unsafe-load config.py:14 - yaml.load() without a
|
|
69
|
+
safe loader can construct arbitrary Python objects from the input.
|
|
70
|
+
[git-security-tool] summary: 1 blocking, 0 warning(s)
|
|
71
|
+
[git-security-tool] commit blocked - fix the blocking findings above, or set
|
|
72
|
+
GIT_SECURITY_NO_BLOCK=1 to override
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Add it to a project
|
|
76
|
+
|
|
77
|
+
### 1. Install the tool (once per machine)
|
|
78
|
+
|
|
79
|
+
The pre-commit hook runs `git-security-tool` from your `PATH`, so install it
|
|
80
|
+
somewhere permanent — [pipx](https://pipx.pypa.io/) is cleanest:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pipx install "git-security-tool[scanners]" # isolated, always on PATH
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Or into a virtualenv you keep active / on `PATH`:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pip install "git-security-tool[scanners]"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Then add **Gitleaks** (a Go binary, not on PyPI) for secret detection —
|
|
93
|
+
[download a release](https://github.com/gitleaks/gitleaks/releases) and put
|
|
94
|
+
it on `PATH`. The scan silently skips any scanner that isn't installed.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
git-security-tool check # confirms hook status + which scanners are present
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 2. Turn on the hook (once per repo)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cd your-repo
|
|
104
|
+
git-security-tool install # writes .git/hooks/pre-commit
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`.git/hooks/` is not version-controlled, so every clone / machine runs this
|
|
108
|
+
once. `git-security-tool uninstall` removes it; `--force` replaces a hook
|
|
109
|
+
you didn't create.
|
|
110
|
+
|
|
111
|
+
### 3. (existing repo) Baseline what's already there
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git-security-tool scan --all # see what it finds first
|
|
115
|
+
git-security-tool baseline # grandfather those findings
|
|
116
|
+
git add .git-security-tool-baseline.json && git commit -m "add security baseline"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
New issues still block — only the pre-existing ones are ignored.
|
|
120
|
+
|
|
121
|
+
### 4. Add the CI check
|
|
122
|
+
|
|
123
|
+
Local hooks can be skipped (`--no-verify`) or missing on a teammate's
|
|
124
|
+
machine, so CI is the real enforcement. Create
|
|
125
|
+
`.github/workflows/security.yml`:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
name: security
|
|
129
|
+
on: [push, pull_request]
|
|
130
|
+
jobs:
|
|
131
|
+
security:
|
|
132
|
+
uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@main
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
That reusable workflow installs the tool + Gitleaks and runs
|
|
136
|
+
`git-security-tool scan --all` over the whole repo.
|
|
137
|
+
|
|
138
|
+
### 5. (optional) Tune it — `.git-security-tool.toml`
|
|
139
|
+
|
|
140
|
+
See [Configuration](#configuration--git-security-tooltoml-optional-repo-root)
|
|
141
|
+
below: severity threshold, disable a scanner, ignore paths, AI suggestions.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
**Commands:** `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
|
|
146
|
+
`uninstall`, `check`, `version`.
|
|
147
|
+
|
|
148
|
+
- `scan` — staged changes (pre-commit)
|
|
149
|
+
- `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
|
|
150
|
+
on stdout for GitHub code scanning
|
|
151
|
+
|
|
152
|
+
Verify the ruleset end to end: `./scripts/probe.sh` (checks all 17 rules +
|
|
153
|
+
secret detection in a throwaway repo).
|
|
154
|
+
|
|
155
|
+
## What it checks
|
|
156
|
+
|
|
157
|
+
| category | tool | severity | blocks by default |
|
|
158
|
+
|---|---|---|---|
|
|
159
|
+
| Secrets / credentials | Gitleaks | CRITICAL | yes |
|
|
160
|
+
| Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH only |
|
|
161
|
+
| Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
|
|
162
|
+
| Formatting | `ruff format --check` | LOW | no |
|
|
163
|
+
|
|
164
|
+
The bundled Semgrep rules cover code/command injection (`eval`, `exec`,
|
|
165
|
+
`os.system`, `shell=True`), unsafe deserialization (`pickle`, `yaml.load`,
|
|
166
|
+
insecure XML), weak crypto & disabled TLS verification, web footguns (Flask
|
|
167
|
+
`debug=True`, Jinja autoescape off, `mark_safe`), and filesystem/network
|
|
168
|
+
hygiene (`extractall`, `mktemp`, `requests` without timeout).
|
|
169
|
+
|
|
170
|
+
Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
|
|
171
|
+
Scanners see the exact **staged** content, not your working tree.
|
|
172
|
+
|
|
173
|
+
## Configuration — `.git-security-tool.toml` (optional, repo root)
|
|
174
|
+
|
|
175
|
+
```toml
|
|
176
|
+
[policy]
|
|
177
|
+
block_threshold = "HIGH" # INFO | LOW | MEDIUM | HIGH | CRITICAL
|
|
178
|
+
|
|
179
|
+
[scanners]
|
|
180
|
+
gitleaks = false # disable a scanner
|
|
181
|
+
|
|
182
|
+
[ignore]
|
|
183
|
+
paths = ["tests/fixtures/", "*.generated.py"]
|
|
184
|
+
|
|
185
|
+
[ai]
|
|
186
|
+
enabled = false # optional LLM remediation suggestions
|
|
187
|
+
provider = "gemini" # "anthropic" | "gemini"
|
|
188
|
+
model = "" # blank = provider default
|
|
189
|
+
max_findings = 3
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**AI suggestions** are off by default and advisory only — they never affect
|
|
193
|
+
the pass/block decision or modify files, they announce before sending code
|
|
194
|
+
to the API, and secret-bearing files are never sent.
|
|
195
|
+
|
|
196
|
+
| provider | key env var | extra install |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
|
|
199
|
+
| `anthropic` | `ANTHROPIC_API_KEY` | `pip install "git-security-tool[ai]"` |
|
|
200
|
+
|
|
201
|
+
## Overrides
|
|
202
|
+
|
|
203
|
+
- `GIT_SECURITY_NO_BLOCK=1 git commit …` — run the scan, report, never block.
|
|
204
|
+
- `git commit --no-verify` — skip the hook entirely (Git built-in).
|
|
205
|
+
|
|
206
|
+
## Development
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pip install -e ".[scanners,dev]"
|
|
210
|
+
pytest # 96 tests
|
|
211
|
+
ruff check src tests && ruff format --check src tests
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for the design and a milestone-by-milestone
|
|
215
|
+
build log.
|
|
216
|
+
|
|
217
|
+
## Scope / non-goals
|
|
218
|
+
|
|
219
|
+
No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
|
|
220
|
+
scanning, no non-Python static analysis. The bundled Semgrep ruleset is
|
|
221
|
+
curated and intentionally small — not a replacement for a full SAST platform
|
|
222
|
+
or the Semgrep registry.
|
|
223
|
+
|
|
224
|
+
## License
|
|
225
|
+
|
|
226
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# git-security-tool
|
|
2
|
+
|
|
3
|
+
[](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/git-security-tool/)
|
|
5
|
+
[](https://pypi.org/project/git-security-tool/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**Stop secrets and dangerous code from reaching a commit.** A local Git
|
|
9
|
+
pre-commit gate (Linux) that scans your *staged* changes and blocks the
|
|
10
|
+
commit when it finds something serious — leaked credentials, `eval()`,
|
|
11
|
+
`pickle.loads`, disabled TLS verification — while surfacing lint and
|
|
12
|
+
formatting issues as non-blocking warnings. The same engine runs in CI over
|
|
13
|
+
the whole repository.
|
|
14
|
+
|
|
15
|
+
It is an **orchestration layer**, not a new scanner: it runs
|
|
16
|
+
[Gitleaks](https://github.com/gitleaks/gitleaks),
|
|
17
|
+
[Semgrep](https://semgrep.dev/), and [Ruff](https://docs.astral.sh/ruff/),
|
|
18
|
+
normalizes their output into one model, applies your policy, and decides
|
|
19
|
+
pass / block.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
git commit
|
|
23
|
+
└─► .git/hooks/pre-commit
|
|
24
|
+
└─► git-security-tool scan
|
|
25
|
+
├─ gitleaks → secrets (CRITICAL → blocks)
|
|
26
|
+
├─ semgrep + 17 rules → insecure code (HIGH → blocks)
|
|
27
|
+
├─ ruff check → lint (LOW → warns)
|
|
28
|
+
└─ ruff format --check → formatting (LOW → warns)
|
|
29
|
+
└─► policy → exit 0 (allow) | exit 1 (block)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### What a blocked commit looks like
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
$ git commit -m "add config loader"
|
|
36
|
+
[git-security-tool] pre-commit security scan
|
|
37
|
+
[git-security-tool] 2 file(s) to scan
|
|
38
|
+
[git-security-tool] 1 blocking finding(s):
|
|
39
|
+
[HIGH] semgrep:python-yaml-unsafe-load config.py:14 - yaml.load() without a
|
|
40
|
+
safe loader can construct arbitrary Python objects from the input.
|
|
41
|
+
[git-security-tool] summary: 1 blocking, 0 warning(s)
|
|
42
|
+
[git-security-tool] commit blocked - fix the blocking findings above, or set
|
|
43
|
+
GIT_SECURITY_NO_BLOCK=1 to override
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Add it to a project
|
|
47
|
+
|
|
48
|
+
### 1. Install the tool (once per machine)
|
|
49
|
+
|
|
50
|
+
The pre-commit hook runs `git-security-tool` from your `PATH`, so install it
|
|
51
|
+
somewhere permanent — [pipx](https://pipx.pypa.io/) is cleanest:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pipx install "git-security-tool[scanners]" # isolated, always on PATH
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or into a virtualenv you keep active / on `PATH`:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install "git-security-tool[scanners]"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then add **Gitleaks** (a Go binary, not on PyPI) for secret detection —
|
|
64
|
+
[download a release](https://github.com/gitleaks/gitleaks/releases) and put
|
|
65
|
+
it on `PATH`. The scan silently skips any scanner that isn't installed.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git-security-tool check # confirms hook status + which scanners are present
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. Turn on the hook (once per repo)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
cd your-repo
|
|
75
|
+
git-security-tool install # writes .git/hooks/pre-commit
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`.git/hooks/` is not version-controlled, so every clone / machine runs this
|
|
79
|
+
once. `git-security-tool uninstall` removes it; `--force` replaces a hook
|
|
80
|
+
you didn't create.
|
|
81
|
+
|
|
82
|
+
### 3. (existing repo) Baseline what's already there
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
git-security-tool scan --all # see what it finds first
|
|
86
|
+
git-security-tool baseline # grandfather those findings
|
|
87
|
+
git add .git-security-tool-baseline.json && git commit -m "add security baseline"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
New issues still block — only the pre-existing ones are ignored.
|
|
91
|
+
|
|
92
|
+
### 4. Add the CI check
|
|
93
|
+
|
|
94
|
+
Local hooks can be skipped (`--no-verify`) or missing on a teammate's
|
|
95
|
+
machine, so CI is the real enforcement. Create
|
|
96
|
+
`.github/workflows/security.yml`:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
name: security
|
|
100
|
+
on: [push, pull_request]
|
|
101
|
+
jobs:
|
|
102
|
+
security:
|
|
103
|
+
uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@main
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
That reusable workflow installs the tool + Gitleaks and runs
|
|
107
|
+
`git-security-tool scan --all` over the whole repo.
|
|
108
|
+
|
|
109
|
+
### 5. (optional) Tune it — `.git-security-tool.toml`
|
|
110
|
+
|
|
111
|
+
See [Configuration](#configuration--git-security-tooltoml-optional-repo-root)
|
|
112
|
+
below: severity threshold, disable a scanner, ignore paths, AI suggestions.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
**Commands:** `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
|
|
117
|
+
`uninstall`, `check`, `version`.
|
|
118
|
+
|
|
119
|
+
- `scan` — staged changes (pre-commit)
|
|
120
|
+
- `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
|
|
121
|
+
on stdout for GitHub code scanning
|
|
122
|
+
|
|
123
|
+
Verify the ruleset end to end: `./scripts/probe.sh` (checks all 17 rules +
|
|
124
|
+
secret detection in a throwaway repo).
|
|
125
|
+
|
|
126
|
+
## What it checks
|
|
127
|
+
|
|
128
|
+
| category | tool | severity | blocks by default |
|
|
129
|
+
|---|---|---|---|
|
|
130
|
+
| Secrets / credentials | Gitleaks | CRITICAL | yes |
|
|
131
|
+
| Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH only |
|
|
132
|
+
| Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
|
|
133
|
+
| Formatting | `ruff format --check` | LOW | no |
|
|
134
|
+
|
|
135
|
+
The bundled Semgrep rules cover code/command injection (`eval`, `exec`,
|
|
136
|
+
`os.system`, `shell=True`), unsafe deserialization (`pickle`, `yaml.load`,
|
|
137
|
+
insecure XML), weak crypto & disabled TLS verification, web footguns (Flask
|
|
138
|
+
`debug=True`, Jinja autoescape off, `mark_safe`), and filesystem/network
|
|
139
|
+
hygiene (`extractall`, `mktemp`, `requests` without timeout).
|
|
140
|
+
|
|
141
|
+
Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
|
|
142
|
+
Scanners see the exact **staged** content, not your working tree.
|
|
143
|
+
|
|
144
|
+
## Configuration — `.git-security-tool.toml` (optional, repo root)
|
|
145
|
+
|
|
146
|
+
```toml
|
|
147
|
+
[policy]
|
|
148
|
+
block_threshold = "HIGH" # INFO | LOW | MEDIUM | HIGH | CRITICAL
|
|
149
|
+
|
|
150
|
+
[scanners]
|
|
151
|
+
gitleaks = false # disable a scanner
|
|
152
|
+
|
|
153
|
+
[ignore]
|
|
154
|
+
paths = ["tests/fixtures/", "*.generated.py"]
|
|
155
|
+
|
|
156
|
+
[ai]
|
|
157
|
+
enabled = false # optional LLM remediation suggestions
|
|
158
|
+
provider = "gemini" # "anthropic" | "gemini"
|
|
159
|
+
model = "" # blank = provider default
|
|
160
|
+
max_findings = 3
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**AI suggestions** are off by default and advisory only — they never affect
|
|
164
|
+
the pass/block decision or modify files, they announce before sending code
|
|
165
|
+
to the API, and secret-bearing files are never sent.
|
|
166
|
+
|
|
167
|
+
| provider | key env var | extra install |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
|
|
170
|
+
| `anthropic` | `ANTHROPIC_API_KEY` | `pip install "git-security-tool[ai]"` |
|
|
171
|
+
|
|
172
|
+
## Overrides
|
|
173
|
+
|
|
174
|
+
- `GIT_SECURITY_NO_BLOCK=1 git commit …` — run the scan, report, never block.
|
|
175
|
+
- `git commit --no-verify` — skip the hook entirely (Git built-in).
|
|
176
|
+
|
|
177
|
+
## Development
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install -e ".[scanners,dev]"
|
|
181
|
+
pytest # 96 tests
|
|
182
|
+
ruff check src tests && ruff format --check src tests
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for the design and a milestone-by-milestone
|
|
186
|
+
build log.
|
|
187
|
+
|
|
188
|
+
## Scope / non-goals
|
|
189
|
+
|
|
190
|
+
No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
|
|
191
|
+
scanning, no non-Python static analysis. The bundled Semgrep ruleset is
|
|
192
|
+
curated and intentionally small — not a replacement for a full SAST platform
|
|
193
|
+
or the Semgrep registry.
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Verify every bundled Semgrep rule fires, plus secret detection. Creates a
|
|
3
|
+
# throwaway git repo, drops one probe file per rule category and a fake
|
|
4
|
+
# secret, runs `git-security-tool scan --all`, then reports which of the 17
|
|
5
|
+
# rules were seen. Touches nothing in your real repositories.
|
|
6
|
+
#
|
|
7
|
+
# ./scripts/probe.sh
|
|
8
|
+
set -eu
|
|
9
|
+
|
|
10
|
+
probe=$(mktemp -d)
|
|
11
|
+
trap 'rm -rf "$probe"' EXIT
|
|
12
|
+
cd "$probe"
|
|
13
|
+
git init -q
|
|
14
|
+
git config user.email probe@example.com
|
|
15
|
+
git config user.name probe
|
|
16
|
+
|
|
17
|
+
# match the real project's lint config so the output isn't full of ruff noise
|
|
18
|
+
cat > pyproject.toml <<'EOF'
|
|
19
|
+
[tool.ruff.lint]
|
|
20
|
+
select = ["E4", "E7", "E9", "F", "I"]
|
|
21
|
+
EOF
|
|
22
|
+
|
|
23
|
+
cat > injection.py <<'EOF'
|
|
24
|
+
import os
|
|
25
|
+
import subprocess
|
|
26
|
+
eval(user_input) # python-dangerous-eval
|
|
27
|
+
exec(user_input) # python-dangerous-exec
|
|
28
|
+
os.system(cmd) # python-os-system
|
|
29
|
+
os.popen(cmd) # python-os-popen
|
|
30
|
+
subprocess.run(cmd, shell=True, check=True) # python-subprocess-shell-true
|
|
31
|
+
EOF
|
|
32
|
+
|
|
33
|
+
cat > deserialization.py <<'EOF'
|
|
34
|
+
import pickle
|
|
35
|
+
import xml.etree.ElementTree as ET
|
|
36
|
+
|
|
37
|
+
import yaml
|
|
38
|
+
pickle.loads(blob) # python-pickle-load
|
|
39
|
+
yaml.load(text) # python-yaml-unsafe-load
|
|
40
|
+
ET.fromstring(untrusted_xml) # python-insecure-xml-parser
|
|
41
|
+
EOF
|
|
42
|
+
|
|
43
|
+
cat > crypto_tls.py <<'EOF'
|
|
44
|
+
import hashlib
|
|
45
|
+
import ssl
|
|
46
|
+
|
|
47
|
+
import requests
|
|
48
|
+
hashlib.md5(b"x") # python-weak-hash
|
|
49
|
+
requests.get(u, verify=False, timeout=5) # python-tls-verification-disabled
|
|
50
|
+
ssl._create_unverified_context() # python-ssl-unverified-context
|
|
51
|
+
EOF
|
|
52
|
+
|
|
53
|
+
cat > web.py <<'EOF'
|
|
54
|
+
import jinja2
|
|
55
|
+
from django.utils.safestring import mark_safe
|
|
56
|
+
app.run(debug=True) # python-flask-debug-true
|
|
57
|
+
jinja2.Environment() # python-jinja-autoescape-disabled
|
|
58
|
+
mark_safe(user_input) # python-django-mark-safe
|
|
59
|
+
EOF
|
|
60
|
+
|
|
61
|
+
cat > filesystem_net.py <<'EOF'
|
|
62
|
+
import tempfile
|
|
63
|
+
|
|
64
|
+
import requests
|
|
65
|
+
archive.extractall(dest) # python-archive-extractall
|
|
66
|
+
tempfile.mktemp() # python-tempfile-mktemp
|
|
67
|
+
requests.get(url) # python-requests-no-timeout
|
|
68
|
+
EOF
|
|
69
|
+
|
|
70
|
+
# Fake GitHub token - not a real credential; never leaves this temp repo.
|
|
71
|
+
# The gitleaks:allow marker keeps *this* script committable; the secrets.txt
|
|
72
|
+
# it writes has no such marker and is what the probe expects gitleaks to flag.
|
|
73
|
+
tok='ghp_016C7f4a9BdEfGhIjKlMnOpQrStUvWx0y1Z2a' # gitleaks:allow
|
|
74
|
+
printf 'API_TOKEN=%s\n' "$tok" > secrets.txt
|
|
75
|
+
|
|
76
|
+
git add -A
|
|
77
|
+
out=$(git-security-tool scan --all 2>&1 || true)
|
|
78
|
+
printf '%s\n' "$out"
|
|
79
|
+
|
|
80
|
+
echo
|
|
81
|
+
echo "=================================================================="
|
|
82
|
+
rules="python-dangerous-eval python-dangerous-exec python-os-system \
|
|
83
|
+
python-os-popen python-subprocess-shell-true python-pickle-load \
|
|
84
|
+
python-yaml-unsafe-load python-insecure-xml-parser python-weak-hash \
|
|
85
|
+
python-tls-verification-disabled python-ssl-unverified-context \
|
|
86
|
+
python-flask-debug-true python-jinja-autoescape-disabled \
|
|
87
|
+
python-django-mark-safe python-archive-extractall python-tempfile-mktemp \
|
|
88
|
+
python-requests-no-timeout"
|
|
89
|
+
|
|
90
|
+
missing=0
|
|
91
|
+
for r in $rules; do
|
|
92
|
+
if printf '%s' "$out" | grep -q "semgrep:$r"; then
|
|
93
|
+
echo " ok $r"
|
|
94
|
+
else
|
|
95
|
+
echo " MISS $r"
|
|
96
|
+
missing=$((missing + 1))
|
|
97
|
+
fi
|
|
98
|
+
done
|
|
99
|
+
|
|
100
|
+
if printf '%s' "$out" | grep -q "gitleaks:"; then
|
|
101
|
+
echo " ok gitleaks secret detection"
|
|
102
|
+
else
|
|
103
|
+
echo " MISS gitleaks secret detection"
|
|
104
|
+
missing=$((missing + 1))
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
echo "=================================================================="
|
|
108
|
+
if [ "$missing" -eq 0 ]; then
|
|
109
|
+
echo "All 17 rules + secret detection fired."
|
|
110
|
+
else
|
|
111
|
+
echo "$missing check(s) missing - a rule regressed."
|
|
112
|
+
exit 1
|
|
113
|
+
fi
|
{git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/providers.py
RENAMED
|
@@ -18,7 +18,8 @@ import os
|
|
|
18
18
|
import urllib.error
|
|
19
19
|
import urllib.request
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
# Newer models spend time reasoning before answering; give them room.
|
|
22
|
+
_TIMEOUT = 60
|
|
22
23
|
_MAX_OUTPUT_TOKENS = 2048
|
|
23
24
|
|
|
24
25
|
|
|
@@ -54,7 +55,7 @@ class AnthropicProvider:
|
|
|
54
55
|
|
|
55
56
|
class GeminiProvider:
|
|
56
57
|
name = "gemini"
|
|
57
|
-
default_model = "gemini-
|
|
58
|
+
default_model = "gemini-3.6-flash" # override via [ai] model = "..."
|
|
58
59
|
env_var = "GEMINI_API_KEY"
|
|
59
60
|
_URL = (
|
|
60
61
|
"https://generativelanguage.googleapis.com/v1beta/models/"
|
|
@@ -52,7 +52,7 @@ def test_gemini_complete_returns_text(monkeypatch):
|
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
54
|
)
|
|
55
|
-
out = GeminiProvider().complete("sys", "user", "gemini-
|
|
55
|
+
out = GeminiProvider().complete("sys", "user", "gemini-3.6-flash")
|
|
56
56
|
assert out == "use parameterized queries"
|
|
57
57
|
|
|
58
58
|
|
|
@@ -60,7 +60,7 @@ def test_gemini_complete_raises_when_blocked(monkeypatch):
|
|
|
60
60
|
monkeypatch.setenv("GEMINI_API_KEY", "k")
|
|
61
61
|
_patch_urlopen(monkeypatch, {"promptFeedback": {"blockReason": "SAFETY"}})
|
|
62
62
|
with pytest.raises(RuntimeError, match="SAFETY"):
|
|
63
|
-
GeminiProvider().complete("sys", "user", "gemini-
|
|
63
|
+
GeminiProvider().complete("sys", "user", "gemini-3.6-flash")
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
def test_gemini_complete_wraps_http_error(monkeypatch):
|
|
@@ -73,4 +73,4 @@ def test_gemini_complete_wraps_http_error(monkeypatch):
|
|
|
73
73
|
|
|
74
74
|
monkeypatch.setattr("urllib.request.urlopen", _raise)
|
|
75
75
|
with pytest.raises(RuntimeError, match="Gemini API error 400"):
|
|
76
|
-
GeminiProvider().complete("sys", "user", "gemini-
|
|
76
|
+
GeminiProvider().complete("sys", "user", "gemini-3.6-flash")
|
git_security_tool-0.1.0/PKG-INFO
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: git-security-tool
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Local Git security and code-quality gate that runs on pre-commit.
|
|
5
|
-
Project-URL: Homepage, https://github.com/MustafaBasit521/commit-guard
|
|
6
|
-
Project-URL: Issues, https://github.com/MustafaBasit521/commit-guard/issues
|
|
7
|
-
Author: Muhammad Mustafa Basit
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Keywords: git,gitleaks,pre-commit,sast,secrets,security,semgrep
|
|
11
|
-
Classifier: Development Status :: 4 - Beta
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Developers
|
|
14
|
-
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
-
Classifier: Topic :: Security
|
|
17
|
-
Classifier: Topic :: Software Development :: Quality Assurance
|
|
18
|
-
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
19
|
-
Requires-Python: >=3.11
|
|
20
|
-
Provides-Extra: ai
|
|
21
|
-
Requires-Dist: anthropic; extra == 'ai'
|
|
22
|
-
Provides-Extra: dev
|
|
23
|
-
Requires-Dist: pytest; extra == 'dev'
|
|
24
|
-
Requires-Dist: ruff; extra == 'dev'
|
|
25
|
-
Provides-Extra: scanners
|
|
26
|
-
Requires-Dist: ruff; extra == 'scanners'
|
|
27
|
-
Requires-Dist: semgrep; extra == 'scanners'
|
|
28
|
-
Description-Content-Type: text/markdown
|
|
29
|
-
|
|
30
|
-
# git-security-tool
|
|
31
|
-
|
|
32
|
-
[](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
|
|
33
|
-
|
|
34
|
-
A local Git **pre-commit gate** for Linux. It scans your *staged* changes and
|
|
35
|
-
blocks the commit when it finds something serious — secrets, dangerous code
|
|
36
|
-
patterns — while surfacing quality and formatting issues as warnings.
|
|
37
|
-
|
|
38
|
-
It is an **orchestration layer**, not a new scanner: it runs
|
|
39
|
-
[Gitleaks](https://github.com/gitleaks/gitleaks),
|
|
40
|
-
[Semgrep](https://semgrep.dev/), and [Ruff](https://docs.astral.sh/ruff/),
|
|
41
|
-
normalizes their output, applies your policy, and decides pass/block.
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
git commit
|
|
45
|
-
└─► .git/hooks/pre-commit
|
|
46
|
-
└─► git-security-tool scan
|
|
47
|
-
├─ gitleaks → secrets (CRITICAL → blocks)
|
|
48
|
-
├─ semgrep → security patterns (HIGH → blocks)
|
|
49
|
-
├─ ruff check → lint (LOW → warns)
|
|
50
|
-
└─ ruff format --check → format (LOW → warns)
|
|
51
|
-
└─► PASS (exit 0) / BLOCK (exit 1)
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Install
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
pip install "git-security-tool[scanners]" # tool + ruff + semgrep
|
|
58
|
-
cd your-repo
|
|
59
|
-
git-security-tool install # writes .git/hooks/pre-commit
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Latest unreleased version, straight from the repo:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
pip install "git-security-tool[scanners] @ git+https://github.com/MustafaBasit521/commit-guard.git"
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Gitleaks is a Go binary — install it separately if you want secret detection
|
|
69
|
-
(the scan skips any tool that isn't on `PATH`).
|
|
70
|
-
|
|
71
|
-
Commands: `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
|
|
72
|
-
`uninstall`, `check`, `version`.
|
|
73
|
-
|
|
74
|
-
- `scan` — staged changes (pre-commit)
|
|
75
|
-
- `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
|
|
76
|
-
on stdout for GitHub code scanning
|
|
77
|
-
- `baseline` — records current findings to `.git-security-tool-baseline.json`
|
|
78
|
-
so a repo can adopt the tool without fixing everything first; new issues
|
|
79
|
-
still block
|
|
80
|
-
|
|
81
|
-
## What it checks
|
|
82
|
-
|
|
83
|
-
| category | tool | severity | blocks by default |
|
|
84
|
-
|---|---|---|---|
|
|
85
|
-
| Secrets / credentials | Gitleaks (staged diff) | CRITICAL | yes |
|
|
86
|
-
| Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH yes |
|
|
87
|
-
| Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
|
|
88
|
-
| Formatting | `ruff format --check` | LOW | no |
|
|
89
|
-
|
|
90
|
-
The bundled Semgrep rules (`src/git_security/rules/semgrep/`) cover code/command
|
|
91
|
-
injection (`eval`, `exec`, `os.system`, `shell=True`), unsafe deserialization
|
|
92
|
-
(`pickle`, `yaml.load`, insecure XML), weak crypto & disabled TLS verification,
|
|
93
|
-
web footguns (Flask `debug=True`, Jinja autoescape off, `mark_safe`), and
|
|
94
|
-
filesystem/network hygiene (`extractall`, `mktemp`, `requests` without timeout).
|
|
95
|
-
|
|
96
|
-
Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
|
|
97
|
-
Scanners see the exact **staged** content, not your working tree.
|
|
98
|
-
|
|
99
|
-
## Configuration — `.git-security-tool.toml` (optional, repo root)
|
|
100
|
-
|
|
101
|
-
```toml
|
|
102
|
-
[policy]
|
|
103
|
-
block_threshold = "HIGH" # INFO | LOW | MEDIUM | HIGH | CRITICAL
|
|
104
|
-
|
|
105
|
-
[scanners]
|
|
106
|
-
gitleaks = false # disable a scanner
|
|
107
|
-
|
|
108
|
-
[ignore]
|
|
109
|
-
paths = ["tests/fixtures/", "*.generated.py"]
|
|
110
|
-
|
|
111
|
-
[ai]
|
|
112
|
-
enabled = false # optional LLM remediation suggestions
|
|
113
|
-
provider = "gemini" # "anthropic" | "gemini"
|
|
114
|
-
model = "" # blank = provider default
|
|
115
|
-
max_findings = 3
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
**AI suggestions** are off by default and advisory only — they never affect
|
|
119
|
-
the pass/block decision or modify files, they announce before sending code
|
|
120
|
-
to the API, and secret-bearing files are never sent.
|
|
121
|
-
|
|
122
|
-
| provider | key env var | extra install |
|
|
123
|
-
|---|---|---|
|
|
124
|
-
| `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
|
|
125
|
-
| `anthropic` | `ANTHROPIC_API_KEY` | `pip install ".[ai]"` |
|
|
126
|
-
|
|
127
|
-
## Overrides
|
|
128
|
-
|
|
129
|
-
- `GIT_SECURITY_NO_BLOCK=1 git commit …` — run the scan, report, never block.
|
|
130
|
-
- `git commit --no-verify` — skip the hook entirely (Git built-in).
|
|
131
|
-
|
|
132
|
-
## Development
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
pip install -e ".[scanners,dev]"
|
|
136
|
-
pytest
|
|
137
|
-
ruff check src tests && ruff format --check src tests
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Scope / non-goals
|
|
141
|
-
|
|
142
|
-
No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
|
|
143
|
-
scanning, no non-Python static analysis. The bundled Semgrep ruleset is
|
|
144
|
-
curated and intentionally small — not a replacement for a full SAST platform
|
|
145
|
-
or the Semgrep registry.
|
|
146
|
-
|
|
147
|
-
## License
|
|
148
|
-
|
|
149
|
-
MIT — see [LICENSE](LICENSE).
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
# git-security-tool
|
|
2
|
-
|
|
3
|
-
[](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
|
|
4
|
-
|
|
5
|
-
A local Git **pre-commit gate** for Linux. It scans your *staged* changes and
|
|
6
|
-
blocks the commit when it finds something serious — secrets, dangerous code
|
|
7
|
-
patterns — while surfacing quality and formatting issues as warnings.
|
|
8
|
-
|
|
9
|
-
It is an **orchestration layer**, not a new scanner: it runs
|
|
10
|
-
[Gitleaks](https://github.com/gitleaks/gitleaks),
|
|
11
|
-
[Semgrep](https://semgrep.dev/), and [Ruff](https://docs.astral.sh/ruff/),
|
|
12
|
-
normalizes their output, applies your policy, and decides pass/block.
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
git commit
|
|
16
|
-
└─► .git/hooks/pre-commit
|
|
17
|
-
└─► git-security-tool scan
|
|
18
|
-
├─ gitleaks → secrets (CRITICAL → blocks)
|
|
19
|
-
├─ semgrep → security patterns (HIGH → blocks)
|
|
20
|
-
├─ ruff check → lint (LOW → warns)
|
|
21
|
-
└─ ruff format --check → format (LOW → warns)
|
|
22
|
-
└─► PASS (exit 0) / BLOCK (exit 1)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Install
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
pip install "git-security-tool[scanners]" # tool + ruff + semgrep
|
|
29
|
-
cd your-repo
|
|
30
|
-
git-security-tool install # writes .git/hooks/pre-commit
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Latest unreleased version, straight from the repo:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
pip install "git-security-tool[scanners] @ git+https://github.com/MustafaBasit521/commit-guard.git"
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Gitleaks is a Go binary — install it separately if you want secret detection
|
|
40
|
-
(the scan skips any tool that isn't on `PATH`).
|
|
41
|
-
|
|
42
|
-
Commands: `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
|
|
43
|
-
`uninstall`, `check`, `version`.
|
|
44
|
-
|
|
45
|
-
- `scan` — staged changes (pre-commit)
|
|
46
|
-
- `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
|
|
47
|
-
on stdout for GitHub code scanning
|
|
48
|
-
- `baseline` — records current findings to `.git-security-tool-baseline.json`
|
|
49
|
-
so a repo can adopt the tool without fixing everything first; new issues
|
|
50
|
-
still block
|
|
51
|
-
|
|
52
|
-
## What it checks
|
|
53
|
-
|
|
54
|
-
| category | tool | severity | blocks by default |
|
|
55
|
-
|---|---|---|---|
|
|
56
|
-
| Secrets / credentials | Gitleaks (staged diff) | CRITICAL | yes |
|
|
57
|
-
| Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH yes |
|
|
58
|
-
| Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
|
|
59
|
-
| Formatting | `ruff format --check` | LOW | no |
|
|
60
|
-
|
|
61
|
-
The bundled Semgrep rules (`src/git_security/rules/semgrep/`) cover code/command
|
|
62
|
-
injection (`eval`, `exec`, `os.system`, `shell=True`), unsafe deserialization
|
|
63
|
-
(`pickle`, `yaml.load`, insecure XML), weak crypto & disabled TLS verification,
|
|
64
|
-
web footguns (Flask `debug=True`, Jinja autoescape off, `mark_safe`), and
|
|
65
|
-
filesystem/network hygiene (`extractall`, `mktemp`, `requests` without timeout).
|
|
66
|
-
|
|
67
|
-
Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
|
|
68
|
-
Scanners see the exact **staged** content, not your working tree.
|
|
69
|
-
|
|
70
|
-
## Configuration — `.git-security-tool.toml` (optional, repo root)
|
|
71
|
-
|
|
72
|
-
```toml
|
|
73
|
-
[policy]
|
|
74
|
-
block_threshold = "HIGH" # INFO | LOW | MEDIUM | HIGH | CRITICAL
|
|
75
|
-
|
|
76
|
-
[scanners]
|
|
77
|
-
gitleaks = false # disable a scanner
|
|
78
|
-
|
|
79
|
-
[ignore]
|
|
80
|
-
paths = ["tests/fixtures/", "*.generated.py"]
|
|
81
|
-
|
|
82
|
-
[ai]
|
|
83
|
-
enabled = false # optional LLM remediation suggestions
|
|
84
|
-
provider = "gemini" # "anthropic" | "gemini"
|
|
85
|
-
model = "" # blank = provider default
|
|
86
|
-
max_findings = 3
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
**AI suggestions** are off by default and advisory only — they never affect
|
|
90
|
-
the pass/block decision or modify files, they announce before sending code
|
|
91
|
-
to the API, and secret-bearing files are never sent.
|
|
92
|
-
|
|
93
|
-
| provider | key env var | extra install |
|
|
94
|
-
|---|---|---|
|
|
95
|
-
| `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
|
|
96
|
-
| `anthropic` | `ANTHROPIC_API_KEY` | `pip install ".[ai]"` |
|
|
97
|
-
|
|
98
|
-
## Overrides
|
|
99
|
-
|
|
100
|
-
- `GIT_SECURITY_NO_BLOCK=1 git commit …` — run the scan, report, never block.
|
|
101
|
-
- `git commit --no-verify` — skip the hook entirely (Git built-in).
|
|
102
|
-
|
|
103
|
-
## Development
|
|
104
|
-
|
|
105
|
-
```bash
|
|
106
|
-
pip install -e ".[scanners,dev]"
|
|
107
|
-
pytest
|
|
108
|
-
ruff check src tests && ruff format --check src tests
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Scope / non-goals
|
|
112
|
-
|
|
113
|
-
No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
|
|
114
|
-
scanning, no non-Python static analysis. The bundled Semgrep ruleset is
|
|
115
|
-
curated and intentionally small — not a replacement for a full SAST platform
|
|
116
|
-
or the Semgrep registry.
|
|
117
|
-
|
|
118
|
-
## License
|
|
119
|
-
|
|
120
|
-
MIT — see [LICENSE](LICENSE).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/dependencies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/crypto_tls.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/injection.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|