gitlab-api 25.36.0__tar.gz → 25.37.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.
- {gitlab_api-25.36.0/gitlab_api.egg-info → gitlab_api-25.37.0}/PKG-INFO +5 -5
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/README.md +1 -1
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/agent_server.py +1 -1
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp_server.py +1 -1
- {gitlab_api-25.36.0 → gitlab_api-25.37.0/gitlab_api.egg-info}/PKG-INFO +5 -5
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api.egg-info/SOURCES.txt +1 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api.egg-info/requires.txt +3 -3
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/pyproject.toml +4 -4
- gitlab_api-25.37.0/scripts/security_sanitizer.py +160 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/LICENSE +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/MANIFEST.in +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/__init__.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/__main__.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/__init__.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_base.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_environments.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_issues.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_merge_requests.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_other.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_pipelines.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_projects.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_repositories.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_system.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api/api_client_users_groups.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/api_client.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/auth.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/gitlab_gql.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/gitlab_input_models.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/gitlab_response_models.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/__init__.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_branches.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_commits.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_custom_api.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_deploy_tokens.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_environments.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_epics.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_graphql.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_groups.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_issues.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_jobs.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_labels.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_members.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_merge_requests.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_merge_rules.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_milestones.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_misc.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_notes.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_packages.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_pipeline_schedules.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_pipelines.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_projects.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_protected_branches.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_releases.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_runners.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_snippets.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp/mcp_tags.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api/mcp_config.json +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api.egg-info/dependency_links.txt +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api.egg-info/entry_points.txt +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/gitlab_api.egg-info/top_level.txt +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/requirements.txt +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/scripts/validate_a2a_agent.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/scripts/validate_agent.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/scripts/verify_api_integration.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/setup.cfg +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/__init__.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/conftest.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_api_wrapper.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_auth.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_concept_parity.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_gitlab_a2a_validation.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_gitlab_api_brute_force_coverage.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_gitlab_gql.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_gitlab_mcp_validation.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_gitlab_models.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_init_dynamics.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_mock_coverage.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_startup.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/test_verify_agent.py +0 -0
- {gitlab_api-25.36.0 → gitlab_api-25.37.0}/tests/verify_a2a_queries.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlab-api
|
|
3
|
-
Version: 25.
|
|
3
|
+
Version: 25.37.0
|
|
4
4
|
Summary: GitLab API + MCP Server + A2A Server
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,13 +12,13 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: <3.14,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities>=0.
|
|
15
|
+
Requires-Dist: agent-utilities>=0.38.0
|
|
16
16
|
Requires-Dist: python-dotenv>=1.0.0
|
|
17
17
|
Requires-Dist: gql>=4.0.0
|
|
18
18
|
Provides-Extra: mcp
|
|
19
|
-
Requires-Dist: agent-utilities[mcp]>=0.
|
|
19
|
+
Requires-Dist: agent-utilities[mcp]>=0.38.0; extra == "mcp"
|
|
20
20
|
Provides-Extra: agent
|
|
21
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.
|
|
21
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.38.0; extra == "agent"
|
|
22
22
|
Provides-Extra: gql
|
|
23
23
|
Requires-Dist: gql>=4.0.0; extra == "gql"
|
|
24
24
|
Provides-Extra: all
|
|
@@ -52,7 +52,7 @@ Dynamic: license-file
|
|
|
52
52
|

|
|
53
53
|

|
|
54
54
|
|
|
55
|
-
*Version: 25.
|
|
55
|
+
*Version: 25.37.0*
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitlab-api
|
|
3
|
-
Version: 25.
|
|
3
|
+
Version: 25.37.0
|
|
4
4
|
Summary: GitLab API + MCP Server + A2A Server
|
|
5
5
|
Author-email: Audel Rouhi <knucklessg1@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -12,13 +12,13 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: <3.14,>=3.11
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
|
-
Requires-Dist: agent-utilities>=0.
|
|
15
|
+
Requires-Dist: agent-utilities>=0.38.0
|
|
16
16
|
Requires-Dist: python-dotenv>=1.0.0
|
|
17
17
|
Requires-Dist: gql>=4.0.0
|
|
18
18
|
Provides-Extra: mcp
|
|
19
|
-
Requires-Dist: agent-utilities[mcp]>=0.
|
|
19
|
+
Requires-Dist: agent-utilities[mcp]>=0.38.0; extra == "mcp"
|
|
20
20
|
Provides-Extra: agent
|
|
21
|
-
Requires-Dist: agent-utilities[agent,logfire]>=0.
|
|
21
|
+
Requires-Dist: agent-utilities[agent,logfire]>=0.38.0; extra == "agent"
|
|
22
22
|
Provides-Extra: gql
|
|
23
23
|
Requires-Dist: gql>=4.0.0; extra == "gql"
|
|
24
24
|
Provides-Extra: all
|
|
@@ -52,7 +52,7 @@ Dynamic: license-file
|
|
|
52
52
|

|
|
53
53
|

|
|
54
54
|
|
|
55
|
-
*Version: 25.
|
|
55
|
+
*Version: 25.37.0*
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
agent-utilities>=0.
|
|
1
|
+
agent-utilities>=0.38.0
|
|
2
2
|
python-dotenv>=1.0.0
|
|
3
3
|
gql>=4.0.0
|
|
4
4
|
|
|
5
5
|
[agent]
|
|
6
|
-
agent-utilities[agent,logfire]>=0.
|
|
6
|
+
agent-utilities[agent,logfire]>=0.38.0
|
|
7
7
|
|
|
8
8
|
[all]
|
|
9
9
|
gitlab-api[agent,gql,logfire,mcp]>=25.36.0
|
|
@@ -12,7 +12,7 @@ gitlab-api[agent,gql,logfire,mcp]>=25.36.0
|
|
|
12
12
|
gql>=4.0.0
|
|
13
13
|
|
|
14
14
|
[mcp]
|
|
15
|
-
agent-utilities[mcp]>=0.
|
|
15
|
+
agent-utilities[mcp]>=0.38.0
|
|
16
16
|
|
|
17
17
|
[test]
|
|
18
18
|
pytest-xdist>=3.6.0
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitlab-api"
|
|
7
|
-
version = "25.
|
|
7
|
+
version = "25.37.0"
|
|
8
8
|
description = "GitLab API + MCP Server + A2A Server"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
classifiers = [ "Development Status :: 5 - Production/Stable", "License :: Public Domain", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",]
|
|
11
11
|
requires-python = ">=3.11, <3.14"
|
|
12
|
-
dependencies = [ "agent-utilities>=0.
|
|
12
|
+
dependencies = [ "agent-utilities>=0.38.0", "python-dotenv>=1.0.0", "gql>=4.0.0",]
|
|
13
13
|
[[project.authors]]
|
|
14
14
|
name = "Audel Rouhi"
|
|
15
15
|
email = "knucklessg1@gmail.com"
|
|
@@ -18,8 +18,8 @@ email = "knucklessg1@gmail.com"
|
|
|
18
18
|
text = "MIT"
|
|
19
19
|
|
|
20
20
|
[project.optional-dependencies]
|
|
21
|
-
mcp = [ "agent-utilities[mcp]>=0.
|
|
22
|
-
agent = [ "agent-utilities[agent,logfire]>=0.
|
|
21
|
+
mcp = [ "agent-utilities[mcp]>=0.38.0",]
|
|
22
|
+
agent = [ "agent-utilities[agent,logfire]>=0.38.0",]
|
|
23
23
|
gql = [ "gql>=4.0.0",]
|
|
24
24
|
all = [ "gitlab-api[mcp,agent,gql,logfire]>=25.36.0",]
|
|
25
25
|
test = [
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import sys
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Config
|
|
9
|
+
ALLOWED_TXT_NAMES = {"requirements.txt", "requirements-dev.txt"}
|
|
10
|
+
TRANSIENT_PY_PATTERNS = [
|
|
11
|
+
re.compile(r"^test_.*\.py$"),
|
|
12
|
+
re.compile(r"^fix_.*\.py$"),
|
|
13
|
+
re.compile(r"^debug_.*\.py$"),
|
|
14
|
+
re.compile(r"^scratch_.*\.py$"),
|
|
15
|
+
re.compile(r"^temp_.*\.py$"),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
SECRET_PATTERNS = [
|
|
19
|
+
("GitHub PAT", re.compile(r"ghp_[A-Za-z0-9_]{36,255}")),
|
|
20
|
+
("GitHub Fine-grained PAT", re.compile(r"github_pat_[A-Za-z0-9_]{82,255}")),
|
|
21
|
+
("GitLab PAT", re.compile(r"glpat-[A-Za-z0-9\-]{20,255}")),
|
|
22
|
+
("Generic Secret Assignment", re.compile(r"secret[A-Za-z0-9_]*\s*[:=]\s*['\"][A-Za-z0-9_\-\.\~\*]{16,255}['\"]", re.IGNORECASE)),
|
|
23
|
+
("Generic Token Assignment", re.compile(r"token\s*[:=]\s*['\"][A-Za-z0-9_\-\.\~\*]{16,255}['\"]", re.IGNORECASE))
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
EXCLUDED_DIRS = {".git", ".venv", "venv", "node_modules", "build", "dist", "__pycache__", ".tox", ".specify"}
|
|
27
|
+
EXCLUDED_EXTENSIONS = {
|
|
28
|
+
".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".pyc", ".db", ".kuzu",
|
|
29
|
+
".sqlite", ".sqlite3", ".zip", ".tar.gz", ".tgz", ".bz2", ".xz", ".pdf",
|
|
30
|
+
".bin", ".exe", ".dll", ".so", ".dylib", ".woff", ".woff2", ".eot", ".ttf",
|
|
31
|
+
".mp4", ".mp3", ".wav", ".lock", ".svg"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# Placeholder / Mock indicators
|
|
35
|
+
PLACEHOLDER_SUBSTRINGS = [
|
|
36
|
+
"1234567890", "abcdef12345", "abc123youandme", "askdfalskdvjas", "your_", "YOUR_", "your-",
|
|
37
|
+
"dummy", "DUMMY", "example", "EXAMPLE", "mock", "MOCK", "test_token", "test_secret",
|
|
38
|
+
"glpat-askdfalskdvjas", "github_pat_12345", "glpat-abc123youandme", "github_pat_...",
|
|
39
|
+
"glpat-*************", "ghp_*************", "github_pat_*************", "token_*************",
|
|
40
|
+
"secret_*************", "glpat-abc", "ghp_abc", "github_pat_abc", "${env:"
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def is_placeholder(match_str: str) -> bool:
|
|
44
|
+
match_lower = match_str.lower()
|
|
45
|
+
for placeholder in PLACEHOLDER_SUBSTRINGS:
|
|
46
|
+
if placeholder in match_lower:
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
# Check if match is mostly asterisks or single repeated char
|
|
50
|
+
cleaned = match_str.replace("'", "").replace('"', "").strip()
|
|
51
|
+
if not cleaned:
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
# Check if there are sequences of asterisks indicating masked values
|
|
55
|
+
if "*" in cleaned:
|
|
56
|
+
# e.g., glpat-*************
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
def get_repo_files(repo_path: Path):
|
|
62
|
+
try:
|
|
63
|
+
result = subprocess.run(
|
|
64
|
+
["git", "ls-files", "--cached", "--others", "--exclude-standard"],
|
|
65
|
+
cwd=str(repo_path),
|
|
66
|
+
capture_output=True,
|
|
67
|
+
text=True,
|
|
68
|
+
check=True
|
|
69
|
+
)
|
|
70
|
+
files = []
|
|
71
|
+
for line in result.stdout.splitlines():
|
|
72
|
+
if line.strip():
|
|
73
|
+
# Avoid files inside excluded directories
|
|
74
|
+
parts = Path(line.strip()).parts
|
|
75
|
+
if not any(part in EXCLUDED_DIRS for part in parts):
|
|
76
|
+
files.append(repo_path / line.strip())
|
|
77
|
+
return files
|
|
78
|
+
except Exception as e:
|
|
79
|
+
# Fallback to manual recursive scan
|
|
80
|
+
files = []
|
|
81
|
+
for root, dirs, walk_files in os.walk(str(repo_path)):
|
|
82
|
+
dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS and not d.startswith('.')]
|
|
83
|
+
for file in walk_files:
|
|
84
|
+
files.append(Path(root) / file)
|
|
85
|
+
return files
|
|
86
|
+
|
|
87
|
+
def scan_repository(repo_path: Path):
|
|
88
|
+
violations = []
|
|
89
|
+
files_to_scan = get_repo_files(repo_path)
|
|
90
|
+
|
|
91
|
+
for file_path in files_to_scan:
|
|
92
|
+
if not file_path.is_file():
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
# 1. Check root level naming constraints
|
|
96
|
+
if file_path.parent == repo_path:
|
|
97
|
+
# Check txt files
|
|
98
|
+
if file_path.suffix == ".txt":
|
|
99
|
+
if file_path.name.lower() not in ALLOWED_TXT_NAMES:
|
|
100
|
+
violations.append(
|
|
101
|
+
f"Non-standard root-level text file detected: '{file_path.name}'. Only 'requirements.txt' and 'requirements-dev.txt' are allowed."
|
|
102
|
+
)
|
|
103
|
+
# Check transient py files
|
|
104
|
+
elif file_path.suffix == ".py":
|
|
105
|
+
for pattern in TRANSIENT_PY_PATTERNS:
|
|
106
|
+
if pattern.match(file_path.name):
|
|
107
|
+
violations.append(
|
|
108
|
+
f"Transient/temporary script detected in root: '{file_path.name}'. Please move it to a subfolder or delete it."
|
|
109
|
+
)
|
|
110
|
+
break
|
|
111
|
+
|
|
112
|
+
# 2. Check for secrets
|
|
113
|
+
if file_path.suffix.lower() in EXCLUDED_EXTENSIONS:
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
if file_path.name == "security_sanitizer.py":
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
content = file_path.read_text(encoding="utf-8", errors="ignore")
|
|
121
|
+
lines = content.splitlines()
|
|
122
|
+
|
|
123
|
+
for idx, line in enumerate(lines, 1):
|
|
124
|
+
if any(bypass in line for bypass in ["# sanitizer:ignore", "# sanitizer-ignore", "# nosec"]):
|
|
125
|
+
continue
|
|
126
|
+
|
|
127
|
+
for label, pattern in SECRET_PATTERNS:
|
|
128
|
+
for match in pattern.findall(line):
|
|
129
|
+
match_str = match[0] if isinstance(match, tuple) else match
|
|
130
|
+
if not is_placeholder(match_str):
|
|
131
|
+
rel_path = file_path.relative_to(repo_path)
|
|
132
|
+
violations.append(
|
|
133
|
+
f"Potential unmasked secret ({label}) detected in {rel_path}:{idx}\n"
|
|
134
|
+
f" Line: {line.strip()}"
|
|
135
|
+
|
|
136
|
+
)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
return violations
|
|
141
|
+
|
|
142
|
+
def main():
|
|
143
|
+
repo_path = Path.cwd()
|
|
144
|
+
|
|
145
|
+
print("🔒 Running Security and Garbage Sanitizer...")
|
|
146
|
+
violations = scan_repository(repo_path)
|
|
147
|
+
|
|
148
|
+
if violations:
|
|
149
|
+
print("\n❌ SECURITY AND GARBAGE VALIDATION FAILED!")
|
|
150
|
+
print("Please correct the following issues before committing:")
|
|
151
|
+
for idx, violation in enumerate(violations, 1):
|
|
152
|
+
print(f"\n[{idx}] {violation}")
|
|
153
|
+
print("\nNote: To bypass secret checks on specific lines, append '# sanitizer:ignore' to the end of the line.")
|
|
154
|
+
sys.exit(1)
|
|
155
|
+
|
|
156
|
+
print("✅ All checks passed! No root garbage or unmasked secrets detected.")
|
|
157
|
+
sys.exit(0)
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|