T09 · Insecure Skill Coding Practices
- Location
examples/workflow-automation.js:102- Finding
Unvalidated Workflow Callback URL Enables Blind Server-Side Request Forgery
- Content
View full analysis
Vulnerability Details
File Location:
examples/workflow-automation.js, lines 102–112 and 127–140
Vulnerability Type: Server-Side Request Forgery (SSRF)
Risk Level: MediumComplete vulnerable code snippet:
javascript // Optionally notify the agent via webhook callback if (data.state?.callbackUrl) { try { await fetch(data.state.callbackUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ workflowId: data.workflowId, status: data.state.finalStatus, result: data.state.result }) }); } catch (err) { console.error('Callback failed:', err.message); } }The callback URL originates directly from the request:
javascript app.post('/workflow/start', async (req, res) => { const conn = await Datastore.open(); // Create the task const task = await conn.insertOne('tasks', { ...req.body, status: 'pending', createdAt: new Date() }); // Start the workflow const workflow = await taskWorkflow.start({ taskId: task._id, callbackUrl: req.body.callbackUrl // Optional: agent's webhook URL });Technical Analysis
The
/workflow/startroute acceptsreq.body.callbackUrland stores it in workflow state without validating its scheme, hostname, port, resolved IP address, or redirect behavior. When the workflow completes, the serverless runtime passes this value directly tofetch().An attacker who can call the workflow-start endpoint can consequently cause the backend to issue a server-side HTTP POST to an attacker-selected destination. Because the request originates from the Codehooks execution environment rather than the attacker's device, it may reach destinations unavailable to the attacker directly, including internal services, loopback interfaces, link-local endpoints, or cloud metadata services where platform networki ...[truncated 1739 chars]
- Remediation
View remediation
Remediation Suggestions
- Permit only
https:callback URLs. - Use an explicit allowlist of trusted callback domains rather than accepting arbitrary hosts.
- Resolve the destination before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IPv4 and IPv6 ranges.
- Protect against DNS rebinding by connecting only to the validated resolved address and repeating validation when DNS is resolved again.
- Disable redirects where possible. If redirects are required, validate every redirect destination using the same controls.
- Reject URLs containing unexpected credentials, ports, encoded addresses, or ambiguous hostname representations.
- Apply strict connection and response timeouts, response-size limits, and outbound network egress controls.
- Avoid including sensitive workflow results in callbacks unless the destination is trusted and the payload is authenticated and encrypted.
- Authenticate callbacks using a keyed signature and a timestamp to prevent tampering and replay.
- Validate the request body against a strict schema before storing it in the workflow state.
- Permit only
