조직의 mods 관리
기준일: 2026-10-23
공식 기준: Manage mods for your organization
개요
mod는 설치한 사용자의 권한으로 Claude Code 안에서 코드를 실행하는 플러그인이며 샌드박스되지 않습니다. managed settings로 사용자 기기에서 mod가 실행될지, 어떤 mod가, 어떤 순서로 실행될지 정합니다. 다른 mod를 감시·거부하는 조직 자체 mod도 설치할 수 있습니다.
대상: 파일, MDM, claude.ai 관리 콘솔로 Claude Code managed settings를 배포하는 사람. mod는 Claude Code v2.1.286 이상에서 기본 켜짐입니다.
- 사용자 자체 mod 차단 (조직 mod 유무 무관): 사용자 설치 mod 차단
- 아무것도 안 바꿨을 때 사용자가 얻는 것: 기본 동작
- 다른 제한으로 mod를 켜 둠: 허용 범위 선택
managed settings를 처음 배포한다면 Deploy managed settings, 설치 가능한 플러그인 제어는 Manage plugins for your organization를 보세요.
핵심 개념
| 개념 | 설명 |
|---|---|
| 내장 guard | Claude Code가 사용자 mod보다 먼저 로드하는 정책 mod sec-default@builtin (/plugin, 디버그 로그에서는 cc-plugin-sec-default) |
allowManagedModsOnly |
guard 옵션. 사용자가 가져온 mod의 hook 실행 거부 |
allowManagedHooksOnly |
더 넓은 설정. 조직 mod와 내장 mod만 로드, 사용자 settings 파일의 hook도 차단 |
disableAllHooks |
가장 넓은 설정. 모든 설치 mod와 settings hook 정지 |
disableSideloadFlags |
--plugin-dir, --plugin-url 시작 시 거부, 세션 중 Claude가 쓴 mod 로드 차단 (--agents, --mcp-config도 거부) |
| 조직의 mod | managed enabledPlugins true + managed settings가 마켓을 절대 경로 디렉터리로 지정 + 마켓이 상대 경로로 플러그인을 나열해 제자리 로드되는 mod |
prependPlugins / appendPlugins |
조직 mod의 순서 (사용자 mod 앞/뒤) |
plugin.register |
다른 mod가 로드되기 직전 정책 mod가 받는 이벤트 |
상세
사용자 설치 mod 로드 차단
사용자가 가져온 모든 mod의 hook 실행을 막으려면 내장 guard에 allowManagedModsOnly 옵션을 설정합니다. 이 옵션은 managed settings의 pluginConfigs 아래, cc-plugin-sec-default@builtin을 키로 둡니다.
{
"pluginConfigs": {
"cc-plugin-sec-default@builtin": {
"options": {
"allowManagedModsOnly": true
}
}
}
}
managed settings에 옵션을 두면:
- 사용자가 가져온 mod의 hook은 실행되지 않음: 사용자가 설치한 플러그인의 mod,
--plugin-dir로 로드한 mod, 세션 중 Claude가 쓴 mod - 조직의 mod는 계속 실행: 조직의 것으로 인정되는 mod는 검사하지 않음. 그 외 모든 mod는 사용자 것으로 거부. GitHub 등 원격 마켓플레이스에서 켠 플러그인의 mod, claude.ai에서 조직이 구성원에게 켠 mod 포함. 조직 것으로 인정되는 mod가 없으면 설치된 mod는 hook을 실행하지 않음
- 사용자는 되돌릴 수 없음: guard가 managed settings에서만 옵션을 읽으므로 사용자/프로젝트/로컬 settings 파일이나
--settings파일의 같은 항목은 효과 없음 - 파일 또는 MDM 정책은 모든 제공자에 적용: Amazon Bedrock, Google Cloud Agent Platform, Microsoft Foundry에서도 동일. claude.ai 관리 콘솔 배포는 Platform availability
- 사용자의 다른 커스터마이징은 유지: settings 파일의 hook과 플러그인
hooks/hooks.json의 hook, status line,/goal은 영향 없음 - 내장 mod는 계속 실행:
AGENTS.md지원 등은 각자 자체 스위치
사용자 기기에서 확인: claude --plugin-dir ./first-mod처럼 mod가 든 디렉터리로 시작합니다. mod hook이 실행되지 않고 transcript와 디버그 로그에 mod 이름과 allowManagedModsOnly를 담은 guard 메시지가 나옵니다. 메시지가 없으면 Check that a policy is in force와 옵션 적용 규칙을 보세요.
초기 접근 때 CLAUDE_CODE_ENABLE_FUNCTION_HOOKS를 0으로 설정했다면 이 옵션으로 대체하세요. v2.1.287 이상은 변수를 어떤 값이든 무시하므로 0이어도 mod가 켜져 있습니다.
기본 동작
mod 설정이 없을 때 사용자가 얻는 것:
mod는 켜짐: 플러그인 설정이 허용하는 어느 마켓플레이스의 mod 포함 플러그인도 설치 가능,
--plugin-dir로 디렉터리 로드 가능내장 guard가 먼저 실행:
sec-default@builtin을 모든 사용자 mod보다 앞서 로드하며 사용자가 끌 수 없음. 로드 조건은 둘 중 하나:- 기기에 managed settings가 있음
- 세션이 Team 또는 Enterprise 플랜의
/login자격 증명으로 실행됨
API 키, Amazon Bedrock, Google Cloud Agent Platform, Microsoft Foundry 사용자는 managed settings가 있는 기기에서만 guard가 로드됩니다. 장기
CLAUDE_CODE_OAUTH_TOKEN으로 도는 세션은 기기에 Team/Enterprise 로그인이 저장돼 있어도 그 로그인으로 치지 않습니다.guard가 보호하는 것: 사용자 mod는 관리되는 hook이 받거나 결정하는 것, 시스템 프롬프트, 관리되는
CLAUDE.md등 지침, mod가 settings로 읽는 것, 관리되는 MCP 서버의 도구와 설명을 바꿀 수 없음관리 범위 밖에서는 사용자 mod가 그 사용자로 행동: 파일 읽기/쓰기, 프로세스 시작, 네트워크 요청, 도구 호출·프롬프트 재작성, 도구 호출 거부, 인터페이스 그리기 가능
권한 규칙과
PreToolUsehook이 사용자 mod보다 우선: guard가 로드되면 기본적으로 사용자 mod는 어느 settings 파일의 규칙이든deny규칙이 거부하거나ask규칙이 묻는 호출을 승인할 수 없고,PreToolUsehook이 막은 호출도 승인 불가. 단 이 규칙과 hook은 Claude의 도구 호출에 적용되며 mod의$.fs,$.process호출에는 아닙니다.Read(.env)가 거부돼도 mod는$.fs.read로 읽거나 읽는 프로그램을 시작할 수 있습니다. 제한하려면 mod 로드를 막거나 정책 mod로 처리하세요일부 프롬프트는 여전히 건너뜀: auto mode에서 mod가 승인한 호출은 classifier 검사 없이 실행. 단 guard가 로드됐고 세션이
--permission-prompt-tool이나 Agent SDKcanUseTool콜백으로 돌면 classifier가 먼저 검사. mod 승인이 건너뛰지 못하는 프롬프트는 Extend permissions with hooks
guard 소스는 Claude Code 저장소의 mods/sec-default에 공개돼 있습니다.
계속 적용되는 제어
- Settings hook은 계속 동작: settings 파일과 플러그인
hooks/hooks.json의 command, HTTP, prompt, agent hook이 mod와 함께 평소대로 실행 - guard가 로드되는 곳에서 deny 규칙이 우선:
allowModsToOverrideDenyRules를 설정하지 않는 한 사용자 mod는deny규칙이 거부하는 호출을 승인할 수 없음 - managed hook이 먼저 실행: managed settings의
PreToolUsehook이 어느 mod의tool.callhook보다 먼저 실행되고, 그 hook이 막으면 그 호출에 mod의tool.callhook은 실행되지 않음. 호출이 진행돼 mod가 재작성하면 managed hook이 재작성된 호출에 다시 실행되어 막을 수 있음. 다른 settings 파일과 플러그인의PreToolUsehook은 마지막 mod 뒤에 실행되며, mod가 도구 호출에 직접 답하면 도구도 그 hook도 실행되지 않음 (mod 실행 순서) - 네트워크 정책이
$.http.fetch를 포괄- 조직 정책이 WebFetch를 허용하지 않으면 모든 mod의
$.http.fetch요청도 거부 (WebFetch availability) CLAUDE_CODE_DISABLE_WEB_FETCH또는WebFetchdeny 규칙으로(managed든 다른 settings든) WebFetch 도구를 꺼도 mod의$.http.fetch는 가능. 모든 사용자 mod를 막으려면 mod 로드를 막거나 정책 mod에서http.fetch를 hook해 사용자 mod 요청을 거부. 그 hook에도 거부하는.catch핸들러를 두세요 (실패한 hook은 건너뛰어 요청이 진행됨). 설치 가능한 플러그인만 제한하면 로드되는 mod는 줄지만 허용한 mod는 여전히 요청 가능CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC를 설정해도 설치한 mod는 요청 가능. 이 변수는 내장 mod와 세션의 Anthropic 자격 증명을 싣는$.http.fetch만 막음. v2.1.288 이전에는 모든 mod의$.http.fetch를 막았음- 이들 중 어느 것도 mod가
$.process.run으로 시작한 프로그램은 막지 못함 (사용자 자신의 접근으로 네트워크에 닿음)
- 조직 정책이 WebFetch를 허용하지 않으면 모든 mod의
- 플러그인 제어가 mod도 포괄: mod는 플러그인이므로
strictKnownMarketplaces등 설치 제한 설정이 설치 가능 여부를 정함 - mod는 권한 프롬프트를 바꿀 수 없음: 프롬프트가 보여주는 내용을 바꾸지 못하지만, 프롬프트 전에 도구 호출을 승인/거부할 수는 있음
- 신뢰 프롬프트가 먼저: 사용자가 아직 신뢰하지 않은 디렉터리의 인터랙티브 세션에서는 신뢰 프롬프트에 답하기 전에 mod가 로드되지 않음
--safe-mode는 설치된 mod를 모두 끔 (조직 mod 포함). mod가 문제를 일으켰는지 확인할 때claude --safe-mode
이 제어 중 어느 것도 mod를 샌드박스하지 않습니다. 허용한 mod는 사용자로서 파일, 프로세스, 네트워크에 접근해 실행됩니다.
mod를 켜 둘지 결정
mod는 Claude Code 안에서 돌기 때문에 플러그인의 다른 부분보다 많은 일을 합니다. 모든 프롬프트와 도구 호출을 보고, 바꾸고, 권한 프롬프트 전에 도구 호출을 허용/거부할 수 있습니다. 사용자가 mod로 로드할 수 있는 것은 이미 가진 플러그인 제어에 달려 있습니다.
| 현재 플러그인 제어 | 사용자가 mod로 로드할 수 있는 것 |
|---|---|
| 없음 | 모든 마켓플레이스의 mod, --plugin-dir의 모든 디렉터리, 세션 중 Claude가 쓴 mod |
| 마켓플레이스 allowlist | 허용한 마켓의 mod 또는 --plugin-dir의 모든 디렉터리. 세션 중 Claude가 쓴 mod는 allowlist가 skills-dir를 포함할 때만 로드 |
마켓플레이스 allowlist + disableSideloadFlags |
허용한 마켓의 mod |
플러그인이 로드되는 방식과 각 제어 설정은 Manage plugins for your organization.
mod가 하는 일 검토
실행하지 않고 mod가 할 수 있는 일을 볼 수 있습니다. 셸에서 플러그인 디렉터리에 claude plugin validate:
claude plugin validate ./some-mod
hooks:와 calls: 줄이 mod의 코드를 설명합니다.
❯ ./register.js hooks: session.start, tool.call, ui.render{component=Pane}
❯ ./register.js calls: $.fs.read, $.http.fetch, $.store.set, $.ui.open
hooks:는 mod가 받는 이벤트, calls:는 코드가 호출하는 mods API 메서드입니다. mod 코드에서 $로 쓰는 mods API가 파일·프로세스·네트워크에 닿는 방법입니다. 이 명령이 읽을 수 없는 방식으로 mods API를 쓰는 mod는 Claude Code가 로드를 거부합니다. calls:에서 볼 것:
| 호출 | 의미 |
|---|---|
$.fs.read, $.fs.write |
사용자가 접근 가능한 어디서든 파일 읽기/쓰기 |
$.process.run, $.process.spawn |
사용자로서 프로그램 시작 |
$.http.fetch |
네트워크 요청 |
$.env.get, $.settings.read |
API 키가 있을 수 있는 환경 변수·설정 읽기. 출력의 env reads: 줄이 변수를 명시 |
$.env.set |
Claude Code와 이후 시작하는 모든 명령·MCP 서버의 환경 변수 설정. 실행되는 프로그램이 바뀔 수 있음. env writes: 줄이 변수를 명시 |
$.mcp.call |
연결된 MCP 서버 도구를 세션 권한 규칙 아래 호출 |
$.model.complete |
사용자의 플랜이나 API 키로 모델 호출 |
$.prompt.submit |
프롬프트 제출. 사용자 본인의 말처럼 보낼 수 있음 |
$.session.send |
다른 세션, subagent, teammate의 Claude가 읽는 메시지 전송 |
hooks: 줄에서 tool.call과 prompt.submit는 모든 도구 호출·프롬프트를 보고 바꿀 수 있다는 뜻, session.append는 저장 전에 대화의 각 행을 재작성할 수 있다는 뜻, ui.render{component=AskUserQuestion}는 Claude가 사용자에게 질문하는 대화상자를 다시 그릴 수 있다는 뜻, tool.check는 권한 프롬프트 전에 도구 호출을 승인/거부할 수 있다는 뜻입니다. 어떤 규칙·hook이 그 답보다 우선하는지는 기본 동작.
허용 범위 선택
mod 정책은 설치 mod 전혀 없음부터 사용자가 고른 어떤 mod든 허용(조직 mod가 나머지를 검사)까지 있고 각각 몇 가지 managed settings입니다. managed settings 위치는 Deploy managed settings.
| 원하는 것 | 설정 |
|---|---|
| 설치 mod 실행 안 함, settings hook은 그대로 | allowManagedModsOnly 설정, 조직 mod 배포 안 함 |
| 설치 mod도 hook도 전부 없음 (관리 hook 포함) | disableAllHooks를 true |
| 조직의 mod만 | guard의 allowManagedModsOnly 옵션 + 조직 mod 설치해 조직 것으로 인정 |
| 승인한 마켓플레이스의 모든 mod | 마켓플레이스 제한 유지 + disableSideloadFlags를 true |
| 모든 mod 허용, 조직 mod가 나머지를 검사 | 조직 mod 설치 후 prependPlugins에 sec-default@builtin과 함께 나열 |
설정별 동작:
allowManagedModsOnly: 내장 guard의 옵션. 사용자의 mod를 거부해 hook이 실행되지 않음. 사용자의 settings hook, status line,/goal은 유지allowManagedHooksOnly: 더 넓음. 조직의 mod와 Claude Code 내장 mod만 로드. 사용자가 직접 설치한 mod는 로드되지 않으며 사용자 settings 파일의 hook도 차단. 설정 전에 What runs underallowManagedHooksOnly확인disableAllHooks: 가장 넓음. managed settings에서 설정하면 조직 것 포함 모든 설치 플러그인의 mod와 settings 파일의 모든 hook이 꺼져 managed settings의PreToolUsehook도 더는 아무것도 막지 못합니다. 커스텀 status line과/goal도 멈춤. 설정 전disableAllHooks확인disableSideloadFlags:--plugin-dir,--plugin-url을 시작 시 거부하고 세션 중 Claude가 쓴 mod 로드를 막음.--agents,--mcp-config도 거부. 설정 전disableSideloadFlags확인
AGENTS.md 지원 같은 내장 mod는 영향받지 않으며 각자 자체 스위치가 있습니다. mod가 거부되거나 로드되지 않은 사용자는 디버그 로그에서 이유를 찾습니다. allowManagedHooksOnly와 disableAllHooks는 Refusal messages, allowManagedModsOnly는 Messages from the built-in guard.
조직의 mod만 허용
위 표의 조직의 mod만 행의 설정에 disableSideloadFlags를 더해 배포합니다. 다음 완전한 managed-settings.json으로 사용자 mod는 거부되고 정책 mod가 다른 mod보다 먼저 실행됩니다.
{
"extraKnownMarketplaces": {
"acme-tools": {
"source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
}
},
"enabledPlugins": { "acme-guard@acme-tools": true },
"prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"],
"pluginConfigs": {
"cc-plugin-sec-default@builtin": {
"options": { "allowManagedModsOnly": true }
}
},
"disableSideloadFlags": true
}
extraKnownMarketplaces,enabledPlugins,prependPlugins: mod를 조직 것으로 설치하고 먼저 실행, guard는 그 뒤. 키가 가리키는 디렉터리는 조직 mod 설치와 순서pluginConfigs: guard의allowManagedModsOnly옵션 설정 (사용자 mod 거부, settings hook·status line·/goal유지)disableSideloadFlags: 시작 시 거부되는 플래그는disableSideloadFlags
테스트 머신에서 claude --debug로 세션을 시작해 디버그 로그를 확인합니다.
- 내 mod:
hooks module줄에tier prepend - 사용자가 설치한 mod:
refused by cc-plugin-sec-default: mods are limited to your organization's by policy (allowManagedModsOnly)줄. 앞선 줄에 그 mod의 hooks module이loaded라 나오므로 거부 줄을 찾으세요 - 플러그인 디렉터리:
claude --plugin-dir ./any-mod는--plugin-dir is disabled by your organization's managed settings (disableSideloadFlags)로 시작하는 메시지와 함께 종료
사용자가 추가할 마켓플레이스도 제한하려면 마켓플레이스 제한과 합치세요.
플러그인 제어를 mod에 적용
mod는 플러그인이므로 조직의 플러그인 관리 방법이 mod 포함 플러그인에도 적용됩니다.
- 조직 전체에서 로드되는 플러그인 확인: Audit and review
- 검토한 플러그인의 업데이트 시점: Set update policy
- 파일럿 등 그룹별 정책: Plan for what managed settings can't enforce
- 플러그인 키를 적용하는 앱·세션 종류: When each surface applies the plugin keys
- CI·컨테이너: Seed containers and CI
- 사용자가 설치할 mod 제공: Host a marketplace. GitHub, git, URL, npm 소스에서 Claude Code가 복사한 mod는 조직의 것이 아니라 사용자 것으로 취급
내장 guard 옵션
managed settings의 pluginConfigs 아래 cc-plugin-sec-default@builtin을 키로 설정합니다.
| 옵션 | 미설정 | true |
|---|---|---|
allowManagedModsOnly |
사용자 mod 실행 | 조직의 mod와 Claude Code 내장 mod만 hook 실행. 사용자가 설치했거나 --plugin-dir로 지정한 mod 포함 다른 모든 mod 거부 |
allowModsToOverrideDenyRules |
deny 규칙이 사용자 mod보다 우선 | 호출을 승인하는 사용자 mod가 deny 규칙이 거부하는 호출도 승인 가능 |
옵션 적용 규칙:
- id는 여기서 한 가지 형태: 옵션은
cc-plugin-sec-default@builtin아래에서만 읽음.prependPlugins는sec-default@builtin도 받고pluginConfigs는 받지 않음 - managed settings만 유효: 사용자/프로젝트/로컬 settings나
--settings파일의 같은 항목은 옵션을 설정하지도 완화하지도 못함 - guard가 로드돼야 함:
prependPlugins를 설정하면 목록에 guard를 명시. guard가 로드되지 않는 곳에서는 두 옵션 모두 적용되지 않음 - guard는 fail-closed: managed settings를 읽을 수 없으면 로드 시 모든 사용자 mod 거부. 사용자 mod가 승인한 호출의 deny 규칙을 확인할 수 없으면 그 호출 거부
옵션이 적용될 때 사용자가 보는 것은 guard 메시지입니다.
조직 자체 mod 실행
조직 mod 설치와 순서 지정
조직의 mod는 사용자 mod가 로드되지 않는 곳에서 로드되고 앞서 실행될 수 있으므로 Claude Code가 출처를 구분해야 합니다. 다음이 모두 참일 때만 조직의 mod로 취급합니다.
- managed
enabledPlugins가 그 플러그인을true로 설정 - managed settings가 플러그인의 마켓플레이스를 사용자 기기의 디렉터리로, 절대 경로로 지정.
extraKnownMarketplaces항목이 그 일을 하고 사용자용 마켓도 등록 - 마켓플레이스가 플러그인을 상대 경로로 나열해 Claude Code가 그 디렉터리에서 제자리 로드
충족하려면 기기 관리 도구로 마켓플레이스 디렉터리를 모든 기기의 같은 경로에 복사합니다. 디렉터리와 상위 디렉터리는 managed settings 파일처럼 관리자만 쓸 수 있게 하세요. 거기에 쓸 수 있는 사람은 mod를 다시 쓸 수 있습니다. claude.ai 관리 콘솔로 배포하는 managed settings는 키를 실을 수 있지만 디렉터리를 기기에 놓을 수는 없습니다.
/opt/acme/claude-plugins/
├── .claude-plugin/
│ └── marketplace.json
└── plugins/
└── acme-guard/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
/opt/acme/claude-plugins/.claude-plugin/marketplace.json:
{
"name": "acme-tools",
"owner": { "name": "Acme" },
"plugins": [
{ "name": "acme-guard", "source": "./plugins/acme-guard", "description": "Acme policy mod" }
]
}
Claude Code가 캐시로 복사하는 플러그인은 managed enabledPlugins가 켜도 사용자 것입니다. GitHub, git, URL, npm 소스의 모든 플러그인이 해당합니다. 그 mod는 사용자 mod 사이에서 실행되고, prependPlugins/appendPlugins가 건너뛰고, allowManagedModsOnly가 거부하고, allowManagedHooksOnly가 로드를 막습니다. 사용자의 디버그 로그에 플러그인 id와 is enabled by managed settings, but로 시작하는 줄이 남습니다.
Claude Code는 도구 실행 같은 동작 직전에 이벤트를 발생시켜 각 mod에 차례로 넘깁니다. 조직의 mod는 어디에도 나열하지 않아도 사용자 mod보다 먼저 실행됩니다. 위치를 정하려면 id(플러그인 이름, @, 마켓 이름. 예 acme-guard@acme-tools)를 두 설정 중 하나에 나열합니다.
prependPlugins: 모든 사용자 mod 앞서 모든 이벤트를 보고 모든 결과를 마지막에 봄. 이벤트를 바꾸고, 거부하고, 사용자 mod를 건너뛸 수 있음appendPlugins: 모든 사용자 mod 뒤에 실행. 그 mod들이 넘기는 이벤트만 넘긴 형태로 봄
acme-tools 마켓플레이스를 /opt/acme/claude-plugins로 선언하고 acme-guard를 켜서 첫 번째로 실행하고 내장 guard를 그 뒤에 둡니다.
{
"extraKnownMarketplaces": {
"acme-tools": {
"source": { "source": "directory", "path": "/opt/acme/claude-plugins" }
}
},
"enabledPlugins": { "acme-guard@acme-tools": true },
"prependPlugins": ["acme-guard@acme-tools", "sec-default@builtin"]
}
extraKnownMarketplaces:acme-tools마켓이 든 디렉터리.path는.claude-plugin/marketplace.json을 담은 디렉터리의 절대 경로enabledPlugins: 이 managed settings를 받는 모든 사용자에게acme-guard를 켬prependPlugins:acme-guard를 첫째, 내장 guard를 둘째로, 둘 다 사용자 mod 앞에. 나열한 순서를 따름
기기가 설정을 받았는지는 Check that a policy is in force. mod 위치는 그 기기에서 claude --debug로 시작해 디버그 로그에서 mod id를 검색합니다.
hooks module acme-guard@acme-tools loaded와tier prepend: 조직 것으로 인정, 첫째로 실행- 같은 줄에
tier user: 사용자 mod로 취급. 두 번째 줄prependPlugins names acme-guard@acme-tools, which is not an enabled managed plugin with a hooks module; skipped가 목록이 건너뛰었다고 알려 줌
두 목록의 id 적용 규칙:
- 목록이 기본값을 대체: managed settings에
prependPlugins를 설정하면 내장 guard를 유지하려고sec-default@builtin을 거기에 명시하세요. guard는 내장이라enabledPlugins항목이 필요 없음 - 내 id는 조직 것으로 인정돼야 함: 조직 mod 조건을 충족하지 않는 플러그인의 id는 건너뜀
- 저장소는 설정 불가: 두 설정은 managed settings에서만 읽고 저장소 settings에서는 읽지 않음. 사용자는 managed settings가 없는 기기이고 세션 자격 증명이 Team/Enterprise
/login이 아닐 때만~/.claude/settings.json에서 자기 mod 순서를 정할 수 있음. 그 밖에서는 사용자 settings의 두 키를 무시하며 거기의 목록은 내장 guard를 더하지도 빼지도 않음
자체 mod로 정책 강제
모든 사용자 mod를 막기만 한다면 자체 mod 없이 allowManagedModsOnly면 됩니다. 일부 사용자의 mod는 허용하고 일부는 거부하거나 mod가 하는 일을 기록하려면 정책 mod를 씁니다.
다른 mod가 로드되기 직전마다 정책 mod는 claude plugin validate가 출력하는 목록을 plugin.register 이벤트로 받습니다. prependPlugins의 mod는 그 목록을 읽어 mod를 거부할 수 있고, mods API 호출을 이름으로 처리해 다른 모든 mod의 호출을 기록하거나 거부할 수도 있습니다. 이름은 $.를 뺀 메서드 이름이라 fs.write에 건 hook은 모든 $.fs.write 호출을 봅니다.
아래는 코드가 $.process.run 또는 $.process.spawn을 호출하는 사용자 mod를 거부하고, 모든 도구 호출과 mod가 쓰는 파일을 디버그 로그에 감사 기록합니다. 먼저 실행되므로 사용자 mod가 바꾸기 전 요청된 내용이 기록됩니다. acme-guard/hooks/register.js:
// The methods no user's mod may call, each spelled namespace.method
const BLOCKED_CALLS = ['process.run', 'process.spawn']
export function register(on) {
// Runs each time another mod is about to load
on('plugin.register', async ($, e, next) => {
// Keep the calls in that mod's code that are on the blocked list
const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))
if (e.tier === 'user' && blocked.length > 0) {
// Returning refuse keeps the mod from loading, and the text is the reason
return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }
}
// Let every other mod load
return next(e)
})
// Record each tool call, then let it go ahead unchanged
on('tool.call', async ($, e, next) => {
$.ui.log('audit tool.call ' + e.tool, { to: 'debug' })
return next(e)
})
// Record which mod wrote a file, then the path, quoted because the mod chose it
on('fs.write', async ($, e, next) => {
$.ui.log('audit fs.write by ' + next.origin.plugin + ' ' + JSON.stringify(e.path), { to: 'debug' })
return next(e)
})
}
plugin.register: 다른 mod 로드 여부 결정. 차단 메서드를 호출하는 사용자 mod는 거부하고 나머지는 통과tool.call: 도구 호출마다audit tool.call Bash같은 줄을 디버그 로그에 쓰고 아무것도 바꾸지 않음fs.write: 다른 mod의$.fs.write호출마다audit fs.write by reader "/tmp/notes.md"같은 줄을 쓰고 아무것도 바꾸지 않음. mod 이름이 먼저고 경로는 따옴표로 감싸 mod가 고른 경로가 줄의 다른 필드로 위장할 수 없음
plugin.register가 읽는 이벤트 필드:
e.tier: mod가 실행될 곳,prepend,user,append,builtin중 하나. 사람이 설치한 mod는 모두usere.uses.calls: mod가 호출하는 mods API 메서드.claude plugin validate가 출력하는$.없이namespace.method형태 (예process.run)
사용자가 $.process.run을 호출하는 mod를 설치하면 mod가 로드되지 않고 디버그 로그에 refused by acme-guard:와 이유로 끝나는 줄이 남습니다. 거부는 plugin 디렉터리를 hot reload하는 세션에서는 transcript에도 나옵니다. mod 전체를 거부하지 않고 호출만 막으려면 그 호출 이름의 hook에서 { deny: 'your reason' }를 반환합니다. 감사 줄을 디버그 로그가 아닌 곳으로 보내려면 같은 hook에서 $.http.fetch를 호출하세요.
내 mod 없이 세션이 돌 수 있습니다. 설치 mod를 실행하는 worker 스레드가 세 번 충돌하면 사용자가 /reload-plugins를 실행하거나 새 세션을 시작할 때까지 내장이 아닌 모든 mod(내 것 포함)를 언로드합니다. --safe-mode로 시작한 사용자도 설치 mod 없이 실행됩니다. mod에 필요한 파일은 Create a mod, 이 정책 mod의 테스트 파일은 Test a policy mod.
검사 실패 시 mod 거부
plugin.register hook이 예외를 던지거나 시간 제한을 넘기면 Claude Code는 그 hook을 건너뛰므로 검사가 fail-open이 되어 검사 중이던 mod가 로드됩니다. fail-closed로 사용자 mod를 거부하려면 검사를 이름 있는 함수로 옮기고 거부를 반환하는 .catch 핸들러를 추가합니다. 아래는 plugin.register hook만 보여 주므로 첫 버전의 두 감사 hook은 register에 그대로 두세요.
const BLOCKED_CALLS = ['process.run', 'process.spawn']
// The same check as before, moved into a function of its own
async function checkMod($, e, next) {
const blocked = e.uses.calls.filter((call) => BLOCKED_CALLS.includes(call))
if (e.tier === 'user' && blocked.length > 0) {
return { refuse: 'Acme policy: mods may not call ' + blocked.join(', ') }
}
return next(e)
}
export function register(on) {
// The handler runs only when checkMod throws or exceeds its time limit
on('plugin.register', checkMod).catch(async ($, e, next) => {
// Let your organization's mods and built-in mods load
if (e.tier !== 'user') return next(e)
// Refuse the user's mod that couldn't be checked
return { refuse: 'Acme policy check failed, so this mod was not loaded' }
})
}
이제 검사 중 예외나 시간 초과가 난 mod는 로드되지 않고 거부 줄에 두 번째 이유가 실립니다 (refused by acme-guard: Acme policy check failed, so this mod was not loaded). 핸들러는 user tier가 아닌 모든 mod를 next(e)로 통과시키므로 검사 실패가 조직이 나열한 mod를 막지 않습니다. 다른 이벤트의 .catch는 Handle a hook that fails.
체크리스트
- 정책 결정:
allowManagedModsOnly/allowManagedHooksOnly/disableAllHooks/disableSideloadFlags중 필요한 범위 - 내장 guard 옵션은 managed settings의
pluginConfigs에만 설정 (cc-plugin-sec-default@builtin) - 조직 mod는 managed
enabledPlugins+ 절대 경로 디렉터리 마켓 + 상대 경로 플러그인으로 설치, 디렉터리는 관리자만 쓰기 가능 -
prependPlugins를 쓰면sec-default@builtin을 목록에 명시 -
claude --debug로tier prepend와 거부 줄 확인 - 정책 mod에
.catch로 fail-closed 처리
함께 보기
- Plugin security: 플러그인이 기기에서 할 수 있는 일과 설치 전 검토
- Mods overview
- Manage plugins for your organization
- The order mods run in, Settings and environment variables
기준일
- 문서 작성·동기화 기준일: 2026-10-23 (v2.1.309 반영)
- 원문: https://code.claude.com/docs/en/plugins/mods/admin