git-security-tool 0.1.0__tar.gz → 0.1.1__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 (61) hide show
  1. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/.github/workflows/scan.reusable.yml +8 -9
  2. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/PKG-INFO +65 -29
  3. git_security_tool-0.1.1/README.md +156 -0
  4. git_security_tool-0.1.1/scripts/probe.sh +113 -0
  5. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/__init__.py +1 -1
  6. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/suggestions/providers.py +3 -2
  7. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_providers.py +3 -3
  8. git_security_tool-0.1.0/README.md +0 -120
  9. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/.github/workflows/ci.yml +0 -0
  10. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/.github/workflows/release.yml +0 -0
  11. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/.gitignore +0 -0
  12. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/ARCHITECTURE.md +0 -0
  13. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/CLAUDE.md +0 -0
  14. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/LICENSE +0 -0
  15. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/pyproject.toml +0 -0
  16. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/__main__.py +0 -0
  17. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/baseline.py +0 -0
  18. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/cli.py +0 -0
  19. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/config/__init__.py +0 -0
  20. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/config/loader.py +0 -0
  21. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/git/__init__.py +0 -0
  22. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/git/diff.py +0 -0
  23. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/git/hooks.py +0 -0
  24. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/git/repository.py +0 -0
  25. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/ignore.py +0 -0
  26. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/installer/__init__.py +0 -0
  27. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/installer/dependencies.py +0 -0
  28. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/installer/git_hook.py +0 -0
  29. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/models/__init__.py +0 -0
  30. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/models/finding.py +0 -0
  31. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/policy/__init__.py +0 -0
  32. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/policy/engine.py +0 -0
  33. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/reporter/__init__.py +0 -0
  34. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/reporter/sarif.py +0 -0
  35. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/reporter/terminal.py +0 -0
  36. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/__init__.py +0 -0
  37. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/semgrep/crypto_tls.yml +0 -0
  38. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/semgrep/deserialization.yml +0 -0
  39. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/semgrep/filesystem_net.yml +0 -0
  40. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/semgrep/injection.yml +0 -0
  41. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/rules/semgrep/web.yml +0 -0
  42. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scan.py +0 -0
  43. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scanners/__init__.py +0 -0
  44. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scanners/base.py +0 -0
  45. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scanners/gitleaks.py +0 -0
  46. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scanners/ruff.py +0 -0
  47. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/scanners/semgrep.py +0 -0
  48. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/suggestions/__init__.py +0 -0
  49. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/src/git_security/suggestions/llm.py +0 -0
  50. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_baseline.py +0 -0
  51. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_cli.py +0 -0
  52. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_config.py +0 -0
  53. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_diff.py +0 -0
  54. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_ignore.py +0 -0
  55. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_installer.py +0 -0
  56. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_policy.py +0 -0
  57. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_rules.py +0 -0
  58. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_sarif.py +0 -0
  59. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_scan.py +0 -0
  60. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/tests/test_scanners.py +0 -0
  61. {git_security_tool-0.1.0 → git_security_tool-0.1.1}/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: |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: git-security-tool
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Local Git security and code-quality gate that runs on pre-commit.
5
5
  Project-URL: Homepage, https://github.com/MustafaBasit521/commit-guard
6
6
  Project-URL: Issues, https://github.com/MustafaBasit521/commit-guard/issues
@@ -30,25 +30,46 @@ Description-Content-Type: text/markdown
30
30
  # git-security-tool
31
31
 
32
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)
33
36
 
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
+ **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.
37
43
 
38
44
  It is an **orchestration layer**, not a new scanner: it runs
39
45
  [Gitleaks](https://github.com/gitleaks/gitleaks),
40
46
  [Semgrep](https://semgrep.dev/), and [Ruff](https://docs.astral.sh/ruff/),
41
- normalizes their output, applies your policy, and decides pass/block.
47
+ normalizes their output into one model, applies your policy, and decides
48
+ pass / block.
42
49
 
43
50
  ```
44
51
  git commit
45
52
  └─► .git/hooks/pre-commit
46
53
  └─► 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)
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
52
73
  ```
53
74
 
54
75
  ## Install
@@ -59,14 +80,8 @@ cd your-repo
59
80
  git-security-tool install # writes .git/hooks/pre-commit
60
81
  ```
61
82
 
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`).
83
+ Gitleaks is a Go binary — [install it](https://github.com/gitleaks/gitleaks#installing)
84
+ separately for secret detection (the scan skips any tool that isn't on `PATH`).
70
85
 
71
86
  Commands: `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
72
87
  `uninstall`, `check`, `version`.
@@ -75,23 +90,26 @@ Commands: `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
75
90
  - `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
76
91
  on stdout for GitHub code scanning
77
92
  - `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
93
+ so an existing repo can adopt the tool without fixing everything first;
94
+ new issues still block
95
+
96
+ Verify the ruleset end to end: `./scripts/probe.sh` (checks all 17 rules +
97
+ secret detection in a throwaway repo).
80
98
 
81
99
  ## What it checks
82
100
 
83
101
  | category | tool | severity | blocks by default |
84
102
  |---|---|---|---|
85
- | Secrets / credentials | Gitleaks (staged diff) | CRITICAL | yes |
86
- | Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH yes |
103
+ | Secrets / credentials | Gitleaks | CRITICAL | yes |
104
+ | Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH only |
87
105
  | Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
88
106
  | Formatting | `ruff format --check` | LOW | no |
89
107
 
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).
108
+ The bundled Semgrep rules cover code/command injection (`eval`, `exec`,
109
+ `os.system`, `shell=True`), unsafe deserialization (`pickle`, `yaml.load`,
110
+ insecure XML), weak crypto & disabled TLS verification, web footguns (Flask
111
+ `debug=True`, Jinja autoescape off, `mark_safe`), and filesystem/network
112
+ hygiene (`extractall`, `mktemp`, `requests` without timeout).
95
113
 
96
114
  Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
97
115
  Scanners see the exact **staged** content, not your working tree.
@@ -122,7 +140,22 @@ to the API, and secret-bearing files are never sent.
122
140
  | provider | key env var | extra install |
123
141
  |---|---|---|
124
142
  | `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
125
- | `anthropic` | `ANTHROPIC_API_KEY` | `pip install ".[ai]"` |
143
+ | `anthropic` | `ANTHROPIC_API_KEY` | `pip install "git-security-tool[ai]"` |
144
+
145
+ ## CI (the second layer)
146
+
147
+ Local hooks can be skipped (`--no-verify`) or simply not installed on a
148
+ teammate's machine, so CI is the enforcement layer. This repo ships a
149
+ reusable workflow:
150
+
151
+ ```yaml
152
+ # .github/workflows/security.yml in your project
153
+ name: security
154
+ on: [push, pull_request]
155
+ jobs:
156
+ security:
157
+ uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@main
158
+ ```
126
159
 
127
160
  ## Overrides
128
161
 
@@ -133,10 +166,13 @@ to the API, and secret-bearing files are never sent.
133
166
 
134
167
  ```bash
135
168
  pip install -e ".[scanners,dev]"
136
- pytest
169
+ pytest # 96 tests
137
170
  ruff check src tests && ruff format --check src tests
138
171
  ```
139
172
 
173
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for the design and a milestone-by-milestone
174
+ build log.
175
+
140
176
  ## Scope / non-goals
141
177
 
142
178
  No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
@@ -0,0 +1,156 @@
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
+ ## Install
47
+
48
+ ```bash
49
+ pip install "git-security-tool[scanners]" # tool + ruff + semgrep
50
+ cd your-repo
51
+ git-security-tool install # writes .git/hooks/pre-commit
52
+ ```
53
+
54
+ Gitleaks is a Go binary — [install it](https://github.com/gitleaks/gitleaks#installing)
55
+ separately for secret detection (the scan skips any tool that isn't on `PATH`).
56
+
57
+ Commands: `scan [--all] [--format sarif]`, `baseline`, `install [--force]`,
58
+ `uninstall`, `check`, `version`.
59
+
60
+ - `scan` — staged changes (pre-commit)
61
+ - `scan --all` — every tracked file (CI / audit); `--format sarif` emits SARIF
62
+ on stdout for GitHub code scanning
63
+ - `baseline` — records current findings to `.git-security-tool-baseline.json`
64
+ so an existing repo can adopt the tool without fixing everything first;
65
+ new issues still block
66
+
67
+ Verify the ruleset end to end: `./scripts/probe.sh` (checks all 17 rules +
68
+ secret detection in a throwaway repo).
69
+
70
+ ## What it checks
71
+
72
+ | category | tool | severity | blocks by default |
73
+ |---|---|---|---|
74
+ | Secrets / credentials | Gitleaks | CRITICAL | yes |
75
+ | Insecure code patterns (17 rules) | Semgrep + bundled rules | HIGH / MEDIUM | HIGH only |
76
+ | Lint (unused imports, undefined names, …) | `ruff check` | LOW | no |
77
+ | Formatting | `ruff format --check` | LOW | no |
78
+
79
+ The bundled Semgrep rules cover code/command injection (`eval`, `exec`,
80
+ `os.system`, `shell=True`), unsafe deserialization (`pickle`, `yaml.load`,
81
+ insecure XML), weak crypto & disabled TLS verification, web footguns (Flask
82
+ `debug=True`, Jinja autoescape off, `mark_safe`), and filesystem/network
83
+ hygiene (`extractall`, `mktemp`, `requests` without timeout).
84
+
85
+ Semgrep/Ruff analysis is **Python only**; Gitleaks is language-agnostic.
86
+ Scanners see the exact **staged** content, not your working tree.
87
+
88
+ ## Configuration — `.git-security-tool.toml` (optional, repo root)
89
+
90
+ ```toml
91
+ [policy]
92
+ block_threshold = "HIGH" # INFO | LOW | MEDIUM | HIGH | CRITICAL
93
+
94
+ [scanners]
95
+ gitleaks = false # disable a scanner
96
+
97
+ [ignore]
98
+ paths = ["tests/fixtures/", "*.generated.py"]
99
+
100
+ [ai]
101
+ enabled = false # optional LLM remediation suggestions
102
+ provider = "gemini" # "anthropic" | "gemini"
103
+ model = "" # blank = provider default
104
+ max_findings = 3
105
+ ```
106
+
107
+ **AI suggestions** are off by default and advisory only — they never affect
108
+ the pass/block decision or modify files, they announce before sending code
109
+ to the API, and secret-bearing files are never sent.
110
+
111
+ | provider | key env var | extra install |
112
+ |---|---|---|
113
+ | `gemini` | `GEMINI_API_KEY` | none (stdlib HTTP) |
114
+ | `anthropic` | `ANTHROPIC_API_KEY` | `pip install "git-security-tool[ai]"` |
115
+
116
+ ## CI (the second layer)
117
+
118
+ Local hooks can be skipped (`--no-verify`) or simply not installed on a
119
+ teammate's machine, so CI is the enforcement layer. This repo ships a
120
+ reusable workflow:
121
+
122
+ ```yaml
123
+ # .github/workflows/security.yml in your project
124
+ name: security
125
+ on: [push, pull_request]
126
+ jobs:
127
+ security:
128
+ uses: MustafaBasit521/commit-guard/.github/workflows/scan.reusable.yml@main
129
+ ```
130
+
131
+ ## Overrides
132
+
133
+ - `GIT_SECURITY_NO_BLOCK=1 git commit …` — run the scan, report, never block.
134
+ - `git commit --no-verify` — skip the hook entirely (Git built-in).
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ pip install -e ".[scanners,dev]"
140
+ pytest # 96 tests
141
+ ruff check src tests && ruff format --check src tests
142
+ ```
143
+
144
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for the design and a milestone-by-milestone
145
+ build log.
146
+
147
+ ## Scope / non-goals
148
+
149
+ No dependency-CVE scanning, no license checks, no SBOM, no IaC/container
150
+ scanning, no non-Python static analysis. The bundled Semgrep ruleset is
151
+ curated and intentionally small — not a replacement for a full SAST platform
152
+ or the Semgrep registry.
153
+
154
+ ## License
155
+
156
+ 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.1"
@@ -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,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).