Back to skill

Security audit

gitlab-skill

Security checks for vulnerabilities and agentic risk

Overview

This GitLab skill is mostly purpose-aligned, but it needs Review because its implementation can expose GitLab access tokens through insecure TLS, unvalidated hosts, and persistent local credential files.

Review before installing. Only use this in a controlled environment with a short-lived, least-privilege GitLab token, a trusted HTTPS GitLab host, and no shared terminal or CI logs. Avoid the clone helper, --insecure mode, token-bearing HTTPS remotes, and commands that print tokens or credential files until the implementation validates hosts, verifies TLS by default, redacts secrets, and securely handles credential file permissions.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/gitlab_api.py:124
Finding

Access Token Sent to Arbitrary Clone Hosts and Persisted in Git Metadata

Content
View full analysis
Dict: """Clone a GitLab repository to the specified directory""" parsed = urllib.parse.urlparse(repo_url) host = parsed.netloc or parsed.path.split('/')[0] if "://" in repo_url: path_parts = parsed.path.strip('/').rstrip('.git').split('/') project_path = '/'.join(path_parts) project_name = path_parts[-1] if path_parts else 'repo' else: match = re.match(r'[^@]+@([^:]+):(.+\.git)', repo_url) if match: host = match.group(1) project_path = match.group(2).rstrip('.git') project_name = project_path.split('/')[-1] else: return {"success": False, "error": "Invalid SSH URL format"} if target_dir is None: target_dir = Path.cwd() / project_name if target_dir.exists(): return {"success": False, "error": f"Directory already exists: {target_dir}"} clone_url = repo_url if repo_url.startswith('https://') and self.token: if '@' not in repo_url: auth_url = repo_url.replace( 'https://', f'https://oauth2:{self.token}@' ) clone_url = auth_url result = subprocess.run( ['git', 'clone', clone_url, str(target_dir)], capture_output=True, text=True, check=True ) ``` The resulting remote URL is subsequently retrieved: ```python result = subprocess.run( ['git', 'remote', 'get-url', 'origin'], cwd=repo_path, capture_output=True, text=True, check=True ) info["remote"] = result.stdout.strip() ``` The CLI prints the retrieved URL: ```python f"**Remote**: {info['remote']}\n" ``` ## ...[truncated 2256 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/gitlab_api.py:56
Finding

Authenticated GitLab API Requests Disable TLS Verification by Default

Content
View full analysis
Dict: """Make an authenticated request to the GitLab API""" if use_curl or self.insecure: return self._make_request_curl(endpoint, method, data) url = f"{self.host}/api/v4/{endpoint}" headers = {"PRIVATE-TOKEN": self.token} if data: headers["Content-Type"] = "application/json" req = urllib.request.Request(url, headers=headers, method=method) if data: req.data = json.dumps(data).encode("utf-8") with urllib.request.urlopen(req, context=self.ssl_context) as resp: body = resp.read().decode("utf-8") if body: return json.loads(body) return {} ``` ### Technical Analysis `ssl._create_unverified_context()` disables certificate-chain and hostname verification. The context is constructed unconditionally during initialization and is used for ordinary API requests. Consequently, the documented `--insecure` option does not represent the actual security boundary. Requests made without that option are still vulnerable to server impersonation. Because the `PRIVATE-TOKEN` header accompanies the request, a successful man-in-the-middle attack directly exposes the GitLab access token. The same client performs both read and write operations, including project, issue, merge-request, and branch creation, increasing the effect of forged responses and stolen credentials. ### Attack Path 1. The vict ...[truncated 1059 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/list_repos.py:23
Finding

Legacy Repository Listing Client Unconditionally Disables TLS Verification

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/credential_loader.py:82
Finding

Credential Loader Accepts Arbitrary and Plaintext API Host URLs

Content
View full analysis
bool: host = os.getenv("GITLAB_HOST") token = os.getenv("GITLAB_TOKEN") if not host or not token: return False if not self._is_valid_token(token): print("⚠️ Warning: GitLab token in environment appears to be a placeholder") return False self._credentials = { "host": host.rstrip("/"), "access_token": token } return True ``` User configuration hosts are handled in the same way: ```python host = config.get("host", "").rstrip("/") token = config.get("access_token", "") if not host or not token: return False self._credentials = { "host": host, "access_token": token } ``` Interactive input is also accepted without validating that it is a secure GitLab URL: ```python host = input( "GitLab Host URL (e.g., https://gitlab.example.com): " ).strip() if not host: print("Error: Host URL is required") sys.exit(1) import getpass token = getpass.getpass( "Access Token (input will be hidden): " ).strip() return { "host": host.rstrip("/"), "access_token": token } ``` ### Technical Analysis The loader validates only that a host string exists. It does not enforce HTTPS or reject: - Plaintext `http://` destinations - Embedded usernames or passwords - Query strings or fragments - Unexpected URL paths - Unsupported schemes - Attacker-controlled destinations introduced through environment or configuration changes Downstream code appends `/api/v4/...` and attaches the private token. Therefore, an incorrectly configured or maliciously modified host can become the destination for sensitive authenticated requests. ### Attack Path 1. An ...[truncated 891 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/credential_loader.py:101
Finding

Credential Files Are Loaded Without Ownership or Permission Validation

Content
View full analysis
bool: if not self.USER_CONFIG_PATH.exists(): return False try: with open(self.USER_CONFIG_PATH, "r", encoding="utf-8") as f: config = json.load(f) except (json.JSONDecodeError, IOError) as e: print(f"⚠️ Warning: Failed to read user config: {e}") return False host = config.get("host", "").rstrip("/") token = config.get("access_token", "") if not host or not token: return False self._credentials = { "host": host, "access_token": token } return True ``` The legacy project-local configuration is also read without security checks: ```python def _try_legacy_config(self) -> bool: if not self.LEGACY_CONFIG_PATH.exists(): return False try: with open(self.LEGACY_CONFIG_PATH, "r", encoding="utf-8") as f: config = json.load(f) except (json.JSONDecodeError, IOError): return False ``` ### Technical Analysis Newly saved credentials are assigned mode `0600`, but existing credential files are trusted without verifying: - File ownership - Group or world access permissions - Whether the path is a symbolic link - Whether the legacy project directory is shared or writable by another user A credential file with mode `0644`, for example, remains silently usable even though other local users can read the token. The project-local legacy fallback also encourages storing a secret inside the Skill directory, where repository sharing or accidental commits are more likely. ### Attack Path 1. A user creates or edits `~/.claude/gitlab_config.json` with permissive permissions, or places a real token in `scripts/config.json`. 2. The loader reads the file without warning or rejection. 3. Anoth ...[truncated 649 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/credential_loader.py:303
Finding

Credential Utility Prints Token Prefixes and Creates a World-Readable Template

Content
View full analysis
None: config_path = CredentialManager.USER_CONFIG_PATH config_path.parent.mkdir(parents=True, exist_ok=True) template = { "_comment": "GitLab credentials configuration", "host": "https://gitlab.example.com", "access_token": "glpat-your-token-here" } with open(config_path, "w", encoding="utf-8") as f: json.dump(template, f, indent=2) config_path.chmod(0o644) print(f"✅ Created template config at: {config_path}") print(" Please edit this file and add your real credentials") print(" Then run: chmod 600 " + str(config_path)) ``` The diagnostic command prints part or all of the loaded token: ```python print(f"✅ Credentials loaded from: {source}") print(f" Host: {creds['host']}") print(f" Token: {creds['access_token'][:10]}..." if len(creds['access_token']) > 10 else f" Token: {creds['access_token']}") ``` ### Technical Analysis The template workflow creates a file with mode `0644` and then instructs the user to add the real token before separately changing permissions. If the user forgets the final command, the credential remains readable by other local users. The source-diagnostic command prints the first ten characters of long tokens and the entire value of short tokens. Even partial secret disclosure is unnecessary and can expose token format, identifiers, or distinguishing material in terminal recordings, CI logs, and Agent transcripts. It also contradicts `SKILL.md`, which directs users not to print or log access tokens. ### Attack Path **Template exposure path:** 1. The user runs the template-creation command. 2. The utility cr ...[truncated 881 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (45)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches the declared GitLab-focused purpose and interacts only with GitLab APIs and local git as expected. It supports both API and git operations, including repository creation and cloning, project listing, issue creation/retrieval/listing, MR listing/creation, branch listing/creation, commit listing, and pipeline listing. However, the description explicitly says the skill should be used for 'creating/updating issues and MRs,' while this code provides no update methods for issues or merge requests and no CLI commands for those updates. That is a concrete description-to-behavior mismatch, though the overall domain and primary purpose remain aligned.

Content

No source excerpt is available for this finding.

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 29)May include surrounding context.

md
- Standard practice for production systems
- Works seamlessly with CI/CD pipelines

**Generating a GitLab Access Token:**
1. Go to GitLab → User Settings → Access Tokens
2. Create a personal access token with appropriate scopes:
   - `api` - Full API access

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/credential_loader.py (reported line 208)May include surrounding context.

python
- Standard practice for production systems
- Works seamlessly with CI/CD pipelines

**Generating a GitLab Access Token:**
1. Go to GitLab → User Settings → Access Tokens
2. Create a personal access token with appropriate scopes:
   - `api` - Full API access

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 30)May include surrounding context.

md
- Works seamlessly with CI/CD pipelines

**Generating a GitLab Access Token:**
1. Go to GitLab → User Settings → Access Tokens
2. Create a personal access token with appropriate scopes:
   - `api` - Full API access
   - `read_repository` - For repository operations

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

The guidance recommends broad scopes including full api access without emphasizing least privilege or narrower alternatives. Over-scoped personal access tokens materially increase blast radius if the token is exposed elsewhere in the workflow.

Content

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

md
**Generating a GitLab Access Token:**
1. Go to GitLab → User Settings → Access Tokens
2. Create a personal access token with appropriate scopes:
   - `api` - Full API access
   - `read_repository` - For repository operations
   - `write_repository` - For creating branches, commits, etc.

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
88% confidence
Finding

The skill instructs writing secrets into the agent's home configuration directory under ~/.claude, which gives the skill workflow access to a sensitive, persistent area that may be shared across agent runs. In a hostile or misconfigured environment, this can enable credential harvesting, cross-skill leakage, or persistence of sensitive material beyond the immediate task.

Content

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

bash
mkdir -p ~/.claude
cat > ~/.claude/gitlab_config.json << 'EOF'
{
  "host": "https://gitlab.example.com",
  "access_token": "glpat-your-token-here"

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 110)May include surrounding context.

md
**DON'T:**
❌ Commit credentials to version control
❌ Store real tokens in `scripts/config.json`
❌ Share access tokens in chat logs or output
❌ Use the same token across multiple environments
❌ Print or log access tokens

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 112)May include surrounding context.

md
**DON'T:**
❌ Commit credentials to version control
❌ Store real tokens in `scripts/config.json`
❌ Share access tokens in chat logs or output
❌ Use the same token across multiple environments
❌ Print or log access tokens

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Instructing users to read ~/.claude/gitlab_config.json directly encourages displaying persistent secrets from a sensitive agent configuration path. This can leak tokens into terminal output, transcripts, logs, or shared debugging sessions.

Content

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

md
**Solutions:**
1. Check environment variables: `echo $GITLAB_HOST $GITLAB_TOKEN`
2. Check user config: `cat ~/.claude/gitlab_config.json`
3. Verify token is valid and not expired
4. Ensure token has required API scopes

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The clone workflow normalizes using the access token from config for repository authentication, and in nearby guidance it suggests inline HTTPS token usage. In a skill with shell and file capabilities, handling long-lived tokens during clone operations meaningfully raises exposure risk if commands, remotes, or config are persisted or echoed.

Content

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

md
1. Parse the repository URL to extract the project path and host
2. Verify the host matches the configured GitLab instance in `config.json`
3. If authentication is required for private repositories, use the access token from config
4. Clone to the specified directory or current directory if not specified
5. Use git clone with appropriate authentication:
   - For HTTPS: use token as password

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/credential_loader.py (reported line 234)May include surrounding context.

python
Args:
            host: GitLab host URL
            token: Access token
        """
        # Create .claude directory if it doesn't exist
        self.USER_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
89% confidence
Finding

This script allows potentially unsafe tool behavior in two ways: it accepts an arbitrary host value and supports --insecure, which disables TLS certificate verification. In the context of a GitLab skill handling access tokens, this can enable credential disclosure or man-in-the-middle interception if a user is tricked into using an attacker-controlled host or bypassing certificate validation.

Content

Scanner excerpt · scripts/fetch_repos.py (reported line 29)May include surrounding context.

python
def fetch_projects(host, token, per_page=100, insecure=False):
    """Fetch projects using curl

    Args:
        host: GitLab host URL

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 421)May include surrounding context.

md
Args:
        host: GitLab host URL
        token: GitLab access token
        per_page: Number of results per page
        insecure: If True, bypass SSL certificate verification

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 498)May include surrounding context.

md
Args:
        host: GitLab host URL
        token: GitLab access token
        per_page: Number of results per page
        insecure: If True, bypass SSL certificate verification

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/credential_loader.py (reported line 211)May include surrounding context.

python
Args:
        host: GitLab host URL
        token: GitLab access token
        per_page: Number of results per page
        insecure: If True, bypass SSL certificate verification

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/fetch_repos.py (reported line 33)May include surrounding context.

python
Args:
        host: GitLab host URL
        token: GitLab access token
        per_page: Number of results per page
        insecure: If True, bypass SSL certificate verification

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/list_repos.py (reported line 44)May include surrounding context.

python
Args:
        host: GitLab host URL
        token: GitLab access token
        per_page: Number of results per page
        insecure: If True, bypass SSL certificate verification

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
91% confidence
Finding

The curl-based fallback broadens the tool's capability to make authenticated requests using attacker-influenced parameters while also supporting insecure TLS mode. In this skill context, that is more dangerous because the tool is explicitly designed to perform high-privilege GitLab actions; prompt-driven misuse can turn it into a general authenticated request primitive against the configured GitLab instance, with token exposure risks compounded by subprocess invocation.

Content

Scanner excerpt · scripts/gitlab_api.py (reported line 237)May include surrounding context.

python
return info

    def _make_request_curl(self, endpoint: str, method: str = "GET", data: Optional[Dict] = None) -> Dict:
        """Make an authenticated request to the GitLab API using curl subprocess

        This method is useful as a fallback when Python's SSL handling fails with self-signed certificates.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill describes capabilities involving environment access, file reads/writes, network access, and shell/git execution, but declares no explicit tool scope or permission boundaries. In an agent setting, this increases the chance of over-broad execution and makes dangerous actions like credential access, filesystem modification, and network operations easier to trigger without clear constraints.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
78% confidence
Finding

The skill encourages use of long-lived personal access tokens with broad scopes, which creates persistent session-like access beyond a single task. If such a token is leaked through the documented debugging or clone practices, an attacker may retain ongoing API and repository access until rotation or revocation.

Content

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

md
**Generating a GitLab Access Token:**
1. Go to GitLab → User Settings → Access Tokens
2. Create a personal access token with appropriate scopes:
   - `api` - Full API access
   - `read_repository` - For repository operations
   - `write_repository` - For creating branches, commits, etc.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

} EOF

chmod 600 ~/.claude/gitlab_config.json # Restrict file permissions

text

**Location:** `~/.claude/gitlab_config.json`

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

} EOF

chmod 600 ~/.claude/gitlab_config.json # Restrict file permissions

text

**Location:** `~/.claude/gitlab_config.json`

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
**DO:**
✅ Use environment variables for production systems
✅ Store user config in `~/.claude/gitlab_config.json`
✅ Restrict file permissions: `chmod 600 ~/.claude/gitlab_config.json`
✅ Use different tokens for different environments
✅ Rotate tokens regularly

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/credential_loader.py (reported line 323)May include surrounding context.

python
**DO:**
✅ Use environment variables for production systems
✅ Store user config in `~/.claude/gitlab_config.json`
✅ Restrict file permissions: `chmod 600 ~/.claude/gitlab_config.json`
✅ Use different tokens for different environments
✅ Rotate tokens regularly

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The troubleshooting section explicitly contradicts earlier guidance by telling users to print credentials and use a curl command that includes the token-bearing environment variable. This materially increases the chance of accidental disclosure in shell history, terminal logging, CI logs, and screenshots.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.