T09 · Insecure Skill Coding Practices
- Location
scripts/openproject.mjs:75- Finding
OpenProject API Token May Be Transmitted over Plaintext HTTP
- Content
View full analysis
Vulnerability Details
File Location:
scripts/openproject.mjs, lines 75–100
Vulnerability Type: Plaintext transmission of credentials
Risk Level: HighVulnerable Code
js function authHeader() { // OpenProject uses Basic auth with 'apikey' as username const token = Buffer.from(`apikey:${CFG.apiToken}`).toString('base64'); return `Basic ${token}`; } function baseUrl() { const host = CFG.host.replace(/\/+$/, ''); const prefix = host.startsWith('http') ? host : `https://${host}`; return `${prefix}/api/v3`; } async function opFetch(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 });Technical Analysis
The
baseUrl()function accepts anOP_HOSTvalue beginning with eitherhttp://orhttps://because it preserves any value that starts withhttp. Every API request then includes the OpenProject API token in an HTTP BasicAuthorizationheader.Base64 encoding does not provide confidentiality. If
OP_HOSTspecifies a plaintext HTTP endpoint, the API token, request payloads, and API responses are transmitted without transport encryption. This is especially relevant because the project explicitly supports self-hosted instances, where users may configure an internal HTTP endpoint.The same transport issue also affects the direct attachment-upload requests because they use
baseUrl()and attach the same authorization header.Attack Path
- A user or agent configures a self-hosted instance using an address such as
OP_HOST=http://openproject.internal. - The CLI preserves the plaintext HTTP scheme w ...[truncated 1598 chars]
- A user or agent configures a self-hosted instance using an address such as
- Remediation
View remediation
Remediation Suggestions
- Parse
OP_HOSTusing the standard URL parser and reject every protocol except HTTPS:
js function baseUrl() { let url; try { url = new URL(CFG.host.includes('://') ? CFG.host : `https://${CFG.host}`); } catch { throw new Error('OP_HOST must be a valid OpenProject URL'); } if (url.protocol !== 'https:') { throw new Error('OP_HOST must use HTTPS to protect the API token'); } url.pathname = `${url.pathname.replace(/\/+$/, '')}/api/v3`; return url.toString().replace(/\/$/, ''); }-
If plaintext HTTP is required for local development, require an explicit option such as
OP_ALLOW_INSECURE_HTTP=trueand permit it only for loopback addresses such as127.0.0.1,::1, orlocalhost. Emit a prominent warning when this mode is active. -
Do not treat arbitrary strings beginning with
httpas valid URLs. Validate the scheme, hostname, optional port, username, password, and path explicitly. -
Ensure all request paths remain relative to the validated OpenProject origin. Avoid accepting absolute URLs in
opFetch()unless they are separately validated as same-origin, preventing future callers from accidentally forwarding the authorization header to another host. -
Apply the same validated HTTPS origin to all direct upload requests for work-package, meeting, and wiki attachments.
-
Update
README.mdandSKILL.mdto state that remote and production OpenProject instances must use HTTPS with valid certificate verification. -
After deployment of the fix, revoke and replace any API token that may previously have been used with an HTTP endpoint.
- Parse
