T01 · Skill Instruction Hijacking
Error
- Location
- scripts/vexa-transform.mjs:11
- Finding
- Untrusted Webhook Payload Is Converted into Agent Instructions and an Executable Command<![CDATA[ ## Vulnerability Details **File Location**: `scripts/vexa-transform.mjs:11-74` **Vulnerability Type**: Webhook-driven Agent instruction and command injection **Risk Level**: Critical ### Vulnerable Code ```js const p = ctx?.payload || {}; // Extract platform + native_meeting_id from common payload shapes const platform = (typeof p.platform === 'string' && p.platform) || (typeof p.meeting?.platform === 'string' && p.meeting.platform) || (typeof p.data?.platform === 'string' && p.data.platform) || ''; const nativeMeetingId = (typeof p.native_meeting_id === 'string' && p.native_meeting_id) || (typeof p.meeting?.native_meeting_id === 'string' && p.meeting.native_meeting_id) || (typeof p.data?.native_meeting_id === 'string' && p.data.native_meeting_id) || ''; if (!platform || !nativeMeetingId) { return null; // skip — no meeting identity } // Only process when meeting is finished (skip in-progress / heartbeat) const status = ( (typeof p.status === 'string' && p.status) || (typeof p.meeting?.status === 'string' && p.meeting.status) || (typeof p.data?.status === 'string' && p.data.status) || '' ).toLowerCase(); const event = ( (typeof p.event === 'string' && p.event) || (typeof p.event_type === 'string' && p.event_type) || '' ).toLowerCase(); const completionReason = p.data?.completion_reason ?? p.meeting?.data?.completion_reason; const isFinished = ['completed', 'finalized', 'done'].includes(status) || (event && (event.includes('complete') || event.includes('final'))) || Boolean(completionReason); const isInProgress = ['active', 'in_progress', 'running'].includes(status); if (isInProgress) { return null; // skip — meeting still running } if (!isFinished && (status || event)) { return null; // skip — has status/event but not finished } // Meeting finished — inject explicit report command const reportCmd = `node skills/vexa/scripts/vexa.mjs report --platform ${platform} --native_meeting_id ${nativeMeetingI ...[truncated 3292 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Verify webhook authenticity before invoking the transform, preferably with a provider-issued HMAC signature, timestamp, and replay protection. 2. Reject events that do not have an explicit, exact completion status. Do not treat missing `status` and `event` fields as completion. 3. Allow only known platforms: - `google_meet` - `teams` - `zoom` 4. Validate meeting IDs with platform-specific regular expressions before further processing. 5. Do not construct a textual shell command from webhook values. Invoke a fixed local handler with structured arguments or use `spawn`/`execFile` with `shell: false` and a fixed executable. 6. Do not place the raw payload in an Agent instruction. If diagnostic data is required, store a filtered representation separately and clearly label it as inert, untrusted data. 7. Use a deterministic report-generation worker for webhook events rather than asking a general-purpose Agent to interpret and execute the payload. 8. Apply length limits and reject control characters, newlines, shell metacharacters, and unexpected properties. 9. Add tests covering missing statuses, forged completion reasons, prompt-injection strings, newline injection, and shell metacharacters. ]]>
