T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/jira.py:24
- Finding
- Jira credentials can be transmitted to an arbitrary or plaintext destination<![CDATA[ ## Vulnerability Details **File Location**: `scripts/jira.py`, lines 24–50 **Vulnerability Type**: Unvalidated credential destination and insecure transport **Risk Level**: Medium ### Vulnerable Code ```python base_url = os.environ.get("JIRA_BASE_URL", "").rstrip("/") email = os.environ.get("JIRA_USER_EMAIL", "") token = os.environ.get("JIRA_API_TOKEN", "") if not all([base_url, email, token]): missing = [] if not base_url: missing.append("JIRA_BASE_URL") if not email: missing.append("JIRA_USER_EMAIL") if not token: missing.append("JIRA_API_TOKEN") print(f"Error: Missing environment variables: {', '.join(missing)}", file=sys.stderr) sys.exit(1) return base_url, email, token def make_request(method, endpoint, data=None): """Make authenticated request to Jira API.""" base_url, email, token = get_config() url = f"{base_url}{endpoint}" import base64 auth = base64.b64encode(f"{email}:{token}".encode()).decode() headers = { "Authorization": f"Basic {auth}", "Accept": "application/json", } ``` The request containing this header is subsequently sent at lines 57–60: ```python req = Request(url, data=body, headers=headers, method=method) try: with urlopen(req) as resp: ``` ### Technical Analysis The script obtains `JIRA_BASE_URL` directly from the environment and concatenates it with an API endpoint without validating its scheme, hostname, port, or origin. It then sends the Jira user email and API token in an HTTP Basic Authorization header to the resulting URL. Base64 encoding is the encoding required by HTTP Basic authentication; it is not encryption. Anyone who receives the header can trivially recover the email and token. The encoded value is not printed to stdout by this script, so the pre-scan warning about direct encoded-secret output is not confirmed. Network transmission of credentials is nece ...[truncated 2021 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse `JIRA_BASE_URL` with `urllib.parse.urlsplit` before using it. 2. Require the `https` scheme and reject plaintext HTTP. 3. Reject URLs containing user information, query strings, or fragments. 4. Restrict the hostname to the organization's explicitly configured Jira tenant. If appropriate for the deployment, permit only a specific `*.atlassian.net` hostname rather than every subdomain. 5. Reject unexpected ports and malformed hostnames. 6. Ensure authentication headers are never forwarded to a different origin during redirects. Prefer disabling redirects for authenticated API calls or validating every redirect target before following it. 7. Keep the API token in a protected secret store or narrowly scoped environment variable, and provision a token with only the Jira scopes required by the intended operations. 8. Avoid logging the Authorization header, email/token pair, environment contents, or complete request objects. 9. Add automated tests confirming that HTTP URLs, attacker-controlled hosts, cross-origin redirects, URLs containing user information, and malformed URLs are rejected. A hardened configuration check should follow this pattern: ```python from urllib.parse import urlsplit def validate_base_url(value): parsed = urlsplit(value) if parsed.scheme != "https": raise ValueError("JIRA_BASE_URL must use HTTPS") if not parsed.hostname: raise ValueError("JIRA_BASE_URL must contain a hostname") if parsed.username or parsed.password: raise ValueError("JIRA_BASE_URL must not contain user information") if parsed.query or parsed.fragment: raise ValueError("JIRA_BASE_URL must not contain a query or fragment") if parsed.hostname != "yourcompany.atlassian.net": raise ValueError("JIRA_BASE_URL is not an approved Jira tenant") if parsed.port not in (None, 443): raise ValueError("JIRA_BASE_URL uses an unexpected port") return value.rstrip("/") ` ...[truncated 8 chars]
