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.
Files changed (62) hide show
  1. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/scan.reusable.yml +8 -9
  2. git_security_tool-0.1.2/PKG-INFO +226 -0
  3. git_security_tool-0.1.2/README.md +197 -0
  4. git_security_tool-0.1.2/scripts/probe.sh +113 -0
  5. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/__init__.py +1 -1
  6. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/providers.py +3 -2
  7. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_providers.py +3 -3
  8. git_security_tool-0.1.0/PKG-INFO +0 -149
  9. git_security_tool-0.1.0/README.md +0 -120
  10. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/ci.yml +0 -0
  11. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.github/workflows/release.yml +0 -0
  12. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/.gitignore +0 -0
  13. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/ARCHITECTURE.md +0 -0
  14. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/CLAUDE.md +0 -0
  15. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/LICENSE +0 -0
  16. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/pyproject.toml +0 -0
  17. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/__main__.py +0 -0
  18. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/baseline.py +0 -0
  19. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/cli.py +0 -0
  20. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/config/__init__.py +0 -0
  21. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/config/loader.py +0 -0
  22. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/__init__.py +0 -0
  23. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/diff.py +0 -0
  24. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/hooks.py +0 -0
  25. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/git/repository.py +0 -0
  26. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/ignore.py +0 -0
  27. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/__init__.py +0 -0
  28. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/dependencies.py +0 -0
  29. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/installer/git_hook.py +0 -0
  30. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/models/__init__.py +0 -0
  31. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/models/finding.py +0 -0
  32. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/policy/__init__.py +0 -0
  33. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/policy/engine.py +0 -0
  34. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/__init__.py +0 -0
  35. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/sarif.py +0 -0
  36. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/reporter/terminal.py +0 -0
  37. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/__init__.py +0 -0
  38. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/crypto_tls.yml +0 -0
  39. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/deserialization.yml +0 -0
  40. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/filesystem_net.yml +0 -0
  41. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/injection.yml +0 -0
  42. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/rules/semgrep/web.yml +0 -0
  43. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scan.py +0 -0
  44. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/__init__.py +0 -0
  45. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/base.py +0 -0
  46. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/gitleaks.py +0 -0
  47. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/ruff.py +0 -0
  48. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/scanners/semgrep.py +0 -0
  49. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/__init__.py +0 -0
  50. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/src/git_security/suggestions/llm.py +0 -0
  51. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_baseline.py +0 -0
  52. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_cli.py +0 -0
  53. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_config.py +0 -0
  54. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_diff.py +0 -0
  55. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_ignore.py +0 -0
  56. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_installer.py +0 -0
  57. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_policy.py +0 -0
  58. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_rules.py +0 -0
  59. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_sarif.py +0 -0
  60. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_scan.py +0 -0
  61. {git_security_tool-0.1.0 → git_security_tool-0.1.2}/tests/test_scanners.py +0 -0
  62. {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@main
7
+ # uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@v0.1.0
8
8
  #
9
- # Pin @v0.1.0 (or a commit SHA) instead of @main once the tool is tagged, so a
10
- # change to the tool can't unexpectedly break a downstream project's CI.
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
- ref:
16
- description: "git-security-tool version, branch, or SHA to install"
16
+ version:
17
+ description: "git-security-tool version to install from PyPI"
17
18
  type: string
18
- default: main
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
+ [![CI](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
33
+ [![PyPI](https://img.shields.io/pypi/v/git-security-tool)](https://pypi.org/project/git-security-tool/)
34
+ [![Python](https://img.shields.io/pypi/pyversions/git-security-tool)](https://pypi.org/project/git-security-tool/)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](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
+ [![CI](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/git-security-tool)](https://pypi.org/project/git-security-tool/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/git-security-tool)](https://pypi.org/project/git-security-tool/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](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
@@ -1,3 +1,3 @@
1
1
  """git-security-tool: a local Git security and code-quality gate."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
@@ -18,7 +18,8 @@ import os
18
18
  import urllib.error
19
19
  import urllib.request
20
20
 
21
- _TIMEOUT = 30
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-2.5-flash"
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-2.5-flash")
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-2.5-flash")
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-2.5-flash")
76
+ GeminiProvider().complete("sys", "user", "gemini-3.6-flash")
@@ -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
- [![CI](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml/badge.svg)](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
- [![CI](https://github.com/MustafaBasit521/commit-guard/actions/workflows/ci.yml/badge.svg)](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).