
AI agent에게 npm install을 시키기 전에, denylist가 아닌 실행 경계를 결정하기
요약
AI 코딩 에이전트가 미지의 저장소에서 명령어를 실행할 때 발생할 수 있는 보안 위험을 방지하기 위한 전략을 다룹니다. 단순한 명령어 차단(denylist) 방식의 한계를 지적하며, 실행 경계(execution boundary)를 설정하고 읽기 전용 검사 단계를 우선시하는 접근법을 제안합니다.
핵심 포인트
- 단순 명령어 차단(denylist) 방식은 우회 가능성이 높아 보안에 취약함
- 실행 경계(execution boundary)를 설정하여 읽기/쓰기/네트워크 권한을 제어해야 함
- 설치 전 package.json의 install 스크립트를 먼저 검사하는 단계가 필요함
- 에이전트에게 읽기 전용 검사(read-only inspection)를 우선하도록 지침을 명시해야 함
최근의 coding agent는 외부 repo의 조사부터 setup까지 상당히 자연스럽게 수행해 줍니다.
README를 읽고, 의존 관계를 설치하고, dev server를 기동하고, 에러가 발생하면 수정합니다. 개인 개발에서는 이 부분을 맡길 수 있는 것만으로도 상당히 편합니다.
하지만, 미지의 repo에서 갑자기 npm install을 시키는 것은 조금 무책임하다고 생각합니다.
위험한 것은 rm -rf /와 같이 명확한 커맨드뿐만이 아닙니다. README, package.json, install script, network access, 로컬의 .env가 연결되면, 겉보기에는 평범한 setup처럼 보여도 상당히 곤란한 경로가 됩니다.
그래서 저라면 denylist를 늘리기 전에 실행 경계(execution boundary)를 결정하겠습니다.
terminal agent의 안전 대책으로 자주 쓰이는 방식은 위험한 command를 금지하는 방법입니다.
rm
curl
chmod
...
물론, 없는 것보다는 있는 편이 낫습니다. 허술한 사고는 줄어듭니다.
다만, 이것을 안전성의 중심으로 두는 것은 취약합니다.
curl을 금지해도 다른 방법으로 network에 접속할 수 있을지도 모릅니다. rm을 금지해도 덮어쓰기나 이동을 통해 같은 결과가 나타날 수도 있습니다. shell은 범위가 넓기 때문에, "이 문자열이 포함되어 있으면 중단한다"는 방식만으로는 금방 우회로가 생깁니다.
CmdNeedle의 논문(paper)에서도 실제 command denylist의 상당수가 매우 취약하다고 보고되고 있습니다. 세세한 수치보다 제가 신경 쓰고 싶은 부분은 여기입니다.
agent가 shell을 가지고 있다면, 문제는 "어떤 command를 금지할 것인가"만이 아닙니다.
어디를 읽을 수 있는가. 어디에 쓸 수 있는가. network에 접속할 수 있는가. secret이 보이는가. package script를 실행할 수 있는가.
이쪽을 먼저 결정하는 것이 운영 측면에서 강력합니다.
미지의 repo를 다룰 때, 저는 첫 단계를 read-only inspection에 집중시킵니다.
갑자기 이것을 하지 않습니다.
npm install
npm run dev
먼저 봅니다.
git clone <repo-url> /tmp/agent-inspect/app
cd /tmp/agent-inspect/app
cat package.json
...
install script도 먼저 확인합니다.
grep -R "\(preinstall\|install\|postinstall\|prepare\)" package.json .
여기서 하고 싶은 것은 완벽한 malware scan이 아닙니다.
우선 "이 repo는 setup 시에 무엇을 실행하려고 하는가"를 보는 것입니다. postinstall, prepare, node -e, binary download, curl | sh, 수상한 DNS lookup. 그런 것들이 나타난다면 그대로 agent에게 맡기지 않습니다.
README에 npm install && npm run dev라고 적혀 있다면, 사람이라도 반사적으로 입력하기 쉽습니다. agent는 훨씬 더 순순히 입력합니다.
그래서 instruction 측면에서 첫 번째 수를 바꿉니다.
## Unknown repository rules
- Start with read-only inspection.
- Do not run package install scripts during inspection.
...
"안전하게 확인해"라고 하는 것은 약합니다. 무엇을 먼저 읽을 것인지, 무엇을 아직 실행하지 않을 것인지를 명시합니다.
Node project에서 가장 고민스러운 점은 의존 관계의 install이 거의 setup과 동의어가 되어 있다는 점입니다.
frontend의 경우 npm install을 하지 않으면 타입(type)도 빌드(build)도 볼 수 없습니다. 하지만 install script를 그대로 실행하는 것은 두렵습니다.
처음에는 이것으로 충분합니다.
npm install --ignore-scripts
pnpm이라면 상황에 따라 script를 비활성화한 install을 사용합니다.
pnpm install --ignore-scripts
이렇게 하면 「dependency tree (의존성 트리)를 설치하는 것」과 「package script (패키지 스크립트)를 실행하는 것」을 분리할 수 있습니다.
그 후에, 필요한 script만 읽습니다.
cat package.json
npm pkg get scripts
lockfile의 차이점도 확인합니다.
git diff -- package-lock.json pnpm-lock.yaml yarn.lock
이 단계에서 agent에게 다음과 같이 묻는 것도 방법입니다.
install scripts를 실행하지 않고 의존성만 설치했습니다.
다음에 실행하고 싶은 package script와 그 이유를 나열해 주세요.
중요한 것은, agent에게 「다음 command를 생각하게 하는 것」이 아니라, 「다음 command를 설명하게 한 뒤에 실행하는 것」입니다.
이 부분이 현실적으로 사고가 가장 발생하기 쉬운 지점이라고 생각합니다.
개인 개발의 작업 디렉토리에는 대개 무언가가 들어있습니다.
.env
.env.local
~/.ssh
...
알 수 없는 repo를 agent에게 맡길 때, 이것들이 동일한 실행 환경에서 읽힐 수 있는 것은 좋지 않습니다.
저라면 clone 하는 위치를 평소의 workspace와 분리하겠습니다.
mkdir -p /tmp/agent-runs
git clone <repo-url> /tmp/agent-runs/app
cd /tmp/agent-runs/app
나아가, agent에게 전달할 instruction (지시 사항)도 명확하게 작성합니다.
## Secret boundary
- Do not read `.env`, `.env.local`, SSH keys, GitHub tokens, or cloud credentials.
- Do not inspect files outside this repository.
...
사실 container (컨테이너)나 disposable VM (일회용 가상 머신)을 사용하는 편이 더 좋습니다.
하지만 매번 그렇게까지 하기는 많은 사람에게 부담이 될 것입니다. 따라서 최소한의 가이드라인으로서, 평소 사용하는 repo와 secret (비밀 정보) 근처에서 unknown setup (알 수 없는 설정)을 실행하지 않는 것, 우선 이것부터 시작해도 좋다고 생각합니다.
setup 과정은 network (네트워크)로 연결되는 경우가 많습니다.
package install, binary download, postinstall의 telemetry (원격 측정), browser automation (브라우저 자동화), API call. 이 모든 것을 영구적으로 금지하는 것은 무리가 있습니다.
하지만, 첫 번째 inspection (검사) 단계에서 자유롭게 외부로 나갈 필요는 없습니다.
저라면 agent에게 다음과 같은 순서를 요구하겠습니다.
1. file inspection
2. package scripts inspection
3. install without scripts
...
특히, setup 도중에 「다른 command를 실행해서」 「이 URL을 호출해서」 「DNS로부터 payload를 읽는」 식의 흐름이 나타나면 중단합니다.
Mozilla 0din의 데모 사례로 보도되었던 예에서도, 깨끗해 보이는 repo와 README의 setup 절차를 기점으로, agent가 친절하게 다음 command로 진행해 나가는 경로가 문제가 되었습니다. 각 단계는 단독으로는 크게 수상해 보이지 않습니다. 하지만 이를 연결하면 상당히 위험해집니다.
이런 종류의 문제는 「agent가 어리석어서 발생하는 것」이라기보다, agent가 setup을 완료하려고 노력하기 때문에 발생합니다.
그러므로 노력하는 범위를 이쪽에서 끊어주어야 합니다.
repo instruction에 security policy (보안 정책)를 작성한다면, 긴 문장보다는 짧은 실행 규칙이 더 효과적입니다.
저라면 이 정도로 작성하겠습니다.
## Unknown dependency and setup rules
- Inspect `package.json`, lockfiles, and install scripts before installing.
- Use `npm install --ignore-scripts` or equivalent for the first install.
...
이 문구는 평범해 보입니다. 하지만 agent에게 기대하는 행동이 상당히 명확해집니다.
「안전하게 해줘」라고 하면, agent는 안전해 보이는 summary (요약)를 내놓고 그대로 setup을 진행해 버릴 수도 있습니다.
「approval (승인) 없이 install script (설치 스크립트)를 실행하지 마라」라고 한다면, 멈춰야 할 지점이 생깁니다.
실제로 build (빌드)나 test (테스트)를 돌리는 단계에서는, 어딘가에서 script (스크립트)를 실행해야 할 필요가 있습니다.
그때는, 버려도 상관없는 환경으로 몰아넣습니다.
Docker (도커)라면 대략 이런 형태부터 시작할 수 있습니다.
docker run --rm -it \
--network none \
-v "$PWD":/work:ro \
...
read-only mount (읽기 전용 마운트)와 network none (네트워크 없음) 상태이므로, 그대로 개발에 사용하기는 어렵습니다. 첫 번째 확인용입니다.
install (설치)이 필요하다면, copy (복사)한 disposable directory (일회용 디렉터리)에서 실행합니다.
tmpdir=$(mktemp -d)
rsync -a --exclude node_modules . "$tmpdir/app"
cd "$tmpdir/app"
...
그 후, 어떻게든 script (스크립트)가 필요하다면, 실행 전에 읽습니다.
npm pkg get scripts
여기서 postinstall이 native binary (네이티브 바이너리)를 내려받거나, prepare가 다른 script (스크립트)를 호출하거나, README가 추가 command (명령어)를 요구하는 경우가 있습니다. 그런 경우에는 agent (에이전트)에게 통째로 맡겨두지 않습니다.
매번 전부를 엄격하게 수행하는 것은 불가능합니다.
하지만, 미지의 repo (레포지토리)를 agent (에이전트)가 만지게 한다면, 이 정도는 확인합니다.
Workspace (워크스페이스):
- 평소 작업하는 repo (레포지토리)가 아니라 temporary directory (임시 디렉터리)에 clone (클론)했는가
- agent (에이전트)에게 불필요한 parent directory (상위 디렉터리)를 보여주고 있지는 않은가
...
이 체크리스트가 완벽하지는 않습니다.
다만, agent (에이전트)에게 npm install을 통째로 떠넘기는 것보다는 훨씬 낫습니다. 특히 개인 개발에서는 production token (프로덕션 토큰)이나 admin credential (관리자 자격 증명)이 손에 가까이 있는 경우가 많으므로, secret boundary (비밀 경계)만이라도 효과가 있습니다.
AI agent (AI 에이전트)의 security (보안)는 agent (에이전트)의 성격이나 똑똑함만으로 결정되지 않습니다.
오히려 실행 환경 쪽이 더 중요합니다.
미지의 repo (레포지토리)에서는 우선 읽습니다. install script (설치 스크립트)를 무효화하고 의존 관계를 봅니다. secret (비밀 정보)이 보이는 곳에서 setup (설정)을 실행하지 않습니다. network access (네트워크 액세스)는 필요해진 뒤에 엽니다. agent (에이전트)에게는 command (명령어)를 실행하기 전에 목록을 나열하게 합니다.
denylist (차단 목록)는 있어도 좋습니다. 하지만 그것을 주인공으로 만들지는 마세요.
agent (에이전트)가 친절하게 작업을 진행할 수 있는 시대이기에, 그 친절함이 닿는 범위를 이쪽에서 미리 정해둘 필요가 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기