dargslan-git-audit 1.0.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dargslan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: dargslan-git-audit
3
+ Version: 1.0.0
4
+ Summary: Git repository security auditor — scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks
5
+ Author-email: Dargslan <info@dargslan.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://dargslan.com
8
+ Project-URL: Documentation, https://dargslan.com/blog
9
+ Project-URL: Repository, https://github.com/Dargslan
10
+ Project-URL: Free Cheat Sheets, https://dargslan.com/cheat-sheets
11
+ Project-URL: Linux & DevOps Books, https://dargslan.com/books
12
+ Keywords: git,security,audit,secrets,gitignore,devops,linux,sysadmin
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Version Control :: Git
20
+ Requires-Python: >=3.7
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: license-file
24
+
25
+ # dargslan-git-audit
26
+
27
+ **Git Repository Security Auditor** — Scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks. Zero external dependencies.
28
+
29
+ [![PyPI version](https://img.shields.io/pypi/v/dargslan-git-audit)](https://pypi.org/project/dargslan-git-audit/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install dargslan-git-audit
36
+ ```
37
+
38
+ ## CLI Usage
39
+
40
+ ```bash
41
+ dargslan-git report # Full security report
42
+ dargslan-git secrets # Scan for secrets
43
+ dargslan-git sensitive # Check sensitive files
44
+ dargslan-git gitignore # Check .gitignore gaps
45
+ dargslan-git large # Find large files
46
+ dargslan-git stats # Repository statistics
47
+ dargslan-git json # JSON output
48
+ ```
49
+
50
+ ## Python API
51
+
52
+ ```python
53
+ from dargslan_git_audit import GitAudit
54
+ ga = GitAudit()
55
+ secrets = ga.scan_working_secrets()
56
+ issues = ga.audit()
57
+ ga.print_report()
58
+ ```
59
+
60
+ ## More from Dargslan
61
+
62
+ - [Dargslan.com](https://dargslan.com) — Linux & DevOps eBook Store
63
+ - [Free Cheat Sheets](https://dargslan.com/cheat-sheets)
64
+ - [Blog & Tutorials](https://dargslan.com/blog)
65
+
66
+ ## License
67
+
68
+ MIT — see [LICENSE](LICENSE)
@@ -0,0 +1,44 @@
1
+ # dargslan-git-audit
2
+
3
+ **Git Repository Security Auditor** — Scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks. Zero external dependencies.
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/dargslan-git-audit)](https://pypi.org/project/dargslan-git-audit/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install dargslan-git-audit
12
+ ```
13
+
14
+ ## CLI Usage
15
+
16
+ ```bash
17
+ dargslan-git report # Full security report
18
+ dargslan-git secrets # Scan for secrets
19
+ dargslan-git sensitive # Check sensitive files
20
+ dargslan-git gitignore # Check .gitignore gaps
21
+ dargslan-git large # Find large files
22
+ dargslan-git stats # Repository statistics
23
+ dargslan-git json # JSON output
24
+ ```
25
+
26
+ ## Python API
27
+
28
+ ```python
29
+ from dargslan_git_audit import GitAudit
30
+ ga = GitAudit()
31
+ secrets = ga.scan_working_secrets()
32
+ issues = ga.audit()
33
+ ga.print_report()
34
+ ```
35
+
36
+ ## More from Dargslan
37
+
38
+ - [Dargslan.com](https://dargslan.com) — Linux & DevOps eBook Store
39
+ - [Free Cheat Sheets](https://dargslan.com/cheat-sheets)
40
+ - [Blog & Tutorials](https://dargslan.com/blog)
41
+
42
+ ## License
43
+
44
+ MIT — see [LICENSE](LICENSE)
@@ -0,0 +1,230 @@
1
+ """
2
+ dargslan-git-audit — Git Repository Security Auditor
3
+
4
+ Scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks.
5
+ Zero external dependencies — uses only Python standard library.
6
+
7
+ Homepage: https://dargslan.com
8
+ Books: https://dargslan.com/books
9
+ Blog: https://dargslan.com/blog
10
+ """
11
+
12
+ __version__ = "1.0.0"
13
+ __author__ = "Dargslan"
14
+ __url__ = "https://dargslan.com"
15
+
16
+ import os
17
+ import re
18
+ import subprocess
19
+ import json
20
+
21
+
22
+ class GitAudit:
23
+ """Audit Git repositories for security issues."""
24
+
25
+ SECRET_PATTERNS = [
26
+ (r'(?:password|passwd|pwd)\s*[:=]\s*["\']?[A-Za-z0-9!@#$%^&*]{8,}', 'password'),
27
+ (r'(?:api[_-]?key|apikey)\s*[:=]\s*["\']?[A-Za-z0-9]{16,}', 'api_key'),
28
+ (r'(?:secret[_-]?key|secretkey)\s*[:=]\s*["\']?[A-Za-z0-9]{16,}', 'secret_key'),
29
+ (r'(?:access[_-]?token|auth[_-]?token)\s*[:=]\s*["\']?[A-Za-z0-9_\-]{20,}', 'token'),
30
+ (r'AKIA[0-9A-Z]{16}', 'aws_access_key'),
31
+ (r'(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}', 'github_token'),
32
+ (r'sk-[A-Za-z0-9]{48,}', 'openai_key'),
33
+ (r'-----BEGIN (?:RSA |DSA |EC )?PRIVATE KEY-----', 'private_key'),
34
+ (r'(?:mysql|postgres|mongodb)://[^:]+:[^@]+@', 'database_url'),
35
+ (r'Bearer\s+[A-Za-z0-9\-._~+/]+=*', 'bearer_token'),
36
+ ]
37
+
38
+ SENSITIVE_FILES = [
39
+ '.env', '.env.local', '.env.production', 'id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519',
40
+ '*.pem', '*.key', '*.p12', '*.pfx', '*.jks', 'credentials.json', 'service-account.json',
41
+ 'wp-config.php', 'config.php', 'secrets.yml', 'secrets.yaml', '.htpasswd',
42
+ ]
43
+
44
+ def __init__(self, repo_path=None):
45
+ self.repo_path = repo_path or os.getcwd()
46
+
47
+ def _run_git(self, args):
48
+ try:
49
+ result = subprocess.run(['git'] + args, capture_output=True, text=True,
50
+ timeout=30, cwd=self.repo_path)
51
+ return result.stdout.strip() if result.returncode == 0 else ''
52
+ except (FileNotFoundError, subprocess.TimeoutExpired):
53
+ return ''
54
+
55
+ def is_git_repo(self):
56
+ """Check if current directory is a Git repo."""
57
+ return os.path.isdir(os.path.join(self.repo_path, '.git'))
58
+
59
+ def scan_staged_secrets(self):
60
+ """Scan staged files for secrets."""
61
+ diff = self._run_git(['diff', '--cached', '--diff-filter=ACMR'])
62
+ return self._scan_content(diff, 'staged')
63
+
64
+ def scan_working_secrets(self):
65
+ """Scan working directory for secrets."""
66
+ issues = []
67
+ tracked = self._run_git(['ls-files'])
68
+ for filepath in tracked.split('\n'):
69
+ if not filepath.strip(): continue
70
+ full_path = os.path.join(self.repo_path, filepath)
71
+ try:
72
+ if os.path.getsize(full_path) > 1048576: continue
73
+ with open(full_path, 'r', errors='replace') as f:
74
+ content = f.read()
75
+ for pattern, secret_type in self.SECRET_PATTERNS:
76
+ matches = re.findall(pattern, content, re.IGNORECASE)
77
+ for match in matches[:3]:
78
+ issues.append({
79
+ 'file': filepath,
80
+ 'type': secret_type,
81
+ 'severity': 'critical',
82
+ 'preview': match[:30] + '...' if len(match) > 30 else match,
83
+ })
84
+ except (FileNotFoundError, PermissionError, UnicodeDecodeError):
85
+ continue
86
+ return issues
87
+
88
+ def _scan_content(self, content, source):
89
+ issues = []
90
+ for pattern, secret_type in self.SECRET_PATTERNS:
91
+ matches = re.findall(pattern, content, re.IGNORECASE)
92
+ for match in matches[:5]:
93
+ issues.append({
94
+ 'source': source,
95
+ 'type': secret_type,
96
+ 'severity': 'critical',
97
+ 'preview': match[:30] + '...',
98
+ })
99
+ return issues
100
+
101
+ def check_gitignore(self):
102
+ """Check for missing .gitignore entries."""
103
+ gitignore_path = os.path.join(self.repo_path, '.gitignore')
104
+ missing = []
105
+ gitignore_content = ''
106
+ if os.path.exists(gitignore_path):
107
+ with open(gitignore_path, 'r') as f:
108
+ gitignore_content = f.read()
109
+
110
+ recommended = ['.env', '.env.*', '*.pem', '*.key', 'id_rsa', 'id_dsa',
111
+ 'node_modules/', '__pycache__/', '*.pyc', '.DS_Store',
112
+ 'credentials.json', '*.log', 'dist/', 'build/']
113
+
114
+ for entry in recommended:
115
+ if entry not in gitignore_content:
116
+ exists = False
117
+ tracked = self._run_git(['ls-files', '--', entry.rstrip('/')])
118
+ if tracked: exists = True
119
+ missing.append({'pattern': entry, 'tracked': exists})
120
+
121
+ return missing
122
+
123
+ def find_large_files(self, threshold_mb=10):
124
+ """Find large files in repository history."""
125
+ output = self._run_git(['rev-list', '--objects', '--all'])
126
+ large_files = []
127
+ if not output: return large_files
128
+
129
+ for line in output.split('\n')[:500]:
130
+ parts = line.split(None, 1)
131
+ if len(parts) < 2: continue
132
+ obj_hash, path = parts
133
+ size_output = self._run_git(['cat-file', '-s', obj_hash])
134
+ if size_output and size_output.isdigit():
135
+ size_bytes = int(size_output)
136
+ if size_bytes > threshold_mb * 1024 * 1024:
137
+ large_files.append({
138
+ 'path': path,
139
+ 'size_mb': round(size_bytes / 1024 / 1024, 2),
140
+ 'hash': obj_hash[:8],
141
+ })
142
+ return sorted(large_files, key=lambda x: x['size_mb'], reverse=True)[:20]
143
+
144
+ def check_sensitive_files(self):
145
+ """Check for tracked sensitive files."""
146
+ tracked = self._run_git(['ls-files'])
147
+ found = []
148
+ for filepath in tracked.split('\n'):
149
+ basename = os.path.basename(filepath)
150
+ for pattern in self.SENSITIVE_FILES:
151
+ if pattern.startswith('*.'):
152
+ if basename.endswith(pattern[1:]):
153
+ found.append({'file': filepath, 'pattern': pattern, 'severity': 'high'})
154
+ break
155
+ elif basename == pattern:
156
+ found.append({'file': filepath, 'pattern': pattern, 'severity': 'critical'})
157
+ break
158
+ return found
159
+
160
+ def repo_stats(self):
161
+ """Get repository statistics."""
162
+ commit_count = self._run_git(['rev-list', '--count', 'HEAD'])
163
+ branch_count = self._run_git(['branch', '-a', '--list'])
164
+ contributor_count = self._run_git(['shortlog', '-sn', '--all'])
165
+
166
+ return {
167
+ 'commits': int(commit_count) if commit_count and commit_count.isdigit() else 0,
168
+ 'branches': len([b for b in branch_count.split('\n') if b.strip()]) if branch_count else 0,
169
+ 'contributors': len([c for c in contributor_count.split('\n') if c.strip()]) if contributor_count else 0,
170
+ }
171
+
172
+ def audit(self):
173
+ """Run full Git security audit."""
174
+ issues = []
175
+
176
+ for s in self.scan_working_secrets():
177
+ issues.append({'severity': 'critical', 'category': 'secret',
178
+ 'message': f"Secret ({s['type']}) in {s['file']}"})
179
+
180
+ for f in self.check_sensitive_files():
181
+ issues.append({'severity': f['severity'], 'category': 'sensitive_file',
182
+ 'message': f"Sensitive file tracked: {f['file']}"})
183
+
184
+ gitignore_gaps = self.check_gitignore()
185
+ tracked_gaps = [g for g in gitignore_gaps if g['tracked']]
186
+ if tracked_gaps:
187
+ issues.append({'severity': 'warning', 'category': 'gitignore',
188
+ 'message': f"{len(tracked_gaps)} sensitive patterns tracked but not in .gitignore"})
189
+
190
+ return issues
191
+
192
+ def print_report(self):
193
+ """Print formatted Git audit report."""
194
+ stats = self.repo_stats()
195
+ issues = self.audit()
196
+
197
+ print(f"\n{'='*60}")
198
+ print(f" Git Repository Security Audit")
199
+ print(f" Path: {self.repo_path}")
200
+ print(f"{'='*60}")
201
+ print(f"\n Commits: {stats['commits']}")
202
+ print(f" Branches: {stats['branches']}")
203
+ print(f" Contributors: {stats['contributors']}")
204
+
205
+ secrets = self.scan_working_secrets()
206
+ if secrets:
207
+ print(f"\n Secrets Found ({len(secrets)}):")
208
+ for s in secrets[:10]:
209
+ print(f" [CRITICAL] {s['file']}: {s['type']}")
210
+
211
+ sensitive = self.check_sensitive_files()
212
+ if sensitive:
213
+ print(f"\n Sensitive Files ({len(sensitive)}):")
214
+ for f in sensitive[:10]:
215
+ print(f" [{f['severity'].upper()}] {f['file']}")
216
+
217
+ if issues:
218
+ print(f"\n Total Issues ({len(issues)}):")
219
+ for i in issues:
220
+ print(f" [{i['severity'].upper():8s}] {i['message']}")
221
+ else:
222
+ print("\n No security issues found.")
223
+
224
+ print(f"\n{'='*60}")
225
+ print(f" More tools: https://dargslan.com")
226
+ print(f" eBooks: https://dargslan.com/books")
227
+ print(f"{'='*60}\n")
228
+
229
+
230
+ __all__ = ["GitAudit"]
@@ -0,0 +1,36 @@
1
+ """CLI for dargslan-git-audit — https://dargslan.com"""
2
+ import argparse
3
+
4
+ def main():
5
+ parser = argparse.ArgumentParser(
6
+ description="Dargslan Git Audit — Git repository security auditor",
7
+ epilog="More tools at https://dargslan.com | eBooks: https://dargslan.com/books",
8
+ )
9
+ parser.add_argument("command", nargs="?", default="report",
10
+ choices=["report", "secrets", "sensitive", "gitignore", "large", "stats", "issues", "json"],
11
+ help="Command (default: report)")
12
+ parser.add_argument("-p", "--path", help="Repository path")
13
+ parser.add_argument("-s", "--size", type=int, default=10, help="Large file threshold (MB)")
14
+ args = parser.parse_args()
15
+
16
+ from dargslan_git_audit import GitAudit
17
+ ga = GitAudit(repo_path=args.path)
18
+ import json
19
+
20
+ if args.command == 'report': ga.print_report()
21
+ elif args.command == 'secrets':
22
+ for s in ga.scan_working_secrets(): print(f" [{s['severity'].upper()}] {s['file']}: {s['type']}")
23
+ elif args.command == 'sensitive':
24
+ for f in ga.check_sensitive_files(): print(f" [{f['severity'].upper()}] {f['file']}")
25
+ elif args.command == 'gitignore':
26
+ for g in ga.check_gitignore():
27
+ tracked = " [TRACKED!]" if g['tracked'] else ""
28
+ print(f" Missing: {g['pattern']}{tracked}")
29
+ elif args.command == 'large':
30
+ for f in ga.find_large_files(args.size): print(f" {f['size_mb']:.1f} MB: {f['path']}")
31
+ elif args.command == 'stats': print(json.dumps(ga.repo_stats(), indent=2))
32
+ elif args.command == 'issues':
33
+ for i in ga.audit(): print(f" [{i['severity'].upper()}] {i['message']}")
34
+ elif args.command == 'json': print(json.dumps(ga.audit(), indent=2))
35
+
36
+ if __name__ == "__main__": main()
@@ -0,0 +1,68 @@
1
+ Metadata-Version: 2.4
2
+ Name: dargslan-git-audit
3
+ Version: 1.0.0
4
+ Summary: Git repository security auditor — scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks
5
+ Author-email: Dargslan <info@dargslan.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://dargslan.com
8
+ Project-URL: Documentation, https://dargslan.com/blog
9
+ Project-URL: Repository, https://github.com/Dargslan
10
+ Project-URL: Free Cheat Sheets, https://dargslan.com/cheat-sheets
11
+ Project-URL: Linux & DevOps Books, https://dargslan.com/books
12
+ Keywords: git,security,audit,secrets,gitignore,devops,linux,sysadmin
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Version Control :: Git
20
+ Requires-Python: >=3.7
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Dynamic: license-file
24
+
25
+ # dargslan-git-audit
26
+
27
+ **Git Repository Security Auditor** — Scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks. Zero external dependencies.
28
+
29
+ [![PyPI version](https://img.shields.io/pypi/v/dargslan-git-audit)](https://pypi.org/project/dargslan-git-audit/)
30
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
31
+
32
+ ## Installation
33
+
34
+ ```bash
35
+ pip install dargslan-git-audit
36
+ ```
37
+
38
+ ## CLI Usage
39
+
40
+ ```bash
41
+ dargslan-git report # Full security report
42
+ dargslan-git secrets # Scan for secrets
43
+ dargslan-git sensitive # Check sensitive files
44
+ dargslan-git gitignore # Check .gitignore gaps
45
+ dargslan-git large # Find large files
46
+ dargslan-git stats # Repository statistics
47
+ dargslan-git json # JSON output
48
+ ```
49
+
50
+ ## Python API
51
+
52
+ ```python
53
+ from dargslan_git_audit import GitAudit
54
+ ga = GitAudit()
55
+ secrets = ga.scan_working_secrets()
56
+ issues = ga.audit()
57
+ ga.print_report()
58
+ ```
59
+
60
+ ## More from Dargslan
61
+
62
+ - [Dargslan.com](https://dargslan.com) — Linux & DevOps eBook Store
63
+ - [Free Cheat Sheets](https://dargslan.com/cheat-sheets)
64
+ - [Blog & Tutorials](https://dargslan.com/blog)
65
+
66
+ ## License
67
+
68
+ MIT — see [LICENSE](LICENSE)
@@ -0,0 +1,10 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ dargslan_git_audit/__init__.py
5
+ dargslan_git_audit/cli.py
6
+ dargslan_git_audit.egg-info/PKG-INFO
7
+ dargslan_git_audit.egg-info/SOURCES.txt
8
+ dargslan_git_audit.egg-info/dependency_links.txt
9
+ dargslan_git_audit.egg-info/entry_points.txt
10
+ dargslan_git_audit.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ dargslan-git = dargslan_git_audit.cli:main
@@ -0,0 +1 @@
1
+ dargslan_git_audit
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "dargslan-git-audit"
7
+ version = "1.0.0"
8
+ description = "Git repository security auditor — scan for secrets in commits, large files, .gitignore gaps, and sensitive data leaks"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.7"
12
+ authors = [{name = "Dargslan", email = "info@dargslan.com"}]
13
+ keywords = ["git", "security", "audit", "secrets", "gitignore", "devops", "linux", "sysadmin"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: POSIX :: Linux",
19
+ "Programming Language :: Python :: 3",
20
+ "Topic :: Security",
21
+ "Topic :: Software Development :: Version Control :: Git",
22
+ ]
23
+
24
+ [project.urls]
25
+ Homepage = "https://dargslan.com"
26
+ Documentation = "https://dargslan.com/blog"
27
+ Repository = "https://github.com/Dargslan"
28
+ "Free Cheat Sheets" = "https://dargslan.com/cheat-sheets"
29
+ "Linux & DevOps Books" = "https://dargslan.com/books"
30
+
31
+ [project.scripts]
32
+ dargslan-git = "dargslan_git_audit.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+