T09 · Insecure Skill Coding Practices
- Location
ait.mjs:247- Finding
Bearer API Key Disclosure Through an Unvalidated Configurable API Endpoint
- Content
View full analysis
Vulnerability Details
File Location:
ait.mjs:247-273
Vulnerability Type: Unvalidated credential destination and plaintext transport exposure
Risk Level: MediumVulnerable Code
js function baseUrl(ctx) { return (ctx.env.AUTOMATE_IT_API_URL || DEFAULT_API_URL).replace(/\/+$/, ""); } function apiKey(ctx) { const key = ctx.env.AUTOMATE_IT_API_KEY; if (!key) { throw new CliError( "AUTOMATE_IT_API_KEY is not set. Create an API key in Automate It (Profile → API keys) and export it." ); } return key; } async function mcpRequest(ctx, method, params) { const url = `${baseUrl(ctx)}/mcp`; let res; try { res = await ctx.fetchFn(url, { method: "POST", headers: { Authorization: `Bearer ${apiKey(ctx)}`, "Content-Type": "application/json", Accept: "application/json, text/event-stream", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }), signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), });Technical Analysis
The CLI accepts
AUTOMATE_IT_API_URLas an unrestricted API base URL. The value is only normalized by removing trailing slashes; its scheme, hostname, port, and trust relationship are not validated.Every MCP request then attaches
AUTOMATE_IT_API_KEYas a bearer credential in theAuthorizationheader. As a result, any party capable of influencing the process environment can redirect authenticated requests to an attacker-controlled endpoint. The implementation also accepts anhttp://URL, allowing the credential and request content to travel without transport encryption.Sending workspace data to the Automate It API is necessary for the Skill's declared remote task-management and publishing functionality. However, sending the bearer credential to an arbitrary destination is not required for ordinary production use and exceeds a safe le ...[truncated 2013 chars]
- Remediation
View remediation
Remediation Suggestions
-
Require encrypted transport
- Parse the configured endpoint with the standard
URLclass. - Reject every scheme other than
https:. - Permit plaintext HTTP only under a clearly named development-only option, and never send production credentials through that mode.
- Parse the configured endpoint with the standard
-
Restrict credential destinations
- Allowlist
api.automate.it.comfor normal production operation. - If private or self-hosted endpoints are required, use a separate explicit configuration containing an administrator-approved hostname allowlist.
- Reject URLs containing embedded user information or unexpected ports.
- Allowlist
-
Require explicit trust for custom endpoints
- Treat a non-default endpoint as a sensitive security decision rather than an ordinary environment override.
- Require an explicit option such as
AUTOMATE_IT_ALLOW_CUSTOM_API_HOST=true. - Display the normalized destination hostname before transmitting credentials, without printing the API key.
-
Reduce credential impact
- Continue recommending narrowly scoped API keys.
- Use separate keys for read-only monitoring, content submission, publishing, and administrative operations.
- Rotate and revoke any key suspected of having been sent to an untrusted endpoint.
-
Add automated validation tests
- Verify that
http://endpoints are rejected. - Verify that unapproved hostnames are rejected.
- Verify that malformed URLs and credential-bearing URLs are rejected.
- Verify that the bearer header is attached only after endpoint validation succeeds.
- Verify that
A hardened implementation should validate the endpoint before constructing any authenticated request, for example:
js function baseUrl(ctx) { const configured = ctx.env.AUTOMATE_IT_API_URL || DEFAULT_API_URL; let url; try { url = new URL(configured); } catch { throw new CliError("AUTOMATE_IT_API_URL must b ...[truncated 543 chars]-
