Back to skill

Security audit

Gitlab Manager

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent GitLab helper, but it uses a broad GitLab token and has weak input validation that could make authenticated changes to unintended GitLab resources.

Install only if you are comfortable giving the skill authenticated write access to GitLab. Prefer a narrowly scoped project or group token, avoid using a personal token with broad access, and verify command arguments before allowing it to create repositories, create issues, or post comments.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gitlab_api.js:44
Finding

Unvalidated CLI Input Enables GitLab API Route and Query Manipulation

Content
View full analysis

Vulnerability Details

File Location: scripts/gitlab_api.js, lines 44–57
Vulnerability Type: Unvalidated API path and query interpolation
Risk Level: Medium

Vulnerable Code

javascript
async function listMergeRequests(projectId, state = 'opened') {
  console.log(`Listing ${state} MRs for project ${projectId}...`);
  // If projectId is 'jorgermp/repo', we need to URL encode it or use ID
  const id = encodeURIComponent(projectId);
  const data = await request(`/projects/${id}/merge_requests?state=${state}`);
  console.log(JSON.stringify(data, null, 2));
}

async function commentOnMergeRequest(projectId, mrIid, body) {
  console.log(`Commenting on MR #${mrIid} in ${projectId}...`);
  const id = encodeURIComponent(projectId);
  const data = await request(`/projects/${id}/merge_requests/${mrIid}/notes`, 'POST', { body });
  console.log(`Comment added: ${data.web_url}`);
}

Technical Analysis

The mrIid command-line argument is inserted directly into the URL path without verifying that it is a positive integer. A value containing path traversal segments can therefore alter the intended GitLab API route. When fetch parses the resulting URL, dot segments such as .. are normalized before the request is sent.

For example, an mrIid of ../issues/1 produces the following URL path before normalization:

text
/projects/<project>/merge_requests/../issues/1/notes

URL normalization changes it to:

text
/projects/<project>/issues/1/notes

Consequently, a command presented as adding a merge-request comment can instead add a note to an issue.

The state argument is also concatenated directly into a query string without encoding or allowlist validation. An attacker can supply query delimiters and inject additional GitLab API query parameters, potentially changing the scope or semantics of the merge-request listing operation.

Attack Path

...[truncated 1300 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require merge-request IIDs to be positive integers before constructing the request:

    javascript
    function requirePositiveInteger(value, fieldName) {
      if (!/^[1-9]\d*$/.test(String(value))) {
        throw new Error(`${fieldName} must be a positive integer.`);
      }
      return String(value);
    }
    
    async function commentOnMergeRequest(projectId, mrIid, body) {
      const id = encodeURIComponent(projectId);
      const validatedIid = requirePositiveInteger(mrIid, 'mr_iid');
      const data = await request(
        `/projects/${id}/merge_requests/${validatedIid}/notes`,
        'POST',
        { body }
      );
      console.log(`Comment added: ${data.web_url}`);
    }
    
  2. Restrict merge-request state to GitLab's documented values:

    javascript
    const ALLOWED_MR_STATES = new Set(['opened', 'closed', 'merged', 'all']);
    
    function validateMrState(state) {
      if (!ALLOWED_MR_STATES.has(state)) {
        throw new Error('Invalid merge-request state.');
      }
      return state;
    }
    
  3. Construct query strings with URLSearchParams instead of direct interpolation:

    javascript
    const params = new URLSearchParams({
      state: validateMrState(state)
    });
    
    const data = await request(
      `/projects/${id}/merge_requests?${params.toString()}`
    );
    
  4. Validate that every required command argument is present before making a network request.

  5. Apply strict validation to every value used as a URL path segment, even where encoding is also performed.

  6. Use a token with the minimum GitLab permissions necessary and avoid broadly privileged personal access tokens where a narrower project or group access token is sufficient.

Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Credential Access

High
Category
Privilege Escalation
Confidence
88% confidence
Finding

The skill requires a GitLab Personal Access Token with broad api scope in the environment, which is a powerful credential that can create repositories, comment on merge requests, and create issues across whatever resources the token can access. In a skill context, encouraging broad token injection into the environment increases the blast radius if the script is modified, misused, or combined with unrestricted network capabilities.

Content

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

md
## Prerequisites

- **GITLAB_TOKEN**: A Personal Access Token with `api` scope must be set in the environment.

## Usage

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill exposes capabilities that require environment access and outbound network access, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this can lead to overbroad execution where the skill can access sensitive tokens and make external API calls without clear confinement or user-visible authorization expectations.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/gitlab_api.js:7