T01 · Skill Instruction Hijacking
- Location
hooks/openclaw/handler.ts:379- Finding
Persistent Behavioral Instruction Injection Through the OpenClaw Bootstrap Hook
- Content
View full analysis
{ // Skip sub-agent sessions to avoid bootstrap issues // Sub-agents have sessionKey patterns like "agent:main:subagent:..." const sessionKey = event.sessionKey || ''; if (sessionKey.includes(':subagent:')) { return; } const context = event.context as Record; // Inject the reminder as a virtual bootstrap file // Check that bootstrapFiles is an array before pushing if (!Array.isArray(context.bootstrapFiles)) { return; } const occupiedByOtherFile = context.bootstrapFiles.some( (file) => isObject(file) && file.path === REMINDER_PATH && !isInjectedReminderFile(file), ); if (occupiedByOtherFile) { return; } let reminderContent = REMINDER_CONTENT; const workspaceDir = typeof context.workspaceDir === 'string' && context.workspaceDir.trim() ? context.workspaceDir : undefined; const pendingSweepCount = await countPendingSweepEntries(workspaceDir); if (pendingSweepCount > 0) { const plural = pendingSweepCount === 1 ? 'y' : 'ies'; reminderContent += `\n\n**Pending triage:** ${pendingSweepCount} auto-detected error entr${plural} ` + `(Source: ${SWEEP_SOURCE}) in \`.learnings/ERRORS.md\` await review. ` + 'Confirm, resolve, or delete them when convenient.'; } const cleanedBootstrapFiles = context.bootstrapFiles.filter( (file, index, files) => !isInjectedReminderFile(file) || files.findIndex((candidate) => isInjectedReminderFile(candidate)) === index, ); const reminderFile = { name: REMINDER_NAME, path: REMINDER_PATH, content: reminderContent, missing: false, virtual: true, }; const existingIndex = cleanedBootstrapFiles.findIndex((file) => ...[truncated 2413 chars]- Remediation
View remediation
