Back to skill

Security audit

Agent Reliability

Security checks for vulnerabilities and agentic risk

Overview

This is an in-memory JavaScript reliability toolkit with integration-quality risks but no evidence of hidden, destructive, persistent, or data-exfiltrating behavior.

Installers should treat this as a developer library, not a hardened security control. If used with untrusted callers or decisions that trigger important actions, add authentication, validate vote decisions, confidence, weights, IDs, and metadata sizes, disable or limit dissent recording when reasons may contain sensitive information, and configure regular cleanup or hard retention limits.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/voting-consensus.js:120
Finding
Consensus Manipulation Through Unvalidated Vote Weights and Confidence Values## Vulnerability Details **File Location**: `src/voting-consensus.js:120-127, 181-212` **Vulnerability Type**: Improper input validation in security-sensitive consensus calculations **Risk Level**: High ### Vulnerable Code ```javascript const voteRecord = { voterId, decision: vote.decision || 'abstain', confidence: vote.confidence ?? 0.5, weight: vote.weight ?? 1, reason: vote.reason || '', metadata: vote.metadata || {}, timestamp: Date.now() }; session.votes.set(voterId, voteRecord); ``` ```javascript let totalWeight = 0; let totalConfidence = 0; for (const vote of votes) { if (counts[vote.decision]) { counts[vote.decision].count++; counts[vote.decision].weight += vote.weight; counts[vote.decision].confidence += vote.confidence * vote.weight; counts[vote.decision].voters.push({ voterId: vote.voterId, confidence: vote.confidence, reason: vote.reason }); } totalWeight += vote.weight; totalConfidence += vote.confidence * vote.weight; } // Calculate weighted average confidence const avgConfidence = totalWeight > 0 ? totalConfidence / totalWeight : 0; // Find the winning decision let winningDecision = 'abstain'; let maxWeight = 0; for (const [decision, data] of Object.entries(counts)) { if (decision !== 'abstain' && data.weight > maxWeight) { maxWeight = data.weight; winningDecision = decision; } } // Calculate agreement const winningWeight = counts[winningDecision].weight; const agreement = totalWeight > 0 ? winningWeight / totalWeight : 0; // Calculate average confidence for the winning decision const winningConfidence = winningWeight > 0 ? counts[winningDecision].confidence / winningWeight : 0; ``` ### Technical Analysis The public `vote()` method accepts `decision`, `confidence`, and `weight` without validating their types, ranges, or finiteness. The values are then used directly in consensus arithmetic. An untrusted voter can submit: - A negative weight to reduce ...[truncated 2151 chars]
Remediation
## Remediation Suggestions 1. Validate every vote before storing it: - Permit only `approve`, `reject`, and `abstain`. - Require `Number.isFinite(vote.confidence)` and a value in `[0,1]`. - Require `Number.isFinite(vote.weight)`, a strictly positive value, and an administrator-defined maximum. 2. Do not trust weights supplied by voters. Resolve weights from a server-side voter registry or immutable session policy. 3. Authenticate and authorize voter identities before accepting votes. 4. Define whether a voter may replace an existing vote. If replacement is permitted, ensure statistics and audit records distinguish replacement from a new vote. 5. Reject any result whose total weight is non-finite or non-positive. 6. Assert that calculated agreement and confidence are finite and within `[0,1]` before consensus evaluation. 7. Add adversarial tests covering negative weights, zero weights, excessive weights, `NaN`, `Infinity`, invalid decisions, duplicate voters, and malformed confidence values.

T09 · Insecure Skill Coding Practices

Warning
Location
src/confidence-calculator.js:104
Finding
Unbounded Confidence and Metadata History Can Exhaust Process Memory## Vulnerability Details **File Location**: `src/confidence-calculator.js:104-116, 228-239` **Vulnerability Type**: Uncontrolled resource consumption through unbounded in-memory retention **Risk Level**: Medium ### Vulnerable Code ```javascript const record = { timestamp: Date.now(), confidence, factors: { baseConfidence, dataQuality, modelAccuracy, historicalSuccess, uncertainty } }; this.confidenceHistory.push(record); ``` ```javascript recordStepConfidence(stepId, confidence, metadata = {}) { const record = { timestamp: Date.now(), confidence, metadata }; if (!this.stepConfidence.has(stepId)) { this.stepConfidence.set(stepId, []); } this.stepConfidence.get(stepId).push(record); this._log('Step confidence recorded', { stepId, confidence }); this.emit('step-recorded', { stepId, record }); } ``` ### Technical Analysis `confidenceHistory` grows on every `calculate()` call, while each unique `stepId` creates a persistent map entry whose array grows on every `recordStepConfidence()` call. No automatic history window, object-size limit, maximum number of step IDs, or eviction policy is applied. The `metadata` object is retained by reference and may contain large attacker-controlled structures. Although a manual `cleanup()` method exists, callers are not required to invoke it, and cleanup does not impose an absolute capacity bound. The same class also lacks finite-number and range validation. Malformed confidence factors can introduce `NaN` into stored history and derived metrics, but the primary security impact in this finding is unbounded resource consumption. ### Attack Path 1. An attacker gains repeated access to application functionality that invokes `calculate()` or `recordStepConfidence()`. 2. The attacker sends many records, uses unique `stepId` values, or supplies large metadata objects. 3. Each request appends another retained object or creates another map entry. 4. Unless the integratin ...[truncated 565 chars]
Remediation
## Remediation Suggestions 1. Add configurable hard limits for: - Global confidence-history length. - Number of distinct step IDs. - Records retained per step. - Metadata depth and serialized size. 2. Use a bounded ring buffer or least-recently-used eviction policy rather than relying on manual cleanup. 3. Copy and sanitize metadata before retention so callers cannot mutate stored state or retain unexpectedly large object graphs. 4. Reject non-finite or out-of-range confidence values and factors. 5. Recalculate statistics correctly after eviction or cleanup. 6. Expose retention defaults that are safe without additional integration work. 7. Add load tests proving that memory usage remains bounded under sustained input.

T09 · Insecure Skill Coding Practices

Warning
Location
src/voting-consensus.js:66
Finding
Unbounded Voting Sessions, Dissent Records, and Timers Permit Denial of Service## Vulnerability Details **File Location**: `src/voting-consensus.js:66-91, 120-129, 282-293` **Vulnerability Type**: Uncontrolled resource consumption through unbounded sessions, records, and timers **Risk Level**: Medium ### Vulnerable Code ```javascript const session = { id, createdAt: Date.now(), votes: new Map(), status: 'open', result: null, options: { strategy: options.strategy || this.strategy, minAgreement: options.minAgreement ?? this.minAgreement, minVoters: options.minVoters ?? this.minVoters, timeout: options.timeout || this.timeout, ...options } }; this.sessions.set(id, session); this.stats.totalSessions++; this._log(`Session created: ${id}`); this.emit('session:created', { sessionId: id, session }); // Set timeout if (session.options.timeout > 0) { setTimeout(() => { if (session.status === 'open') { this._finalizeSession(id); } }, session.options.timeout); } ``` ```javascript session.votes.set(voterId, voteRecord); this.stats.totalVotes++; this._log(`Vote recorded: ${voterId} -> ${voteRecord.decision} (weight: ${voteRecord.weight})`); this.emit('vote:recorded', { sessionId: targetSessionId, voterId, vote: voteRecord }); ``` ```javascript if (this.enableDissentRecording && result.dissenters.length > 0) { for (const dissenter of result.dissenters) { this.dissentRecords.push({ sessionId, timestamp: Date.now(), voterId: dissenter.voterId, decision: result.decision, confidence: dissenter.confidence, reason: dissenter.reason }); } } ``` ### Technical Analysis The class does not limit the number of sessions, votes per session, voter identifiers, reason or metadata sizes, or dissent records. Each created session may also schedule a new timeout. The timeout handle is not retained, so it cannot be canceled when a session is resolved, removed, or the component is reset. Closed sessions and dissent records remain in memory until callers expl ...[truncated 1057 chars]
Remediation
## Remediation Suggestions 1. Enforce maximum counts for open sessions, total retained sessions, votes per session, and dissent records. 2. Limit the lengths of session IDs, voter IDs, reasons, and serialized metadata. 3. Store timeout handles in each session and call `clearTimeout()` when the session closes, is deleted, or the component resets. 4. Automatically evict completed sessions after a bounded retention period. 5. Reject duplicate session identifiers instead of silently replacing existing sessions. 6. Validate session options against a strict schema, including bounded positive timeouts and minimum-voter limits. 7. Apply per-caller rate limits and authorization in the integrating application. 8. Add stress tests for session floods, vote floods, oversized metadata, and timer cleanup.

T09 · Insecure Skill Coding Practices

Warning
Location
src/fallback-manager.js:72
Finding
Execution State Accumulates Without an Automatic Retention Limit## Vulnerability Details **File Location**: `src/fallback-manager.js:72-84` **Vulnerability Type**: Uncontrolled resource consumption through retained execution state **Risk Level**: Medium ### Vulnerable Code ```javascript this.stats.totalExecutions++; // Initialize execution state const state = { id: executionId, startTime: Date.now(), attempts: 0, lastError: null, fallbackUsed: false, recovered: false }; this.executionState.set(executionId, state); ``` ### Technical Analysis Every `execute()` call inserts an object into `executionState`. Successful and failed executions update that object but do not remove it. The only removal mechanisms are explicit calls to `cleanup()` or `resetStats()`. If an application exposes repeated fallback execution to an untrusted or high-volume caller, execution IDs and state objects accumulate for the lifetime of the manager. Caller-supplied `executionId` values are also not length-limited. A manual cleanup API does not provide a safe default or an absolute capacity bound. ### Attack Path 1. An attacker repeatedly triggers an application operation wrapped by `FallbackManager.execute()`. 2. Every invocation creates and retains a new execution-state entry. 3. The operations complete or fail, but their state remains in the map. 4. If the application does not call `cleanup()` frequently enough, the map grows continuously. 5. Sustained requests increase heap use and garbage-collection work, potentially causing process-level denial of service. ### Impact Assessment The issue does not provide code execution or additional privileges. It affects the availability of the Node.js process and can disrupt all operations using the same manager or runtime. The practical severity depends on whether untrusted users can trigger executions and whether the integrating application performs reliable cleanup.
Remediation
## Remediation Suggestions 1. Remove completed execution state automatically unless retention is explicitly enabled. 2. Add a configurable maximum number of retained states and evict the oldest completed entries. 3. Enforce a maximum length and allowed character set for caller-provided execution IDs. 4. Use a bounded time-to-live cache for diagnostic state. 5. Ensure in-progress entries cannot be evicted while completed entries are available. 6. Add sustained-execution tests demonstrating bounded memory use. 7. Apply request-rate limits at the integration boundary where untrusted callers can trigger operations.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Memory Manipulation

High
Category
Memory Poisoning
Content
monitor.reset();
  
  const history = monitor.getHistory();
  runner.assert(history.length === 0, 'Should reset history');
});

// Run tests
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The main descriptive content is presented in Chinese while the file does not indicate that the skill is region-specific or provide an opt-in language choice. This creates a natural-language locale policy issue because users may be forced into a specific language without consent or justification.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The module stores dissent records containing voter identifiers and free-form reasons, which may include sensitive personal or operational information. In an agent orchestration context, these records can persist beyond the session lifecycle and be retrieved via getDissentRecords(), creating privacy and data minimization risks if callers are not explicitly warned or if access controls are absent.

Natural-Language Policy Violations

Low
Confidence
87% confidence
Finding
This JavaScript file includes natural-language documentation primarily in Chinese, such as the module description and parameter comments, without indicating that other languages are supported. Under the stated policy, forcing a specific language without user opt-in can be a locale-policy violation, especially for maintainers or users relying on in-file guidance.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
The emitted alert message is a natural-language string fixed in English, while the rest of the file is documented in Chinese, and there is no mechanism for locale selection or opt-in. This may violate the language/locale policy because the skill imposes a specific output language rather than offering a choice or documenting a justified locale constraint.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This file’s human-facing documentation strings and comments are written exclusively in Chinese, including the module description and public API parameter docs. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy violation when no justification or alternative is provided.

Natural-Language Policy Violations

Low
Confidence
93% confidence
Finding
The natural-language documentation and interface comments are written in Chinese, indicating a fixed language choice without any opt-in or alternative locale support. Under the policy, forcing a specific language without user choice is a locale/language policy concern.

Static analysis

No suspicious patterns detected.