jvinhit//lab

Search posts

Type to search across journal entries.

↑ ↓ navigate ↵ open esc close

Claude Code từ zero đến thành thạo · Phần 7 — Permission mode và cấu hình quyền

Sáu permission mode và lúc nào dùng cái nào, classifier của auto mode chặn gì, cú pháp allow/ask/deny rule cho Bash, Read, Edit, WebFetch, MCP — cùng protected path và critical path mà không mode nào vượt qua.

Permission mode là thứ quyết định Claude làm được gì mà không hỏi bạn. Đây cũng là chỗ người dùng mới hay chọn sai theo hai hướng ngược nhau: hoặc bực mình vì bị hỏi liên tục rồi bật thẳng bypassPermissions, hoặc sợ tới mức ở lì Manual mode và mất hết lợi ích của agent.

Bài này chỉ ra khoảng giữa: auto mode với classifier duyệt thay bạn, cộng permission rule để tiền phê duyệt đúng thứ bạn tin tưởng.

Sáu mode

ModeChạy mà không hỏiHợp với
default (Manual)Chỉ đọcTự review từng hành động, việc nhạy cảm
acceptEditsĐọc, sửa file, và lệnh filesystem thông dụng (mkdir, touch, mv, cp…)Lặp trên code mà bạn đang review
planĐọc, cộng các lệnh được classifier duyệt khi auto mode khả dụngKhám phá codebase trước khi đổi nó
autoMọi thứ, kèm kiểm tra an toàn ở nềnTask dài, đỡ mệt vì bị hỏi
dontAskChỉ tool đã tiền phê duyệtCI và script bị khoá chặt
bypassPermissionsMọi thứChỉ container và VM cô lập

Lưu ý về tên: mode duyệt từng hành động hiển thị là Manual trong CLI và các extension, nhưng giá trị cấu hình của nó là default. CLI chấp nhận manual làm alias ở mọi chỗ bạn gõ giá trị.

Bấm Shift+Tab để xoay vòng giữa các mode trong phiên đang chạy.

Phiên khởi động ở mode nào

Claude Code lấy permission mode từ cái đầu tiên khớp trong ba nguồn:

  1. Flag --permission-mode, hoặc --dangerously-skip-permissions
  2. permissions.defaultMode trong file settings
  3. Mặc định dựng sẵn

Mặc định dựng sẵn phụ thuộc cách bạn chạy và gói bạn dùng:

Cách chạyMode khởi động
Có file settings nào đặt disableAutoMode thành "disable"default
Phiên đầu tiên sau khi cài hoặc nâng cấp lên bản thêm mặc định nàydefault
claude -p hoặc Agent SDKdefault
Bedrock, Google Cloud’s Agent Platform, Microsoft Foundry, Claude Platform on AWS, phiên gateway đã đăng nhậpdefault
Gói Pro, Max hoặc Team, trong terminal hoặc VS Code extensionauto
Gói Enterprise hoặc Claude Console API keydefault

Một cái bẫy đáng nhớ: đặt defaultMode: "auto" trong .claude/settings.json hoặc .claude/settings.local.json không có tác dụng. Giá trị auto chỉ ăn từ ~/.claude/settings.json. Nếu bạn đặt auto ở settings project mà phiên vẫn khởi động ở Manual không kèm lỗi, đây là nguyên nhân.


Auto mode và classifier

Trong auto mode, một model thứ hai — gọi là classifier — duyệt hành động thay cho bạn. Nó chạy ở nền, chặn cái rủi ro và cho qua phần còn lại.

Classifier tin thư mục làm việc của bạn và các git remote đã cấu hình sẵn lúc phiên bắt đầu. Một remote được thêm hoặc trỏ lại giữa phiên bằng git remote add hay git remote set-url không được tin; mọi thứ khác bị coi là bên ngoài cho tới khi bạn cấu hình hạ tầng tin cậy.

Chặn mặc định:

  • Tải và chạy code, kiểu curl | bash
  • Gửi dữ liệu nhạy cảm ra endpoint bên ngoài
  • Deploy và migration lên production
  • Xoá hàng loạt trên cloud storage
  • Cấp quyền IAM hoặc quyền repo
  • Sửa hạ tầng dùng chung
  • Phá huỷ không hồi phục các file đã tồn tại trước phiên
  • Force push
  • Commit hoặc push một thay đổi khiến secret hoặc dữ liệu nhạy cảm bị đưa ra ngoài repository khi nó chạy, hoặc mở rộng thứ mà deploy phơi ra

Classifier cũng duyệt mỗi tin nhắn Claude gửi cho agent khác qua SendMessage, và duyệt các lệnh rm/rmdir nhắm vào critical path.

Auto mode còn thúc Claude làm tiếp mà không dừng lại hỏi câu làm rõ — dù nó vẫn hỏi khi prompt của bạn hoặc một skill yêu cầu rõ như vậy.

Auto mode giảm số lần bị hỏi nhưng không đảm bảo an toàn. Dùng nó cho task mà bạn tin hướng đi chung, đừng coi nó là vật thay thế cho việc review các thao tác nhạy cảm.

Auto mode chỉ khả dụng khi tài khoản của bạn đủ điều kiện về model (trên Anthropic API: Opus 4.6 trở lên, Sonnet 4.6 trở lên, hoặc Fable 5) và tổ chức không tắt nó. Nếu Claude Code báo auto mode không khả dụng, đó là một điều kiện chưa thoả — không phải sự cố tạm thời.

Plan mode

Plan mode bảo Claude nghiên cứu và đề xuất thay đổi mà không thực hiện. Claude đọc file, chạy lệnh shell để khám phá, và viết ra plan — nhưng không sửa source. Thay đổi bị chặn cho tới khi bạn duyệt plan.

Vào plan mode bằng Shift+Tab, hoặc đặt tiền tố /plan cho một prompt đơn lẻ, hoặc khởi động bằng claude --permission-mode plan.

Khi plan xong, Claude trình bày và hỏi bạn muốn làm gì:

  • Yes, and use auto mode — duyệt và chuyển sang auto mode
  • Yes, manually approve edits — duyệt và review từng thay đổi
  • No, keep planning — ở lại plan mode và nói cho Claude biết cần sửa gì

Bấm Ctrl+G để mở plan trong text editor mặc định và sửa trực tiếp trước khi Claude tiếp tục. Duyệt plan cũng tự đặt tên phiên theo nội dung plan, trừ khi bạn đã đặt tên bằng --name hoặc /rename.

Một chi tiết liên quan tới phần 3: nếu bạn dùng thiết lập model opusplan, mỗi lần vào/ra plan mode là một lần đổi model — và đổi model huỷ toàn bộ prompt cache.

acceptEdits

Mode này cho Claude tạo và sửa file trong thư mục làm việc mà không hỏi. Ngoài sửa file, nó còn tự duyệt các lệnh Bash filesystem thông dụng: mkdir, touch, rm, rmdir, mv, cp, và sed.

Việc tự duyệt chỉ áp cho path bên trong thư mục làm việc hoặc additionalDirectories. Path ngoài phạm vi đó, ghi vào protected path, rm/rmdir nhắm critical path, và mọi lệnh Bash khác ngoài tập read-only dựng sẵn — tất cả vẫn hỏi.

Dùng acceptEdits khi bạn muốn review thay đổi sau đó trong editor hoặc qua git diff, thay vì duyệt từng cái inline.

dontAsk và bypassPermissions

dontAsk tự từ chối mọi tool call lẽ ra sẽ hỏi bạn. Claude chỉ chạy hành động khớp permissions.allow, các lệnh Bash read-only, và những gì được PreToolUse hook duyệt. Phiên không bao giờ chờ input — nên đây là mode cho CI pipeline và môi trường hạn chế.

bypassPermissions tắt hết prompt và kiểm tra an toàn. Ba điều cần biết:

  • Bạn không vào được mode này từ một phiên đã khởi động mà không bật nó. Phải bật lúc launch.
  • Lần đầu chạy tương tác với mode này, Claude Code hiện hộp thoại cảnh báo yêu cầu bạn nhận trách nhiệm. Từ chối thì Claude Code thoát.
  • Trên Linux và macOS, Claude Code từ chối khởi động ở mode này khi chạy dưới quyền root hoặc sudo.

bypassPermissions không có bất kỳ lớp bảo vệ nào trước prompt injection hay hành động ngoài ý muốn. Muốn ít bị hỏi mà vẫn có kiểm tra an toàn ở nền thì dùng auto mode. Chỉ dùng bypass trong container, VM, hoặc dev container không có internet.


Hai lằn ranh không mode nào vượt qua

Đây là phần quan trọng nhất của bài, và cũng là phần ít người biết.

Protected path

Ghi vào một nhóm nhỏ các path không bao giờ được tự duyệt, trừ ở bypassPermissions. Điều này ngăn hỏng trạng thái repository và hỏng chính cấu hình của Claude.

ModeGhi vào protected path
default, acceptEditsHỏi
planChuyển cho classifier nếu auto mode khả dụng; nếu không thì hỏi
autoChuyển cho classifier
dontAskTừ chối
bypassPermissionsCho phép

Thư mục được bảo vệ: .git, .config/git, .vscode, .idea, .husky, .cargo, .devcontainer, .yarn, .mvn, và .claude (trừ .claude/worktrees).

File được bảo vệ: .gitconfig, .gitmodules; các file khởi động shell (.bashrc, .zshrc, .profile, .envrc…); cấu hình package manager (.npmrc, .yarnrc, .pnpmfile.cjs, bunfig.toml…); .pre-commit-config.yaml và các file lefthook; .devcontainer.json; .ripgreprc, pyrightconfig.json; .mcp.json và .claude.json.

Rule permissions.allow không tiền phê duyệt được ghi vào protected path. Kiểm tra an toàn chạy trước khi Claude Code xét allow rule, nên một dòng như Edit(.claude/**) trong settings không đổi được kết quả ở bảng trên.

Critical path

Claude Code không bao giờ để một allow rule hay một PreToolUse hook trả "allow" duyệt lệnh rm hoặc rmdir nhắm vào critical path — kể cả ở các mode bỏ qua mọi prompt khác. Đây là cầu dao chống lỗi của model.

Critical path gồm: gốc filesystem; thư mục cấp một dưới gốc như /usr, /etc; thư mục home của bạn; ổ đĩa Windows và thư mục cấp một của chúng như C:\ và C:\Windows; thư mục làm việc của bạn và các thư mục cha của nó.

Ba chi tiết chống lách luật:

  • Glob ngay dưới một biến shell, ví dụ rm -rf "$DIR"/*, cũng bị coi là critical-path removal — vì lệnh trở thành xoá từ gốc filesystem khi biến rỗng.
  • Giấu lệnh xoá trong command substitution $(...), backtick, hay process substitution <(...) không né được kiểm tra.
  • Ở bypassPermissions, critical-path removal vẫn hỏi bạn.

Permission rule

Mode đặt nền; rule xếp chồng lên trên để tiền phê duyệt hoặc chặn từng thứ cụ thể. Mở /permissions để xem và sửa.

  • Allow — cho Claude Code dùng tool đó mà không cần duyệt tay
  • Ask — hỏi xác nhận mỗi lần
  • Deny — chặn hẳn

Thứ tự xét: deny → ask → allow. Cái khớp đầu tiên theo thứ tự đó quyết định kết quả, và độ cụ thể của rule không đổi được thứ tự này. Một deny rule rộng như Bash(aws *) chặn mọi lệnh khớp, kể cả lệnh cũng khớp một allow rule hẹp hơn như Bash(aws s3 ls) — nên deny rule không mang được ngoại lệ allowlist.

Deny rule hành xử khác nhau tuỳ bạn viết thế nào:

  • Tên tool trần như Bash gỡ hẳn tool khỏi context của Claude — nó không bao giờ thấy tool đó. (Đây cũng là hành động huỷ prompt cache, xem phần 3.)
  • Rule có phạm vi như Bash(rm *) giữ tool khả dụng và chỉ chặn các lệnh khớp khi Claude cố gọi.

Permission rule do Claude Code cưỡng chế, không phải model. Chỉ dẫn trong prompt hay CLAUDE.md định hình thứ Claude cố làm, nhưng không đổi được thứ Claude Code cho phép.

Cú pháp

Rule có dạng Tool hoặc Tool(specifier).

RuleKhớp với
BashMọi lệnh Bash. Bash(*) tương đương
Bash(npm run build)Đúng lệnh npm run build
Bash(npm run *)Mọi lệnh bắt đầu bằng npm run
Read(./.env)Đọc file .env ở thư mục hiện tại
WebFetch(domain:example.com)Request tới example.com
mcp__puppeteerMọi tool của server puppeteer
mcp__puppeteer__puppeteer_navigateĐúng một tool của server đó
Agent(Explore)Subagent tên Explore

Hậu tố :* là cách viết tương đương của wildcard cuối, nên Bash(ls:*) khớp đúng như Bash(ls *). Nhưng nó chỉ được nhận ở cuối pattern — trong Bash(git:* push) thì dấu hai chấm bị coi là ký tự thường.

Ví dụ cho phép npm và git commit nhưng chặn git push:

{
  "permissions": {
    "allow": ["Bash(npm run *)", "Bash(git commit *)"],
    "deny": ["Bash(git push *)"]
  }
}

Khớp theo tham số đầu vào

Deny và ask rule khớp được một tham số cấp cao nhất của bất kỳ tool nào, dạng Tool(param:value):

RuleKhớp với
Agent(model:opus)Lời gọi Agent yêu cầu model tier Opus
Agent(isolation:worktree)Lời gọi Agent yêu cầu một git worktree
Bash(run_in_background:true)Lời gọi Bash chạy ở nền

Mỗi rule đặt tên một tham số; muốn chặn theo hai tham số thì viết hai rule. Tham số model bỏ trống thì không bao giờ khớp, nên Agent(model:*) không khớp lời gọi không set model.

Không khớp được theo trường nội dung chính của tool: command của Bash, file_path của Read/Edit/Write, path của Grep/Glob, url của WebFetch. Một rule như Bash(command:rm *) sẽ bị lách bằng lệnh ghép, nên Claude Code bỏ qua nó và cảnh báo lúc khởi động.

Rule cho đường dẫn file

Read và Edit rule dùng cú pháp pattern kiểu gitignore, với bốn dạng neo khác nhau:

PatternNghĩaVí dụ
//pathĐường dẫn tuyệt đối từ gốc filesystemRead(//Users/alice/secrets/**)
~/pathTừ thư mục homeRead(~/Documents/*.pdf)
/pathTương đối so với nguồn settingsEdit(/src/**/*.ts)
path hoặc ./pathTương đối so với thư mục hiện tạiRead(*.env)

Một pattern như /Users/alice/file không phải đường dẫn tuyệt đối. Dấu gạch chéo đơn ở đầu neo vào nguồn settings, không phải gốc filesystem. Dùng //Users/alice/file cho đường dẫn tuyệt đối.

Ba điều dễ sai khác:

  • Edit rule áp cho mọi tool built-in sửa file. Đừng viết rule cho Write, NotebookEdit hay MultiEdit — Claude Code nhận rule nhưng không bao giờ tra tới nó, và cảnh báo lúc khởi động. Dùng Edit(docs/**) thay cho Write(docs/**).
  • Read deny rule cũng chặn Edit và Write trên cùng path, gồm cả tạo file mới ở đó. Nhưng NotebookEdit không được phủ, nên thêm Edit deny rule cho path không tool nào được đổi.
  • Read và Edit deny rule chỉ áp cho tool file built-in và các lệnh file mà Claude Code nhận diện được trong Bash như cat, head, tail, sed. Chúng không áp cho subprocess bất kỳ đọc/ghi file gián tiếp — ví dụ một script Python tự mở file. Muốn cưỡng chế ở mức hệ điều hành thì bật sandbox.

WebFetch và wildcard tên miền

WebFetch(domain:*.example.com) khớp mọi subdomain ở mọi độ sâu, nhưng không khớp chính example.com. Ở vị trí khác ngoài *. dẫn đầu, wildcard chỉ khớp phần text giữa hai dấu chấm — nên WebFetch(domain:example.*) khớp example.org nhưng không khớp example.evil.com. Thiết kế này ngăn wildcard cuối khớp phải tên miền mà kẻ tấn công đăng ký được.

Thứ tự ưu tiên

Permission rule theo đúng thứ tự ưu tiên settings chung, với managed settings cao nhất — không cấp nào, kể cả command line, ghi đè được một managed permission rule.

Và một quy tắc bao trùm: nếu một tool bị deny ở bất kỳ cấp nào, không cấp nào allow lại được. User settings allow mà project settings deny thì deny thắng; ngược lại cũng vậy.


Chọn thiết lập nào

Bạn muốnBắt đầu với
Tự review từng hành độngclaude --permission-mode default
Ít bị hỏi ở local mà không cần classifierManual mode cộng Bash sandbox ở chế độ auto-allow
Khám phá trước khi đổi gìclaude --permission-mode plan
Làm không cần trông, có classifier duyệt từng hành độngclaude --permission-mode auto
Chạy trong CI với allowlist chính xácclaude -p "..." --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"
Chạy hoàn toàn tự động trong containerclaude -p "..." --dangerously-skip-permissions

Với người dùng hằng ngày trên gói Pro/Max/Team, câu trả lời gần như luôn là: để auto mode làm mặc định, rồi thêm dần allow rule cho các lệnh bạn tin tưởng và deny rule cho thứ bạn không bao giờ muốn nó chạm vào. Chạy /fewer-permission-prompts (phần 6) sau vài tuần để nó tự đề xuất allowlist từ transcript thật của bạn.

Nguồn

Docs gốc cập nhật thường xuyên; khi có mâu thuẫn, tin bản tiếng Anh.