본문으로 건너뛰기

보안 계층

TinyElf Agent 엔진은 도구 호출이 실행되기 전에 충분한 보안 검증을 거치도록 3계층 보안 파이프라인을 구현합니다. 세 계층은 순서대로 실행되며, 어느 계층에서든 거부되면 프로세스가 종료됩니다.

3계층 보안 파이프라인

graph TB
TC["Tool call request<br/>(toolName, args)"] --> L1

subgraph Layer1["Layer 1: ExecutionFirewall"]
L1["checkToolCall(name, args)"] --> L1a{"Path in deny list?"}
L1a -->|Yes| Block1["Block<br/>[firewall] Blocked"]
L1a -->|No| L1b{"Bash command contains deny path?"}
L1b -->|Yes| Block1
L1b -->|No| Pass1["Pass"]
end

Pass1 --> L2

subgraph Layer2["Layer 2: GuardianAgent"]
L2["review(name, args)"] --> L2a{"Mode = off?"}
L2a -->|Yes| Pass2a["Skip"]
L2a -->|No| L2b{"In review scope?"}
L2b -->|No| Pass2b["Skip"]
L2b -->|Yes| L2c["LLM risk assessment<br/>(15s timeout)"]
L2c --> L2d{"Risk level decision"}
L2d -->|"guard: high/critical"| Block2["Block<br/>[guardian] Blocked"]
L2d -->|"strict: medium+"| Block2
L2d -->|Allow| Pass2["Pass"]
end

Pass2 --> L3
Pass2a --> L3
Pass2b --> L3

subgraph Layer3["Layer 3: PermissionCallback"]
L3["isSensitiveTool(name)"] --> L3a{"Requires confirmation?"}
L3a -->|No| Pass3a["Auto-pass"]
L3a -->|Yes| L3b["Send confirmation request<br/>(5min timeout)"]
L3b --> L3c{"User reply"}
L3c -->|Allow| Pass3["Pass"]
L3c -->|AllowSession| Pass3s["Pass + cache"]
L3c -->|Deny/Timeout| Block3["Deny"]
end

Pass3 --> Exec["Execute tool"]
Pass3a --> Exec
Pass3s --> Exec

계층 1: ExecutionFirewall

ExecutionFirewall는 경로 패턴 규칙을 기반으로 민감한 시스템 리소스 접근을 차단하는 결정론적 보안 게이트웨이입니다.

인터페이스

class ExecutionFirewall {
constructor(workspacePath: string);
checkPath(filePath: string, operation?: 'read' | 'write'): FirewallCheckResult;
checkCommand(command: string): FirewallCheckResult;
checkToolCall(toolName: string, params: Record<string, unknown>): FirewallCheckResult;
setWorkspacePath(newPath: string): void;
}

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

거부 경로 규칙

읽기와 쓰기 모두 거부

경로 패턴이유
C:\Windows\Windows 시스템 디렉터리
C:\Program Files\프로그램 설치 디렉터리
C:\ProgramData\프로그램 데이터 디렉터리
C:\Recovery\복구 파티션
/etc/시스템 구성 디렉터리
/usr/시스템 디렉터리
/sbin/시스템 바이너리
/boot/부트 디렉터리
/proc/프로세스 의사 파일 시스템
/sys/시스템 의사 파일 시스템
/dev/디바이스 파일
.ssh/SSH 키 디렉터리
.gnupg/GPG 키 디렉터리
.aws/AWS 자격 증명
.azure/Azure 자격 증명
.gcloud/GCloud 자격 증명
.kube/configKubernetes 구성
.docker/config.jsonDocker 구성
id_rsa, id_ed25519, id_ecdsaSSH 개인 키 파일
.env, .env.*환경 변수 파일
credentials.json자격 증명 파일
service_account*.json서비스 계정 키
Firefox/Chrome/Edge user data브라우저 구성 파일
System32\config\SAM/SYSTEM/...Windows 레지스트리

쓰기만 거부(읽기 가능)

경로 패턴이유
.gitconfigGit 구성
.npmrcnpm 구성
.bashrcBash 구성
.zshrcZsh 구성
.profileShell 구성
.bash_profileBash 구성

도구 경로 매개변수 매핑

도구추출된 경로 매개변수작업 유형
Readpath, file_pathread
Writepath, file_pathwrite
Editpath, file_pathwrite
ListDirpath, dir_pathread

명령 경로 추출

checkCommand()는 Shell 명령에서 경로를 추출하고 각각을 검사합니다.

  • Windows 절대 경로: C:\path\to\file
  • POSIX 절대 경로: /path/to/file

계층 2: GuardianAgent

GuardianAgent는 LLM을 사용해 도구 호출의 보안 위험 평가를 수행합니다.

인터페이스

class GuardianAgent {
constructor(
config: GuardianAgentConfig,
llmAdapter: TinyElfLLMAdapter,
auditLogger?: AuditLogger,
sessionId?: string,
workspacePath?: string,
);
review(toolName: string, args: Record<string, unknown>): Promise<GuardianReviewResult>;
clearCache(): void;
}

interface GuardianReviewResult {
allowed: boolean;
riskLevel: RiskLevel; // 'none' | 'low' | 'medium' | 'high' | 'critical'
reason: string;
cached: boolean;
}

작업 모드

모드검토 범위차단 조건제한 시간/오류 동작
off없음차단 없음N/A
monitor민감한 도구차단 없음(로깅만)N/A
guard민감한 도구high, criticalFail open(허용)
strict모든 도구medium, high, criticalFail closed(거부)

민감한 도구 목록

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

monitorguard 모드는 이 도구들만 검토합니다. strict 모드는 모든 도구를 검토합니다.

위험 수준

수준의미예시
none완전히 안전워크스페이스 내부 파일 읽기
low낮은 위험프로젝트 파일 쓰기
medium중간 위험패키지 설치, 상태 수정
high높은 위험워크스페이스 외부 작업, 네트워크 요청, 자격 증명 접근
critical치명적 위험재귀 삭제, 권한 상승, 데이터 탈취

핵심 규칙(항상 high/critical로 표시)

  • 워크스페이스 외부 파일 삭제
  • 재귀 삭제 명령(rm -rf, del /s /q, Remove-Item -Recurse)
  • 시스템 디렉터리 작업(/etc, C:\Windows)
  • 사용자 개인 디렉터리 작업(Desktop, Documents)
  • 권한 상승(sudo, runas)
  • 파이프 실행(curl | sh)
  • 보안 우회(--no-verify, --force)
  • 자격 증명 접근

캐싱 전략

  • 승인 결과를 캐시합니다(sha256(toolName:args)의 처음 16비트를 키로 사용).
  • 허용 결과만 캐시합니다(거부 결과는 매번 다시 평가).
  • 세션 종료 시 캐시를 지웁니다.

상수

상수설명
GUARDIAN_TIMEOUT_MS15,000LLM 평가 제한 시간

계층 3: PermissionCallback

TinyElfPermissionManager

class TinyElfPermissionManager {
resolvePermission(requestId: string, result: PermissionResult): void;
cleanupSession(dbSessionId: string): void;
requestPermission(dbSessionId, sender, toolName, toolInput): Promise<PermissionResult>;
buildPermissionCallback(dbSessionId, sender, permissionMode, channelCtx, channelPermissionGate):
((toolName, toolInput) => Promise<PermissionResult>) | undefined;
}

interface PermissionResult {
behavior: 'allow' | 'deny' | 'allowSession';
message?: string;
}

권한 모드 동작

모드buildPermissionCallback 반환값
bypassPermissionsundefined(콜백을 주입하지 않으며 모든 도구가 자동 통과)
defaultDesktop 콜백 / Channel 콜백
acceptEdits기본값과 동일하지만 Write/Edit는 isSensitiveTool에서 false를 반환

세션 수준 화이트리스트

사용자가 allowSession을 선택하면 다음이 적용됩니다.

sessionAllowedTools: Map<string, Set<string>> // dbSessionId → Set<toolName>

이후 같은 도구 이름의 호출은 자동 통과합니다. 화이트리스트는 cleanupSession()에서 지워집니다.

제한 시간

  • Desktop 모드: 5분 제한 시간 → 자동 거부
  • Channel 모드: 5분 제한 시간 → 자동 거부

ChannelPermissionGate

Channel 시나리오의 권한 확인은 메시지 라우팅을 통해 구현됩니다.

확인 흐름

sequenceDiagram
participant A as TinyElfAgentLoop
participant G as ChannelPermissionGate
participant C as Channel (Discord/Telegram)
participant U as Channel User

A->>G: requestConfirmation(channelId, chatId, senderId, toolName, desc)
G->>G: Check always-allow list
alt Already in always-allow
G-->>A: true
else Not in list
G->>C: sendMessage("Confirmation needed: Bash: npm test")
C->>U: Display confirmation message
U->>C: Reply "y" / "n" / "always"
C->>G: processReply(channelId, chatId, senderId, "y")
G-->>A: true / false
end

지문 범위

always 응답의 범위는 buildToolFingerprint()로 결정됩니다.

fingerprint = `${toolName}:${description.slice(0, 120)}`

예를 들어 Bash:npm testBash:rm -rf /는 서로 다른 지문이므로 별도 승인이 필요합니다.

상수

상수설명
MAX_PENDING5Channel당 최대 대기 중 확인 수
TIMEOUT_MS300,000확인 제한 시간(5분)

응답 파싱

지원되는 확인 응답은 다음과 같습니다(대소문자 구분 없음, Discord 멘션 등 자동 제거).

응답동작
y, yes, approve, ok, 确认, はい허용
n, no, deny, cancel, 拒绝, いいえ거부
always, always allow, 始终允许, 常に許可허용 + 항상 허용 목록에 추가

AuditLogger

모든 보안 이벤트는 audit_log 데이터베이스 테이블에 기록됩니다.

이벤트 유형

type AuditEventType =
| 'firewall_block'
| 'permission_denied'
| 'permission_granted'
| 'permission_timeout'
| 'rate_limited'
| 'role_changed'
| 'user_created'
| 'user_deleted'
| 'injection_detected'
| 'command_blocked'
| 'guardian_review'
| 'prompt_review';

type AuditSeverity = 'info' | 'warn' | 'critical';

쓰기 전략

  • Fire-and-forget: 비동기 쓰기이며 기본 흐름을 차단하지 않습니다.
  • 쓰기 실패는 조용히 처리되며(console.warn) 도구 실행에 영향을 주지 않습니다.

주요 파일

파일경로설명
ExecutionFirewallplatform/security/ExecutionFirewall.ts경로 방화벽
GuardianAgentplatform/security/GuardianAgent.tsAI 보안 평가
AuditLoggerplatform/security/AuditLogger.ts감사 로깅
ChannelPermissionGateplatform/security/ChannelPermissionGate.tsChannel 권한 게이트
InputSanitizerplatform/security/InputSanitizer.ts입력 정리
PromptGuardianplatform/security/PromptGuardian.ts프롬프트 인젝션 감지
RateLimiterplatform/security/RateLimiter.ts속도 제한
UserPermissionServiceplatform/security/UserPermissionService.ts사용자 역할 권한
TinyElfPermissionsagent-core/engine/tinyelf/TinyElfPermissions.ts권한 관리자

모든 경로는 packages/desktop/app/main/services/ 기준 상대 경로입니다.

확장 지점

  • 사용자 지정 거부 경로: SYSTEM_DENIED_PATHS 배열에 규칙 추가
  • 사용자 지정 Guardian 시스템 프롬프트: SYSTEM_PROMPT 상수 수정
  • 사용자 지정 감사 이벤트: AuditEventType에 새 유형 추가
  • 사용자 지정 Channel 응답: processReply의 정규식 매칭 확장

관련 모듈

모듈경로관계
TinyElfAgentLoopagent-core/engine/tinyelf/TinyElfAgentLoop.ts보안 파이프라인 호출자
TinyElfSessionRunneragent-core/engine/tinyelf/TinyElfSessionRunner.tsFirewall/Guardian 인스턴스화
ShellToolagent-core/engine/tinyelf/tools/ShellTool.ts내장 명령 블랙리스트
TinyElfLLMAdapteragent-core/engine/tinyelf/TinyElfLLMAdapter.tsGuardian이 사용하는 LLM