패키지 공급망 보안 — 최소 공개 기간 설정
AI 에이전트가 자동으로 npm install·uv add·pnpm add를 실행하는 시대에는 방금 발행된 패키지를 곧바로 받아오는 것이 위험합니다. 한 줄 설정으로 최소 N일 지난 버전만 설치하도록 만들 수 있습니다.
Hacker News PSA — 원문
1. 왜 최소 공개 기간이 필요한가? #
현대 공급망 공격은 보통 다음 흐름을 따릅니다.
- 공격자가 잘 알려진 패키지의 유지자 계정을 탈취하거나 비슷한 이름으로 typosquatting 패키지를 발행
- 새 버전(또는 신규 패키지)이 npm·PyPI 등 레지스트리에 게시됨
- 몇 시간~며칠 안에 누군가
npm install·pip install을 실행 → 악성 코드 실행 - 일반적으로 1~7일 안에 보안 스캐너·커뮤니티가 발견하고 게시 취소·CVE 발급
최소 공개 기간을 7일로 설정하면 위 흐름의 3·4 단계 사이에 안전 지대가 생깁니다. 갓 게시된 악성 버전은 자동으로 설치 거부되고, 보안 스캐너가 1주일 동안 검토할 시간을 벌어줍니다.
npm install some-new-package를 자동 실행할 수 있어, 사람보다 훨씬 짧은 응답 시간 안에 악성 패키지를 끌어옵니다. 본 가이드의 설정은 Permissions·Harness Engineering 보안 레이어와 함께 적용하는 것을 권장합니다.
2. 패키지 매니저별 설정 방법 #
네 도구가 같은 개념을 다른 키 이름·다른 시간 단위로 노출합니다. 모두 7일 기준으로 통일한 예시입니다.
2.1 npm
min-release-age=7 # 단위: 일(days)
ignore-scripts=true # lifecycle 스크립트도 함께 차단 (3절 참고)
워크스페이스 단위로 적용하려면 프로젝트_루트/.npmrc에 동일 내용을 둡니다. 두 위치가 모두 존재하면 워크스페이스 설정이 우선합니다.
2.2 pnpm
minimum-release-age=10080 # 단위: 분(minutes) → 7일 = 10080분
설정 파일 경로는 OS·pnpm 버전에 따라 다릅니다. 다음 명령으로 확인할 수 있습니다.
pnpm config get globalconfig
2.3 bun
[install]
minimumReleaseAge = 604800 # 단위: 초(seconds) → 7일 = 604,800초
워크스페이스 단위 적용은 프로젝트 루트의 bunfig.toml에 같은 키를 둡니다.
2.4 uv
exclude-newer = "7 days" # 자연어 표현 그대로 사용 가능
uv는 ISO 날짜("2026-05-03")도 지원합니다. 특정 시점 이후 발행 버전을 모두 막고 싶다면 유용합니다.
2.5 단위 차이 한눈에 보기
| 도구 | 설정 키 | 단위 | 7일 값 | 설정 파일 |
|---|---|---|---|---|
| npm | min-release-age |
일(days) | 7 |
.npmrc |
| pnpm | minimum-release-age |
분(minutes) | 10080 |
pnpm rc |
| bun | install.minimumReleaseAge |
초(seconds) | 604800 |
.bunfig.toml |
| uv | exclude-newer |
자연어 / ISO 날짜 | "7 days" |
uv.toml |
2.6 프롬프트로 설정 요청하기
위 표를 모두 외울 필요는 없습니다. 사용 중인 패키지 매니저를 알려주고 에이전트에게 직접 작성을 부탁하세요. 그대로 복사·붙여넣기해 쓸 수 있는 예시입니다.
- 이 워크스페이스에 최소 공개 기간 7일 정책을 적용해 줘. 사용 중인 패키지 매니저(npm·pnpm·bun·uv)를 자동으로 감지해서 해당 도구의 설정 파일에만 키를 추가하고, 이미 설정이 있다면 값만 비교해 알려줘. 변경 전후의 diff를 한 줄씩 보여줘.
- ~/.npmrc, ~/.bunfig.toml, ~/.config/uv/uv.toml, pnpm rc 네 파일에 각각 최소 공개 기간 7일을 한 번에 적용해 줘. 단위가 다르니 npm=일, pnpm=분, bun=초로 환산해서 정확한 값을 넣고, 파일이 없으면 만들어 줘. 작업 끝나면 각 파일을 cat으로 출력해 결과를 보여줘.
- npm은 lifecycle 스크립트도 함께 차단하고 싶어. ~/.npmrc에 ignore-scripts=true도 추가해 줘. 단, esbuild·sharp 등 native binary가 필요한 패키지 빌드는 안내 메시지에 정리해 줘.
- 지금 설정을 7일 → 14일로 늘리고 싶어. 네 도구(npm·pnpm·bun·uv) 모두 한 번에 바꿔 줘. 값만 바꾸고 다른 키는 건드리지 마.
- 최소 공개 기간 설정을 임시로 끄고 한 번만 새 버전을 설치하고 싶어. 영구 변경은 하지 말고, 그 명령에만 적용되는 명령행 옵션 형태로 알려줘. (해당 패키지 매니저에서 지원하지 않으면 그 사실을 알려줘.)
💡 사용 중인 도구를 명시하면 더 정확합니다 (예: "이 프로젝트는 pnpm 사용"). 시스템 전역(~/.npmrc)과 워크스페이스(.npmrc)를 함께 다룰 때는 어느 쪽을 우선할지 함께 말해주세요.
3. lifecycle 스크립트 차단 #
최소 공개 기간은 "갓 발행된 악성 코드"를 막지만, 이미 설치 시점에 코드가 실행되는 npm lifecycle 스크립트는 별도 위험입니다. preinstall·install·postinstall은 npm install 한 번으로 임의 코드를 실행시키는 가장 흔한 공격 경로입니다.
| 도구 | 기본 동작 | 차단 설정 |
|---|---|---|
| npm | 모든 lifecycle 스크립트 자동 실행 | ignore-scripts=true in .npmrc |
| pnpm | 대부분 차단 (allow 목록 필요) | 기본값 사용 권장. 필요한 패키지만 pnpm.allowedScripts에 추가 |
| bun | 기본 차단 (신뢰 목록 기반) | 기본값 사용 권장 |
| uv | Python 생태계 — npm lifecycle 개념 없음 | — |
npm 사용자는 ignore-scripts=true를 반드시 설정해야 합니다. 일부 도구(esbuild, sharp 등)는 postinstall이 정상 워크플로지만, 그 경우 npm install --ignore-scripts 이후 필요한 도구만 수동으로 npm rebuild <패키지> 하는 패턴이 안전합니다.
npm install 한 번만으로 토큰·SSH 키·환경변수가 외부로 유출됐습니다.
4. AI 에이전트가 이 제약을 알게 하기 #
설정만 해 두면 에이전트가 새 패키지를 설치 시도할 때 명령이 실패합니다. 에이전트가 실패 원인을 모르면 같은 명령을 반복하거나 우회를 시도할 수 있어, Rules에 안내 문구를 한 줄 추가하는 것이 효과적입니다.
# description: npm/pnpm/bun/uv로 패키지를 설치하는 모든 작업에 적용
# globs: **/package.json, **/pyproject.toml, **/uv.toml, **/.npmrc
## 패키지 설치 보안 규칙
이 워크스페이스는 **최소 공개 기간 7일** 정책을 사용한다.
- 새 패키지·새 버전 설치 시도가 "minimum release age" 또는
"package is too new" 같은 오류로 실패할 수 있다.
- 그 경우 절대로 `--ignore-min-age` 같은 우회 옵션을 추가하지 말 것.
- 대신 사용자에게 다음을 보고한다.
1. 어떤 패키지·어떤 버전이 막혔는지
2. 안전한 대안 버전(7일 이상 지난 가장 최신 버전)이 있는지
3. 그 패키지가 정말 필요한지
`ignore-scripts=true` 정책 때문에 일부 네이티브 패키지(`esbuild`,
`sharp` 등)는 설치 후 `npm rebuild <name>`이 필요할 수 있다. 이 경우만
사용자 확인을 받고 명시적으로 `npm rebuild`를 실행한다.
같은 내용을 CLAUDE.md·GEMINI.md 전역 규칙으로 두면 모든 워크스페이스에서 일관되게 동작합니다.
- 이 워크스페이스의 .agent/rules/ 디렉터리에 package-security.md를 만들어 줘. 다음 내용을 포함: (1) 패키지 매니저 작업 시작 전 최소 공개 기간 정책을 안내, (2) "minimum release age" 오류 발생 시 우회 옵션 사용 금지, (3) 사용자에게 막힌 패키지·버전·안전한 대안 버전을 한 줄씩 보고. globs는 package.json, pyproject.toml, .npmrc로 한정해 줘.
- 현재 ~/.gemini/GEMINI.md(또는 ~/.claude/CLAUDE.md)에 패키지 설치 보안 규칙 단락을 추가해 줘. 이미 비슷한 단락이 있으면 중복 추가하지 말고 누락된 규칙만 보강해 줘. 변경 전후 diff를 보여줘.
- 방금 추가한 규칙이 실제로 동작하는지 검증하고 싶어. 일부러 새로 게시된 가짜 패키지를 설치하는 명령을 시뮬레이션하고, 에이전트가 어떤 메시지를 사용자에게 돌려주는지 시나리오로 작성해 줘.
💡 Rule 본문이 길어지면 에이전트가 일부만 읽는 경우가 생깁니다. "우회 금지"·"보고 형식" 같은 핵심 두 줄을 가장 앞에 두는 것이 효과적입니다.
5. 자주 묻는 질문 & 실패 처리 #
한 번만 우회하려면 명령에 환경변수나 옵션으로 임시 해제합니다. 예:
npm install --foreground-scripts=false --min-release-age=0 <패키지>. 하지만 가능하면 전역 설정을 잠시 끄지 말고, 신뢰할 만한 다른 경로(공식 깃허브 릴리스, 미러 등)를 우선 검토하세요.
예, 특히 CI는 정기적으로
npm install을 다시 실행하므로 같은 정책이 필수입니다. GitHub Actions의 경우 .npmrc·uv.toml을 저장소에 커밋하거나, 워크플로에서 환경변수(NPM_CONFIG_MIN_RELEASE_AGE=7)로 주입합니다.
이미 합의된 버전만 설치하므로 어느 정도 안전합니다. 그러나
npm install <새 패키지>·npm update 같은 명령은 lock에 없는 새 항목을 추가하므로 위험. 최소 공개 기간 설정은 lock 파일 사용 여부와 무관하게 켜두는 것이 좋습니다.
pip에는 공식
min-release-age 옵션이 없습니다. uv로 전환하거나, pip-audit·safety 같은 보안 스캐너를 별도 단계로 추가하세요. uv는 pip 명령(uv pip install ...) 대체로 거의 그대로 쓸 수 있습니다.