T09 · Insecure Skill Coding Practices
- Location
scripts/jira.mjs:77- Finding
Unrestricted Jira Host Allows Credential and Data Transmission over Plaintext HTTP
- Content
View full analysis
Vulnerability Details
File Location:
scripts/jira.mjs, lines 27 and 77–102; the same unsafe destination is used for attachment uploads at lines 477–485
Vulnerability Type: Insufficient validation of a sensitive network destination and transport
Risk Level: HighVulnerable Code:
js const host = process.env.JIRA_HOST; const email = process.env.JIRA_EMAIL; const apiToken = process.env.JIRA_API_TOKEN; function authHeader() { const token = Buffer.from(`${CFG.email}:${CFG.apiToken}`).toString('base64'); return `Basic ${token}`; } function baseUrl() { const host = CFG.host.replace(/\/+$/, ''); const prefix = host.startsWith('http') ? host : `https://${host}`; return `${prefix}/rest/api/3`; } async function jiraFetch(path, options = {}, retries = 3) { const url = path.startsWith('http') ? path : `${baseUrl()}${path}`; const headers = { 'Authorization': authHeader(), 'Accept': 'application/json', ...options.headers, }; if (!(options.body instanceof FormData)) { headers['Content-Type'] = headers['Content-Type'] || 'application/json'; } for (let attempt = 1; attempt <= retries; attempt++) { const resp = await fetch(url, { ...options, headers }); // ... } }The attachment upload uses the same unchecked base URL and credential:
js const url = `${baseUrl()}/issue/${options.key}/attachments`; const resp = await fetch(url, { method: 'POST', headers: { 'Authorization': authHeader(), 'X-Atlassian-Token': 'no-check', }, body: form, });Technical Analysis
JIRA_HOSTis taken directly from the environment without parsing or validating its protocol, hostname, port, path, query, or fragment. If it starts with the charactershttp,baseUrl()accepts it unchanged. This includes plaintexthttp://URLs and arbitrary non-Atlassian servers....[truncated 2949 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse
JIRA_HOSTwithnew URL()rather than using string-prefix checks. - Require
url.protocol === "https:"; reject plaintext HTTP under all circumstances. - For the declared Jira Cloud use case, restrict the destination to the expected tenant hostname or an explicit allowlist, such as a validated subdomain of
atlassian.net. - Reject embedded usernames or passwords, unexpected ports, paths, query strings, and fragments in
JIRA_HOST. - Construct API URLs from validated URL components rather than string concatenation.
- Remove support for absolute URLs in
jiraFetch(), or separately validate absolute destinations and never attach authorization credentials to a different origin. - Set
redirect: "error"or manually process redirects while ensuring theAuthorizationheader is never forwarded across origins. - Apply the same centralized validated HTTP client to attachment uploads instead of using a separate direct
fetch()call. - Fail closed with a clear configuration error when host validation fails.
- Document that users should create a least-privilege Jira API token and rotate it immediately if an untrusted or plaintext host may have been used.
- Add automated tests covering
http://, attacker-controlled domains, deceptive hostnames, embedded credentials, unusual ports, paths, and cross-origin redirects.
- Parse
