T09 · Insecure Skill Coding Practices
- Location
scripts/get_pr_diff.py:65- Finding
GitLab access token can be disclosed to an attacker-controlled host
- Content
View full analysis
Vulnerability Details
File Location:
scripts/get_pr_diff.py, lines 65-78, 101-108, 128-134, 147, 163-168, and 191-207
Vulnerability Type: Credential disclosure through an untrusted network destination
Risk Level: HighVulnerable Code
python def get_gitlab_diff(project, pr_number, token=None, base_url="https://gitlab.com"): """Fetch an MR diff from the GitLab API.""" # URL encode project path project_encoded = project.replace("/", "%2F") url = f"{base_url}/api/v4/projects/{project_encoded}/merge_requests/{pr_number}/diffs" headers = {} if token: headers["PRIVATE-TOKEN"] = token req = urllib.request.Request(url, headers=headers) try: with urllib.request.urlopen(req, timeout=30) as response: diffs = json.loads(response.read().decode("utf-8"))The destination can be populated from a user-provided merge request URL:
python def parse_gitlab_url(url): """Parse a GitLab MR URL.""" # https://gitlab.com/group/project/-/merge_requests/123 match = re.match(r"https://([^/]+)/([^/]+)/([^/]+)/-/merge_requests/(\d+)", url) if match: base_url = f"https://{match.group(1)}" project = f"{match.group(2)}/{match.group(3)}" return base_url, project, int(match.group(4)) return NoneIt can also be derived from the current repository's remote:
python gitlab_match = re.match(r"(?:git@([^:]+):|https://([^/]+)/)([^/]+)/([^/]+?)(?:\.git)?$", remote_url) if gitlab_match: host = gitlab_match.group(1) or gitlab_match.group(2) return { "provider": "gitlab", "base_url": f"https://{host}", "project": f"{gitlab_match.group(3)}/{gitlab_match.group(4)}" }The environment token is then automatically attached to the request:
python gitlab_info = parse_gitlab_url(args.url) if gitlab_info: base_url, project, pr_number = gitlab_info token = args.token or os.environ.get("GITLAB_TOKEN") pri ...[truncated 2842 chars]- Remediation
View remediation
Remediation Suggestions
- Restrict authenticated requests to an explicit hostname allowlist. Default to
gitlab.comand reject other hosts unless the user has deliberately configured a trusted self-hosted instance. - Bind each credential to a specific hostname. For example, use a host-specific credential configuration rather than forwarding the generic
GITLAB_TOKENvariable to arbitrary instances. - Require explicit confirmation before transmitting credentials to a non-default GitLab host.
- Normalize and validate destinations with a URL parser. Require HTTPS, reject embedded credentials, unexpected ports, fragments, malformed hosts, and ambiguous hostnames.
- Disable automatic redirects for authenticated requests or implement redirect handling that removes authentication headers whenever the destination host changes.
- Do not infer authorization to send credentials solely from a repository's
originremote. Treat repository configuration as untrusted input. - Separate token variables for GitLab.com and self-hosted instances, such as a host-to-token mapping stored in protected configuration.
- Use narrowly scoped, short-lived tokens and document the minimum required read-only permissions.
- Add tests confirming that tokens are not sent to unapproved hosts or across cross-origin redirects.
- Restrict authenticated requests to an explicit hostname allowlist. Default to
