T09 · Insecure Skill Coding Practices
- Location
scripts/fetch_commits.py:43- Finding
GitLab Personal Access Token Can Be Sent to an Unvalidated or Plaintext Endpoint
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch_commits.py:43-44, 84-108, 150
Vulnerability Type: Unvalidated credential destination and insecure transport
Risk Level: HighVulnerable Code
python GITLAB_URL = os.getenv("GITLAB_URL", "https://gitlab.example.com") GITLAB_TOKEN = os.getenv("GITLAB_TOKEN", "")python def get_project_id(headers): """Get project ID""" encoded_path = urllib.parse.quote(PROJECT_PATH, safe="") url = f"{GITLAB_URL}/api/v4/projects/{encoded_path}" response = requests.get(url, headers=headers, timeout=10, proxies={"http": None, "https": None}) data = response.json() if "id" not in data: print(f"Error: {data.get('error', 'Unknown error')}") print(f"Response: {response.text[:200]}") return None return data["id"] def get_commits(project_id, headers, limit=50): """Get recent commits""" url = f"{GITLAB_URL}/api/v4/projects/{project_id}/repository/commits" response = requests.get( url, headers=headers, params={"per_page": limit, "ref_name": BRANCH}, timeout=10, proxies={"http": None, "https": None} ) return response.json() def get_commit_diff(project_id, commit_id, headers): """Get commit diff""" url = f"{GITLAB_URL}/api/v4/projects/{project_id}/repository/commits/{commit_id}/diff" response = requests.get(url, headers=headers, timeout=10, proxies={"http": None, "https": None}) return response.json()python headers = {"Private-Token": GITLAB_TOKEN}Technical Analysis
The script reads
GITLAB_URLfrom a workspace configuration file and uses it directly to construct all API request destinations. It does not validate the URL scheme, hostname, port, or origin before attaching the GitLab Personal Access Token in thePrivate-Tokenheader.A configured URL using plain HTTP transmits the token without transport encryption. ...[truncated 1770 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse
GITLAB_URLwithurllib.parse.urlparsebefore making any request. - Require the
httpsscheme by default. If HTTP is needed for development, restrict it to explicitly approved loopback addresses and require a separate opt-in setting. - Reject embedded credentials, fragments, malformed hosts, unexpected schemes, and ambiguous URLs.
- Normalize the approved base URL and ensure every API URL remains on the same origin.
- Disable automatic redirects for authenticated requests, or manually follow redirects only after confirming that the destination remains on the original HTTPS origin.
- Construct and attach the authentication header only after destination validation succeeds.
- Consider supporting an administrator-defined hostname allowlist.
- Document that users must supply only a minimally scoped, short-lived token.
- Parse
