T09 · Insecure Skill Coding Practices
Error
- Location
- index.ts:26
- Finding
- Shared Global State Enables Cross-Session Data Leakage and Stream Corruption## Vulnerability Details **File Location**: `index.ts`, lines 26-34 and 54-75 **Vulnerability Type**: Shared mutable state without session isolation **Risk Level**: High ### Vulnerable Code ```typescript // Global state let buffer = ""; let lastOutput = ""; let config = { buffer_size: 10, format_markdown: true, fix_incomplete_sentences: true, remove_duplicates: true }; ``` ```typescript export default async function streamFormatter(params: StreamFormatterParams) { const validatedParams = StreamFormatterParams.parse(params); switch (validatedParams.action) { case "init": { config = { ...config, ...validatedParams.options }; buffer = ""; lastOutput = ""; return { success: true, config }; } case "process": { const { chunk, flush } = validatedParams; // Add to buffer buffer += chunk; // Duplicate removal if (config.remove_duplicates && buffer.includes(lastOutput) && lastOutput.length > 0) { buffer = buffer.replace(lastOutput, ""); } ``` ### Technical Analysis The formatter stores its buffer, previous output, and configuration in module-level mutable variables. Every invocation handled by the same runtime therefore operates on the same state. The API does not accept a stream identifier, session identifier, or caller-specific state object. Calling `init` resets the shared state and changes the shared configuration. Calling `process` appends data to the same shared `buffer`, regardless of which user or conversation supplied it. In a persistent or concurrent multi-user runtime, one conversation can consequently read, modify, flush, reset, or corrupt another conversation's pending output. Asynchronous function syntax does not provide isolation. Interleaved calls can access this state between separate requests, even though the function contains no explicit asynchronous operation a ...[truncated 1459 chars]
- Remediation
- ## Remediation Suggestions - Remove module-global mutable stream state. - Create a separate formatter instance for each stream and keep `buffer`, `lastOutput`, and `config` in that instance. - If the hosting interface requires a single exported function, require an authenticated, unpredictable stream identifier and store state in a map keyed by both caller identity and stream identity. - Do not trust a caller-supplied stream identifier by itself; bind it to the authenticated caller to prevent enumeration or cross-user access. - Delete stream state immediately after a final flush, reset, timeout, or cancellation. - Apply expiration and capacity limits to any server-side state map. - Prevent one caller from resetting or reconfiguring another caller's stream. - Add concurrency tests that interleave `init`, `process`, `flush`, and `reset` operations from multiple simulated users and verify that no content or configuration crosses session boundaries.
