Back to skill

Security audit

Hle Reasoning Wrapper

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it also includes an undeclared persistent answer cache that can save user questions or model answers to disk.

Install only if you are comfortable with a local cache feature that is not documented in the skill instructions. Avoid using it with sensitive questions or answers unless the cache is removed, documented, or changed to an opt-in design with safer keys and retention controls.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Findings (2)

T01 · Skill Instruction Hijacking

Warning
Location
index.js:23
Finding

Untrusted Input Enables Prompt Instruction Hijacking and Validation Bypass

Content
View full analysis

Vulnerability Details

File Location: index.js, lines 23–40
Vulnerability Type: Prompt injection and ineffective structural validation
Risk Level: Medium

Vulnerable Code

javascript
function formatPrompt(question, context = "") {
    return `
You are a reasoning engine solving a hard logic problem.
Question: ${question}
Context: ${context}

You MUST follow this format:
--- REASONING ---
(Your step-by-step logic here)
--- FINAL ANSWER ---
(Your concise final answer here)
`;
}

function validateOutput(text) {
    if (!text || typeof text !== 'string') return false;
    const hasReasoning = text.includes("--- REASONING ---");
    const hasAnswer = text.includes("--- FINAL ANSWER ---");
    return hasReasoning && hasAnswer;
}

Technical Analysis

The question and context parameters are inserted directly into an instruction-bearing prompt without a robust trust boundary, escaping strategy, or explicit isolation as untrusted data. An attacker controlling either parameter can inject instructions that conflict with the wrapper's intended behavior, include forged structural markers, or ask the model to ignore the surrounding prompt.

The corresponding validator does not enforce the actual output grammar. It only checks whether both marker strings occur somewhere in the response. It does not verify marker order, uniqueness, section boundaries, nonempty content, or the absence of arbitrary text before or after the expected sections. Consequently, malicious or malformed output can pass validation merely by containing both strings.

Attack Path

  1. An attacker supplies a crafted question or context containing instructions such as a request to ignore the wrapper, follow a different task, or emit attacker-selected content.
  2. formatPrompt() interpolates this content directly alongside trusted instructions.
  3. The downstream language model interprets the injected ...[truncated 805 chars]
Remediation
View remediation

Remediation Suggestions

  • Treat question and context as untrusted data and place them inside clearly delimited data sections that cannot be confused with trusted instructions.
  • Reject, escape, or encode reserved structural markers occurring in caller-controlled values.
  • Keep authoritative instructions separate from untrusted content and explicitly state that content inside the data boundaries must not be interpreted as instructions.
  • Prefer a structured model-output mechanism, such as schema-constrained JSON, when supported by the downstream model API.
  • Replace substring checks with a strict parser that verifies marker order, uniqueness, permitted surrounding text, and nonempty section contents.
  • Apply maximum input and output lengths to limit abuse.
  • Do not rely on prompt formatting or output validation as a security boundary for sensitive tool execution. Independently authorize any downstream action.
  • Add tests covering embedded markers, reversed markers, duplicate sections, trailing malicious content, empty sections, and instruction-injection payloads.

T02 · Agent Memory Poisoning

Warning
Location
index.js:42
Finding

Persistent Answer-Cache Poisoning Through Truncated Cache Keys

Content
View full analysis

Vulnerability Details

File Location: index.js, lines 5–21 and 42–55
Vulnerability Type: Predictable cache-key collision and persistent state poisoning
Risk Level: Medium

Vulnerable Code

javascript
function loadCache() {
    try {
        if (fs.existsSync(CACHE_FILE)) {
            return JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
        }
    } catch (e) {
        console.warn('Failed to load cache:', e.message);
    }
    return {};
}

function saveCache(cache) {
    try {
        fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
    } catch (e) {
        console.warn('Failed to save cache:', e.message);
    }
}

function getCachedAnswer(question) {
    const cache = loadCache();
    // Simple key normalization
    const key = Buffer.from(question).toString('base64').slice(0, 64);
    return cache[key] || null;
}

function cacheAnswer(question, answer) {
    const cache = loadCache();
    const key = Buffer.from(question).toString('base64').slice(0, 64);
    cache[key] = answer;
    saveCache(cache);
}

Technical Analysis

Cache entries are identified by truncating the Base64 encoding of the question to 64 characters. Base64 encodes 48 input bytes into 64 characters, so questions sharing approximately the same first 48 bytes can deliberately resolve to the same cache key even when their remaining contents differ.

Base64 is an encoding rather than a collision-resistant hash. Truncating the encoded input makes collisions predictable and easy to construct. cacheAnswer() then overwrites the value associated with the shared key without recording or validating the complete original question.

The resulting answer is persisted in cache.json and reused by getCachedAnswer(). There is no provenance validation, record schema, expiration policy, integrity check, or confirmation that the stored question exactly matches the requested ques ...[truncated 1587 chars]

Remediation
View remediation

Remediation Suggestions

  • Generate cache keys using a collision-resistant digest over the complete canonicalized question, for example:

    javascript
    const crypto = require('crypto');
    
    function cacheKey(question) {
        return crypto
            .createHash('sha256')
            .update(String(question), 'utf8')
            .digest('hex');
    }
    
  • Store the complete normalized question with each cache record and verify an exact match before returning its answer.

  • Validate question and answer types and enforce reasonable size limits before caching.

  • Define cache expiration, invalidation, and namespace isolation policies so unrelated users or tasks do not share untrusted state.

  • Restrict write access to trusted callers rather than exposing unrestricted cache mutation across trust boundaries.

  • Avoid caching secrets or sensitive model responses. If sensitive caching is required, use a protected storage location with restrictive file permissions and appropriate encryption.

  • Write updates atomically through a securely created temporary file followed by a rename to reduce corruption and race risks.

  • Add regression tests proving that questions with identical prefixes but different suffixes receive distinct cache entries.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • 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 (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The prompt-formatting and output-validation behavior generally aligns with the declared reasoning-wrapper purpose. However, the code also adds a persistent caching subsystem that reads from and writes to a local cache.json file using fs. This is a substantive undeclared capability and involves resource access inconsistent with the declared permissions, which are empty. Because the skill does more than just wrap HLE questions for CoT formatting and compliance, the description does not fully and accurately represent the actual behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Filesystem-based caching is not justified by a simple reasoning-format wrapper and introduces unnecessary persistence of user-provided content. In this skill context, the wrapper handles arbitrary questions and context, so saving them to disk may retain sensitive or proprietary information without a clear operational need.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill persists cached data to disk even though its stated purpose is only to wrap HLE questions into a formatting template. Because both questions and answers may contain sensitive user inputs or model outputs, writing them to a local cache creates undisclosed data retention and expands the attack surface beyond the declared functionality.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code writes cached question/answer data to disk without any notice, consent flow, or indication to users that their content may be retained. Since the inputs are arbitrary questions and context strings, this can silently persist sensitive data and create privacy, compliance, and data leakage risks.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.