Back to skill

Security audit

React Game Loop Hooks

Security checks for vulnerabilities and agentic risk

Overview

This skill provides a small React game-loop hook and example, with no hidden access or unrelated behavior found.

Reasonable to install for React animation work. If using it in production, review or patch the start/stop cleanup logic so repeated synchronous start calls cannot leave duplicate animation-frame loops running.

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

T09 · Insecure Skill Coding Practices

Warning
Location
assets/hooks/useGameLoop.ts:26
Finding

Duplicate requestAnimationFrame Chains Can Survive Component Unmount

Content
View full analysis
{ isRunningRef.current = isRunning; }, [isRunning]); const rafRef = useRef(null); const lastTimestampRef = useRef(null); const elapsedTimeRef = useRef(0); const tick = useCallback((timestamp: number) => { if (!isRunningRef.current) return; if (lastTimestampRef.current !== null) { let delta = timestamp - lastTimestampRef.current; if (delta > maxDeltaTime) delta = maxDeltaTime; elapsedTimeRef.current += delta; try { callbackRef.current(delta, elapsedTimeRef.current); } catch (err) { // eslint-disable-next-line no-console console.error('useGameLoop callback error:', err); } } lastTimestampRef.current = timestamp; rafRef.current = requestAnimationFrame(tick); }, [maxDeltaTime]); const start = useCallback(() => { if (isRunningRef.current) return; setIsRunning(true); lastTimestampRef.current = null; // skip stale timestamp rafRef.current = requestAnimationFrame(tick); }, [tick]); const stop = useCallback(() => { if (!isRunningRef.current) return; setIsRunning(false); if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } }, []); // Cleanup on unmount useEffect(() => { return () => { if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } }; }, []); ``` ### Technical Analysis The `start()` function checks `isRunningRef.current`, but it does not synchronously update that ref before scheduling a frame. Instead, the ref is updated later by a React effect after `setIsRunning(true)` causes a render. Consequently, two or more synchronous calls to `start()` before the effect executes can all ...[truncated 2234 chars]
Remediation
View remediation
{ if (isRunningRef.current) return; isRunningRef.current = true; setIsRunning(true); lastTimestampRef.current = null; rafRef.current = requestAnimationFrame(tick); }, [tick]); const stop = useCallback(() => { if (!isRunningRef.current) return; isRunningRef.current = false; setIsRunning(false); if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } }, []); useEffect(() => { return () => { isRunningRef.current = false; if (rafRef.current !== null) { cancelAnimationFrame(rafRef.current); rafRef.current = null; } }; }, []); ``` Additional hardening measures: 1. Treat `isRunningRef` as the immediate source of truth for asynchronous RAF callbacks rather than waiting for React effects to synchronize it. 2. Consider using a generation token. Increment it on every start and stop, capture the current generation in the scheduled chain, and refuse to reschedule callbacks belonging to an obsolete generation. 3. If the design permits multiple active callbacks, store every RAF identifier in a collection and cancel all identifiers during stop and unmount. 4. Add automated tests covering: - Multiple synchronous calls to `start()`. - `start()` immediately followed by `stop()`. - Component unmount immediately after `start()`. - Unmount after repeated synchronous starts. - Confirmation that no frame callback executes or reschedules after cleanup. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep