Back to skill

Security audit

GitLab Batch Cloner

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does the GitLab cloning and indexing it advertises, but it handles GitLab tokens and bulk local changes in risky, under-disclosed ways.

Review before installing. Use only a short-lived, least-privilege GitLab token; avoid running on shared machines; prefer fixing TLS verification and git authentication before use; set a bounded local directory and total timeout; and treat the generated Excel file as containing untrusted GitLab metadata.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/clone_and_index.py:79
Finding

GitLab API token transmitted without TLS certificate verification

Content
View full analysis
list | dict: """GET request to GitLab API. Returns parsed JSON.""" url = f"{gitlab_url}/api/v4{path}" if params: url += "?" + urllib.parse.urlencode(params) req = urllib.request.Request(url, headers={"PRIVATE-TOKEN": token}) try: with urllib.request.urlopen(req, context=_SSL_CTX, timeout=30) as resp: ``` ### Technical Analysis The global SSL context disables both certificate-chain validation and hostname verification. Every GitLab API call uses this context while sending the personal access token in the `PRIVATE-TOKEN` request header. Consequently, the client cannot authenticate the GitLab server. A network-positioned adversary can present an arbitrary certificate without triggering an error. This defeats HTTPS server authentication and permits interception or modification of API traffic. The recursive subgroup and project requests are necessary for the declared batch-cloning functionality and are scoped to user-selected groups. The vulnerability is not the API access itself, but the insecure transport configuration used for all such access. ### Attack Path 1. The user supplies a token with the documented `read_api` and `read_repository` scopes. 2. The Skill connects to the configured GitLab URL over a network controlled or observable by an attacker. 3. The attacker intercepts the connection and presents an untrusted certificate. 4. Because certificate and hostnam ...[truncated 952 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/clone_and_index.py:251
Finding

Personal access token exposed in Git clone process arguments

Content
View full analysis
None: """Clone a project via HTTPS with embedded token, then strip token from remote. Uses start_new_session + process-group kill to avoid orphan git processes.""" url_with_token = http_url.replace("https://", f"https://oauth2:{token}@") proc = subprocess.Popen( ["git", "clone", "--quiet", url_with_token, target_dir], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, start_new_session=True, ) try: proc.communicate(timeout=CLONE_TIMEOUT) except subprocess.TimeoutExpired: try: os.killpg(os.getpgid(proc.pid), signal.SIGKILL) except OSError: proc.kill() proc.wait() # Always try to strip token from remote (even if clone partially failed) _sanitize_remote(target_dir, http_url) raise # Always try to strip token from remote (even if clone partially failed) _sanitize_remote(target_dir, http_url) ``` ### Technical Analysis The token is embedded in the HTTPS URL supplied as an argument to `git clone`. Although the code resets the repository's remote URL after cloning, this cleanup only addresses the credential persisted in `.git/config`. It does not protect the token while the Git process is running. Command-line arguments may be visible through process inspection facilities, process-monitoring software, endpoint security products, audit logs, debugging tools, or crash reports. Parallel cloning increases the number of token-bearing processes that can exist simultaneously. Using an argument list rather than `shell=True` correctly avoids shell injection, but it does not prevent credential disclosure through the proc ...[truncated 1037 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/clone_and_index.py:43
Finding

Unpinned dependency installed automatically during script import

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/clone_and_index.py:482
Finding

GitLab-controlled metadata is written to Excel without formula neutralization

Content
View full analysis
list: """Convert a project data dict to a row list matching HEADERS_CN.""" return [ item["main_group"], item["sub_group"], item["project_path"], item["project_name"], item["description"], item["branches"], item["times"], item["ssh_url"], item["download_time"], item.get("project_id", ""), ] ``` The resulting values are appended directly to worksheets: ```python for item in items: ws.append(_item_to_row(item)) ``` The external description source is assigned without sanitization in the project worker: ```python description = project_info.get("description", "") or "" ``` ### Technical Analysis Project names, paths, descriptions, branch names, and repository URLs originate from GitLab project metadata or repositories. These strings are transferred directly into XLSX cells. Spreadsheet applications and `openpyxl` can interpret strings beginning with formula markers—particularly `=`—as formulas rather than inert text. Values beginning with `+`, `-`, or `@` may also be interpreted specially by some spreadsheet clients. An attacker who can modify metadata for a project visible to the supplied token can place a formula-like value in a field such as the project description. The Skill then creates a workbook containing that value without neutralization. ### Attack Path 1. An attacker creates or modifies a GitLab project accessible to the auditing user's token. 2. The attacker places a spreadsheet formula payload in project metadata, such as the description. 3. The user runs the Skill for a group containing that project. 4. The Skill retrieves the attacker-controlled metadata and appends it directly to `01.Index.xlsx`. 5. The user opens ...[truncated 834 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 24)May include surrounding context.

md
| Parameter | Required | Default | Notes |
|-----------|----------|---------|-------|
| GitLab URL | ✅ | — | e.g. `https://gitlab.company.com` |
| Personal Access Token | ✅ | — | Needs `read_api` + `read_repository` scopes |
| Target Group(s) | ✅ | — | Group/sub-group/project paths, comma-separated. Supports: top-level group (`myGroup`), sub-group path (`myGroup/mySubGroup`), or direct project path (`myGroup/mySubGroup/my-project`) |
| Local Storage Path | ❌ | `~/Desktop/Code` | Where repos are stored |
| Auth Method | ❌ | HTTPS+Token | Or SSH if key is configured |

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/clone_and_index.py (reported line 18)May include surrounding context.

python
Environment variables:
    GITLAB_URL       - GitLab instance URL (required)
    GITLAB_TOKEN     - Personal access token (required)
    GITLAB_GROUPS    - Comma-separated group/sub-group/project paths (required)
    GITLAB_BASE_DIR  - Local storage path (default: ~/Desktop/Code)
    GITLAB_MODE      - "clone" (default), "update" (skip clone, only pull),

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Disabling TLS validation without explicit warning makes the authenticated API traffic susceptible to interception while hiding that risk from the operator. Because the script sends a private token on every request, this materially raises the chance of credential theft and tampered project metadata.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill declares behavior that requires environment variables, network access, and shell execution, but it does not explicitly scope or constrain those capabilities. In an agent setting, missing tool-scope declarations weakens least-privilege controls and can cause the skill to run with broader access than users or orchestrators expect.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The workflow causes large-scale filesystem changes by cloning or updating many repositories and repeatedly writing an Excel index, but the skill does not clearly warn about the breadth and persistence of those side effects before execution. This can lead to unintended local data modification, disk consumption, and overwriting or mixing content in the chosen base directory.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Runtime package installation is outside the narrow operational scope of cloning repositories and generating an index, and it introduces unnecessary code-execution and supply-chain exposure. An attacker who can influence package sources, indexes, TLS interception, or the environment may gain a path to execute untrusted code during setup.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
91% confidence
Finding

Automatically installing a package with pip at runtime causes the script to execute additional network-enabled code and modify the host environment beyond its stated cloning/indexing purpose. This expands supply-chain and integrity risk, especially in enterprise or restricted environments where dependency installation should be explicit and controlled.

Content

Scanner excerpt · scripts/clone_and_index.py (reported line 50)May include surrounding context.

python
from openpyxl.styles import Font, Alignment, Border, Side
except ImportError:
    print("[setup] Installing openpyxl ...")
    subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl", "-q"])
    import openpyxl
    from openpyxl.styles import Font, Alignment, Border, Side

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

The default global timeout is disabled, allowing the batch operation to run without an overall execution bound. In a large or adversarial repository set, this can cause prolonged resource consumption, excessive network/disk use, and operational denial-of-service on the host or shared runner.

Content

Scanner excerpt · scripts/clone_and_index.py (reported line 62)May include surrounding context.

python
GIT_CMD_TIMEOUT = 60      # 1 minute for lightweight git commands
API_PER_PAGE = 100
INCREMENTAL_EXCEL_BATCH = 50  # Write Excel every N completed projects
DEFAULT_TOTAL_TIMEOUT = 0     # 0 = no global timeout; override via GITLAB_TOTAL_TIMEOUT

HEADERS_CN = [
    "主Group名称", "子Group路径", "Project路径", "Project名称",

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The generated Excel output uses fixed Chinese column names, which imposes a specific language on users regardless of preference or locale. The file does not indicate that this is region-specific or provide any opt-in or configuration for alternate languages.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script globally disables TLS certificate validation and hostname checking for all GitLab API requests. That enables man-in-the-middle interception or modification of API responses and can expose the GitLab token to attackers on the network, which is especially dangerous because the tool performs authenticated enumeration of repositories.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code sends the GITLAB_TOKEN in HTTP headers to a remote GitLab instance, which is a sensitive credential transmission. Although the module docstring documents the environment variable, there is no user-facing warning at the operation site about network transmission of credentials or repository metadata.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/clone_and_index.py (reported line 230)May include surrounding context.

python
def _run_git(args: list[str], cwd: str, timeout: int = GIT_CMD_TIMEOUT) -> subprocess.CompletedProcess:
    """Run a git command with timeout.  Uses a new session so the entire
    process group can be killed on timeout (prevents orphan git-remote-https)."""
    proc = subprocess.Popen(
        ["git"] + args,
        cwd=cwd,
        stdout=subprocess.PIPE,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The clone flow embeds the access token in the repository URL and provides no clear operator warning about that sensitive handling. Even if the remote is sanitized afterward, the secret may be visible during execution to local users, monitoring agents, shell history substitutes, or diagnostic artifacts.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
89% confidence
Finding

The script embeds the GitLab personal access token directly into the clone URL passed to git. Although it later resets the remote URL, the secret is still exposed to child-process arguments during execution and may leak via process listings, debugging tools, logs, crash reports, or other local telemetry.

Content

Scanner excerpt · scripts/clone_and_index.py (reported line 255)May include surrounding context.

python
"""Clone a project via HTTPS with embedded token, then strip token from remote.
    Uses start_new_session + process-group kill to avoid orphan git processes."""
    url_with_token = http_url.replace("https://", f"https://oauth2:{token}@")
    proc = subprocess.Popen(
        ["git", "clone", "--quiet", url_with_token, target_dir],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

This function creates or overwrites the Excel index file, and elsewhere the script also clones repositories into the configured base directory. While the script prints status messages, it does not clearly warn beforehand that it will write many repositories locally and replace an existing index file.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_clone_and_index.py (reported line 473)May include surrounding context.

python
tmpdir = tempfile.mkdtemp()
        try:
            # Use a git command that will hang: init a repo first, then run a slow command
            subprocess.run(["git", "init", tmpdir], capture_output=True)
            with self.assertRaises(subprocess.TimeoutExpired):
                # 'git log' on empty repo is fast, so use a sleep trick via GIT_TRACE
                # Instead, just call _run_git with a very short timeout on a command that takes time

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The Excel specification hard-codes Chinese field names such as 主Group名称, 子Group路径, and 下载时间. This is a natural-language locale choice imposed by the skill, and the file does not indicate user opt-in or a region-specific justification for requiring Chinese output.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.