Перейти к основному содержимому

Модель безопасности

Elftia реализует многоуровневую архитектуру безопасности с глубокой эшелонированной защитой, охватывающую полную цепочку: от изоляции процессов Electron до проверки вызовов инструментов AI.

Полный конвейер безопасности

Полный конвейер безопасности для внешнего сообщения (Channel) от входа до выполнения:

flowchart TB
Input[Channel message input] --> Sanitize[InputSanitizer<br/>strip dangerous characters]
Sanitize --> Rate{RateLimiter<br/>rate check}
Rate -->|over limit| Reject1[Reject: 429]
Rate -->|pass| Permission{UserPermissionService<br/>user permissions}
Permission -->|blocked| Reject2[Reject: 403]
Permission -->|guest/moderator/admin| PG{PromptGuardian<br/>prompt injection detection}
PG -->|block mode + injection| Reject3[Reject + deflect message]
PG -->|monitor mode| Log1[Log event]
PG -->|pass| Magi[MagiService<br/>Agent processing]

Magi --> ToolCall[Tool call request]
ToolCall --> FW{ExecutionFirewall<br/>path deny list}
FW -->|deny| Block1[Deny: path blocked]
FW -->|pass| GA{GuardianAgent<br/>AI safety review}
GA -->|critical/high| Gate{ChannelPermissionGate<br/>human confirmation}
GA -->|low/none| Exec[Execute tool]
GA -->|monitor| LogExec[Log + execute]
Gate -->|approved| Exec
Gate -->|denied| Block2[Deny: user vetoed]
Exec --> Audit[AuditLogger<br/>audit record]
LogExec --> Exec
Log1 --> Magi

Основы безопасности Electron

Context Isolation

Context Isolation в Electron гарантирует, что процесс рендерера не может напрямую обращаться к Node.js API:

{/* Конфигурация безопасности при создании BrowserWindow */}
const mainWindow = new BrowserWindow({
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
sandbox: true,
preload: preloadPath,
},
});

contextBridge

Скрипт preload открывает доступ только к разрешённым API через contextBridge.exposeInMainWorld():

{/* preload/index.ts -- безопасное предоставление IPC-интерфейса */}
contextBridge.exposeInMainWorld('api', {
completion: {
chatInSession: (params) => ipcRenderer.invoke('completion:chat', params),
},
});

Токен аутентификации IPC

Каждый IPC-вызов содержит токен аутентификации; главный процесс проверяет его через secureHandle:

sequenceDiagram
participant R as Renderer
participant P as Preload
participant M as Main (secureHandle)

Note over R,M: App startup
M->>P: ipcRenderer.sendSync('auth:bootstrap') returns token
P->>P: Save token to memory

Note over R,M: IPC call
R->>P: window.api.someMethod(params)
P->>M: ipcRenderer.invoke(channel, {token, ...params})
M->>M: validateToken(token)
alt Token invalid
M-->>P: throw Error('AUTH_FAILED')
else Token valid
M->>M: execute handler
M-->>P: result
end
  • Генерация токена: главный процесс генерирует случайный токен при запуске
  • Синхронная инициализация: preload получает токен через sendSync до загрузки страницы
  • Проверка при каждом вызове: все IPC-вызовы, обёрнутые в secureHandle, проверяют токен

ExecutionFirewall

Детерминированный контроль доступа по путям — блокирует доступ к системным каталогам и файлам с учётными данными. Нулевые накладные расходы на LLM.

Правила запрета

{/* Структура правила пути */}
interface DeniedPathRule {
pattern: RegExp;
reason: string;
denyOps?: ('read' | 'write')[];
}
КатегорияПримеры правилЗаблокированные операции
Системные каталогиC:\Windows\, /etc/, /usr/, /proc/чтение + запись
Файлы с учётными данными.ssh/, .aws/, .gnupg/, .envчтение + запись
Данные браузераChrome\User Data\, .mozilla/чтение + запись
РеестрSystem32\config\SAM|SYSTEM|SOFTWAREчтение + запись
Конфигурационные файлы.gitconfig, .npmrc, .bashrcтолько запись

Извлечение путей из инструментов

Автоматически извлекает пути к файлам из параметров вызова инструментов для проверки:

{/* Сопоставление инструментов с параметрами путей */}
const FILE_TOOL_PATH_PARAMS: Record<string, string[]> = {
Read: ['path', 'file_path'],
Write: ['path', 'file_path'],
Edit: ['path', 'file_path'],
ListDir: ['path', 'dir_path'],
};
{/* Интерфейс проверки межсетевого экрана */}
class ExecutionFirewall {
constructor(workspacePath: string);
checkPath(filePath: string, operation: 'read' | 'write'): FirewallCheckResult;
checkToolCall(toolName: string, toolInput: Record<string, unknown>): FirewallCheckResult;
}

interface FirewallCheckResult {
allowed: boolean;
deniedBy?: string;
reason?: string;
}

GuardianAgent

Проверщик безопасности вызовов инструментов на основе LLM, расположенный после ExecutionFirewall и перед подтверждением человеком.

Режимы работы

РежимОбласть проверкиПолитика блокировкиПоведение при ошибке/таймауте
offНетНетНе запускается
monitorЧувствительные инструментыТолько журналирование, без блокировкиfail-open
guardЧувствительные инструментыБлокировать high/criticalfail-open
strictВсе инструментыБлокировать medium+fail-closed

Уровни риска

type RiskLevel = 'none' | 'low' | 'medium' | 'high' | 'critical';
УровеньЗначениеПример
noneПолностью безопасноЧтение файла в рабочем пространстве
lowНезначительный рискЗапись в файл проекта
mediumУмеренный рискКоманды Shell, изменяющие состояние, установка пакетов
highВысокий рискДоступ к чувствительным путям, операции за пределами рабочего пространства
criticalКрайне опасноrm -rf, утечка данных, эскалация привилегий

Критические правила

Следующие операции всегда оцениваются как high или critical:

  • Удаление файлов за пределами рабочего пространства
  • Рекурсивные команды удаления (rm -rf, del /s /q, Remove-Item -Recurse)
  • Доступ к системным каталогам (/etc, C:\Windows, C:\Users)
  • Эскалация привилегий (sudo, runas)
  • Передача в shell через пайп (curl | sh, wget | bash)
  • Доступ к учётным данным и ключам

Кэширование

Использует хэши SHA-256 для кэширования проверенных вызовов инструментов, избегая лишних обращений к LLM:

{/* Генерация ключа кэша GuardianAgent */}
function cacheKey(toolName: string, toolInput: unknown): string {
return createHash('sha256')
.update(JSON.stringify({ tool: toolName, input: toolInput }))
.digest('hex');
}

Список чувствительных инструментов

const SENSITIVE_TOOLS = new Set(['Bash', 'Write', 'Edit', 'Agent']);

Нечувствительные инструменты (например, Read, ListDir) пропускают проверку в режимах monitor/guard; они проверяются только в режиме strict.

PromptGuardian

Детектор инъекций промптов на основе LLM, используемый для обнаружения вредоносных инъекций в сообщениях Channel:

Режимы работы

РежимПоведение
offОтключён
monitorОбнаруживать и журналировать, не блокировать
blockБлокировать и возвращать отклоняющее сообщение при обнаружении инъекции

Механизм обнаружения

{/* Основной интерфейс PromptGuardian */}
class PromptGuardian {
async review(content: string, source: MessageSource): Promise<{
allowed: boolean;
isInjection: boolean;
confidence: number;
reason: string;
cached: boolean;
}>;
}
  • Кэш SHA-256 для уже проверенного содержимого
  • fail-open при таймауте/ошибке
  • Включён только для сообщений из Channel

InputSanitizer

Санитайзер на основе регулярных выражений, удаляющий опасные символы из сообщений:

{/* Упрощённый InputSanitizer */}
class InputSanitizer {
sanitize(input: string): string;
updateConfig(config: SanitizationConfig): void;
}

Удаляемые символы:

  • Символы нулевой ширины (ZWS, ZWNJ, ZWJ, ZWSP)
  • Управляющие символы направления текста Unicode
  • Невидимые символы форматирования
  • Управляющие символы (символы новой строки и табуляции сохраняются)

RateLimiter

Ограничитель частоты запросов со скользящим окном, поддерживающий лимиты на пользователя и глобальные лимиты:

{/* Конфигурация ограничителя частоты */}
interface RateLimitConfig {
perUser: {
maxRequests: number; // default 20
windowMs: number; // default 60000 (1 minute)
};
global: {
maxRequests: number; // default 100
windowMs: number; // default 60000
};
cleanupIntervalMs: number; // expired entry cleanup interval
}
class RateLimiter {
check(userId: string): { allowed: boolean; retryAfter?: number };
updateConfig(config: Partial<RateLimitConfig>): void;
}

UserPermissionService

Управление правами пользователей Channel с поддержкой автоматической регистрации и назначения ролей:

Система ролей

РольПраваОписание
adminВсеАдминистратор
moderatorОтправка сообщений + запуск Agent'овМодератор
guestОтправка сообщений (с ограничением частоты)Гость (роль по умолчанию)
blockedНетЗаблокированный пользователь
class UserPermissionService {
checkPermission(channelId: string, platformUserId: string): Promise<{
allowed: boolean;
role: UserRole;
user: ChannelUser;
}>;

autoRegister(channelId: string, platformUserId: string, displayName: string): Promise<ChannelUser>;
}

ChannelPermissionGate

Вызовы чувствительных инструментов из Channel требуют подтверждения пользователя:

sequenceDiagram
participant Agent as TinyElf Agent
participant Gate as PermissionGate
participant Channel as Channel Platform
participant User as Remote User

Agent->>Gate: requestPermission(toolCall)
Gate->>Gate: Generate confirmation fingerprint (SHA-256)
Gate->>Channel: Send confirmation message + fingerprint
Channel->>User: Agent wants to perform operation XX — reply YES to confirm

alt User confirms
User->>Channel: YES
Channel->>Gate: Match fingerprint
Gate-->>Agent: approved: true
else Timeout (60s)
Gate-->>Agent: approved: false, reason: timeout
else User denies
User->>Channel: NO
Gate-->>Agent: approved: false, reason: denied
end

AuditLogger

Журнал аудита безопасности, записывающий все события, связанные с безопасностью, в таблицу SQLite audit_log:

Типы событий

Тип событияОписаниеСерьёзность
tool_executedВызов инструмента выполненinfo
tool_blockedВызов инструмента заблокированwarning
tool_guardian_reviewРезультат проверки Guardianinfo/warning
permission_requestedЗапрос подтверждения разрешенияinfo
permission_grantedРазрешение одобреноinfo
permission_deniedВ разрешении отказаноwarning
rate_limitedСрабатывание ограничителя частотыwarning
injection_detectedОбнаружена инъекция промптаcritical
user_blockedПользователь заблокированwarning
{/* Запись журнала аудита */}
interface AuditLogEntry {
id: string;
timestamp: number;
eventType: AuditEventType;
severity: 'info' | 'warning' | 'critical';
channelId?: string;
userId?: string;
toolName?: string;
details: Record<string, unknown>;
}

SecurityService / CryptoService

Сервисы шифрования на уровне приложения, защищающие чувствительные данные, хранящиеся локально:

{/* Схема шифрования */}
class SecurityService {
encrypt(plaintext: string): string; // AES-256-GCM, IV + authTag + ciphertext
decrypt(ciphertext: string): string;
}

class CryptoService {
deriveKey(password: string, salt: Buffer): Buffer;
// PBKDF2, 100K iterations, SHA-512, 32 bytes (256 bits)
}

Все API-ключи шифруются через SecurityService.encrypt() перед сохранением в SQLite.

Связанные файлы

ФайлОписание
packages/desktop/app/main/services/platform/security/ExecutionFirewall.tsМежсетевой экран путей
packages/desktop/app/main/services/platform/security/GuardianAgent.tsПроверка инструментов AI
packages/desktop/app/main/services/platform/security/PromptGuardian.tsОбнаружение инъекций промптов
packages/desktop/app/main/services/platform/security/RateLimiter.tsОграничитель частоты
packages/desktop/app/main/services/platform/security/InputSanitizer.tsСанитайзер входных данных
packages/desktop/app/main/services/platform/security/UserPermissionService.tsПрава пользователей
packages/desktop/app/main/services/platform/security/ChannelPermissionGate.tsШлюз подтверждения разрешений
packages/desktop/app/main/services/platform/security/AuditLogger.tsЖурнал аудита
packages/desktop/app/main/services/platform/security/SecurityService.tsШифрование AES-256-GCM
packages/desktop/app/main/services/infra/crypto/CryptoService.tsПолучение ключей PBKDF2
packages/desktop/app/main/ipc/safe-handle.tsБезопасный дескриптор IPC
packages/desktop/app/preload/index.tsПредоставление интерфейса contextBridge
packages/desktop/app/shared/contracts/security-types.tsОбщие типы, связанные с безопасностью
packages/desktop/app/main/workers/db/auditLog.tsОперации с БД журнала аудита
packages/desktop/app/main/workers/db/channelUsers.tsОперации с БД пользователей Channel