Back to skill

Security audit

TaskQueue — Async Task Queue for AI Agents

Security checks for vulnerabilities and agentic risk

Overview

This skill is a small in-memory JavaScript task queue, but users should configure timeouts and avoid automatic retries for non-idempotent tasks.

Install only if you are comfortable treating this as a lightweight helper rather than a hardened production queue. Use finite timeouts, keep retry counts low, avoid retries for actions that publish, delete, charge, or mutate external systems unless they are idempotent, and do not accept untrusted task definitions without dependency validation.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/task-queue.js:150
Finding

Timed-Out Handlers Continue Running and May Be Executed Repeatedly

Content
View full analysis
0) { task.result = await this._withTimeout(resultPromise, task.timeout, task.name); } else { task.result = await resultPromise; } task.status = 'success'; task.endTime = Date.now(); this._log(task, `Completed in ${task.endTime - task.startTime}ms`); this.emit('task:complete', task); break; } catch (err) { task.retries++; task.error = err.message; if (task.retries <= task.maxRetries) { this._log(task, `Retry ${task.retries}/${task.maxRetries}: ${err.message}`); this.emit('task:retry', { task, attempt: task.retries, error: err }); await this._delay(this.retryDelay * task.retries); // Exponential backoff } else { task.status = 'failed'; task.endTime = Date.now(); this._log(task, `Failed after ${task.maxRetries} retries: ${err.message}`); this.emit('task:failed', task); } } } ``` ```javascript _withTimeout(promise, ms, name) { return new Promise((resolve, reject) => { const timer = setTimeout( () => reject(new Error(`Task "${name}" timed out after ${ms}ms`)), ms ); promise .then(result => { clearTimeout(timer); resolve(result); }) .catch(err => { clearTimeout(timer); reject(err); }); }); } ``` ### Technical Analysis `_withTimeout()` races the supplied operation against a timer on ...[truncated 2199 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/task-queue.js:113
Finding

Dependency Ordering and Cycles Can Deadlock Queue Execution

Content
View full analysis
a.priority - b.priority); // Build lookup map for dependency resolution — uses the full pre-drain list const taskMap = new Map(tasksToRun.map(t => [t.id, t])); const runOne = async (task) => { // Skip if cancelled if (this._cancelledIds.has(task.id) || task.status === 'cancelled') { task.status = 'cancelled'; this._log(task, 'Cancelled before start'); this.results.push(this._toResult(task)); this.emit('task:cancelled', task); return; } // Wait while paused while (this._paused) await this._delay(100); // Check dependency if (task.dependsOn) { const dep = taskMap.get(task.dependsOn); if (dep) { // Wait for dependency to finish (it may be running in a parallel batch) while (dep.status === 'running' || dep.status === 'queued') { await this._delay(50); } if (dep.status === 'failed' || dep.status === 'skipped' || dep.status === 'cancelled') { task.status = 'skipped'; this._log(task, `Skipped — dependency "${task.dependsOn}" did not succeed`); this.results.push(this._toResult(task)); this.emit('task:skipped', task); return; } } } ``` ```javascript // Run with concurrency — parallel batches of size this.concurrency for (let i = 0; i < tasksToRun.length; i += this.concurrency) { const batch = tasksToRun.slice(i, i + this.concurrency); await Promise.all(batch.map(t => runOne(t))); } ``` ### Technical Analysis Tasks are sorted by priority and divided into fixed batches. Every task in the current batch must settle through `Promise.all()` before the next batch starts. Dependency resolution, however, waits while the referenced task ...[truncated 2400 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
93% confidence
Finding

The queue permits a default timeout of 0, meaning tasks may run indefinitely. In an agent context, untrusted or buggy task handlers can hang forever, tying up concurrency slots, preventing queue drain, and causing denial of service or resource exhaustion over time.

Content

Scanner excerpt · src/task-queue.js (reported line 17)May include surrounding context.

js
* @param {number} [options.maxRetries=3]        - Default max retries per task
   * @param {number} [options.retryDelay=5000]     - Base delay between retries (ms); multiplied by retry count
   * @param {number} [options.concurrency=1]       - Max tasks running in parallel
   * @param {number} [options.timeout=0]           - Default task timeout in ms (0 = no timeout)
   */
  constructor(options = {}) {
    super();

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · src/task-queue.js (reported line 34)May include surrounding context.

js
}

  /**
   * Add a task to the queue.
   * @param {object} task
   * @param {string}   [task.id]         - Unique ID (auto-generated if omitted)
   * @param {string}   [task.name]       - Human-readable name