Một workflow chỉ cần kiểm tra thay đổi của pull request nhưng lại thừa hưởng token có quyền ghi và secret dùng deploy. Khi các bước chạy mã do người mở pull request cung cấp, quyền dư trở thành một phần của môi trường mà mã đó có thể tiếp cận.
Bài này hướng dẫn rà ranh giới quyền của job CI. Tài liệu GitHub Actions và SHA của action được đối chiếu ngày 23/10/2026. Ví dụ dùng GitHub-hosted runner; nếu dùng runner riêng, cần kiểm tra thêm phiên bản runner, cách cô lập máy và tài nguyên nội bộ mà job có thể truy cập.
Bắt đầu bằng ba câu hỏi
Job được kích hoạt bởi sự kiện nào? Nó sẽ checkout mã từ đâu? Token, secret và hệ thống mạng nào có mặt khi các bước bắt đầu chạy? Trả lời ba câu hỏi này trước khi thêm một action vào workflow sẽ dễ hơn việc suy luận chỉ từ tên job test hay lint.
Với kiểm tra mã pull request thông thường, pull_request là điểm khởi đầu phù hợp. pull_request_target chạy trong ngữ cảnh khác và có thể tiếp cận quyền hoặc secret của repository đích. Không đổi trigger sang sự kiện này chỉ để làm một bước thiếu quyền chạy được, rồi checkout và thực thi mã từ pull request không tin cậy.
Những công việc có đặc quyền như gắn nhãn, cập nhật metadata hoặc deploy nên được thiết kế riêng theo nhu cầu. Tách workflow chỉ có ích khi dữ liệu và artifact đi qua ranh giới đó cũng được kiểm tra; một artifact từ job không tin cậy vẫn là dữ liệu không tin cậy.
Đặt quyền mặc định rồi mở đúng ở job
Ví dụ dưới đây chỉ kiểm tra lỗi khoảng trắng trong diff của merge commit pull request. Nó chưa chạy unit test, chưa cài dependency và không thực hiện deploy. Lưu vào .github/workflows/pr-permissions.yml trong repository thử để xem phạm vi quyền thực tế.
name: PR permission example
on:
pull_request:
permissions: {}
jobs:
diff-check:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Checkout pull request merge commit
# actions/checkout v6; SHA verified on 2026-10-23
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
with:
fetch-depth: 2
persist-credentials: false
- name: Check whitespace in the proposed diff
run: git diff --check HEAD^ HEAD
Khi khai báo một tập quyền cụ thể, các quyền không được khai báo được đặt thành none theo quy tắc của GitHub. Job trên mở contents: read để đọc repository. Khi mở rộng thành pipeline thật, chỉ thêm quyền mà từng bước cần, đồng thời đối chiếu giới hạn của sự kiện, fork và chính sách tổ chức.
persist-credentials: false yêu cầu checkout không giữ credential cho những lệnh Git về sau. Nó không biến runner thành sandbox an toàn cho mọi đoạn mã và không thay thế phần permissions. Hai cấu hình giải quyết những việc khác nhau.
Pin action là một quyết định có lịch bảo trì
SHA trong ví dụ đã được lấy từ tag v6 của repository chính thức actions/checkout; đây là mốc cố định để tái lập ví dụ, không phải khẳng định phiên bản mới nhất. Pin full commit SHA giúp chỉ rõ code action mà workflow sử dụng. Team vẫn cần rà cập nhật và thay pin khi đã kiểm tra bản mới.
Với action bên thứ ba, hãy xem repository sở hữu, source của action, quyền cần dùng và cách xử lý đầu vào. Một action được pin nhưng có hành vi không phù hợp vẫn không phù hợp. Bản cập nhật action cũng có thể tăng yêu cầu runner, nên đặc biệt kiểm tra release note nếu hạ tầng dùng máy tự quản lý.
Mã trong repository cũng là đầu vào của CI
Lệnh cài dependency có thể chạy lifecycle script. Một script mang tên test có thể làm nhiều hơn chạy assertion. Vì vậy, tránh cấp secret production cho toàn bộ job rồi cho rằng chỉ bước deploy mới đọc được chúng. Với pull request từ fork, kiểm tra chính sách phê duyệt workflow của repository và tổ chức.
Các trường như tiêu đề pull request cũng là dữ liệu do người khác kiểm soát. Đừng chèn trực tiếp biểu thức lấy tiêu đề vào nội dung shell run. Khi thật sự cần xử lý chuỗi đó, chuyển qua biến môi trường, dùng quoting phù hợp và giới hạn việc đưa nội dung không tin cậy vào log hay công cụ khác.
Cách xác nhận cấu hình có hiệu lực
Mở log phần thiết lập job để xem quyền của GITHUB_TOKEN, đối chiếu với YAML và chính sách repository. Kiểm tra workflow thử không tham chiếu secret deploy, không nhận token ghi qua biến môi trường khác và không checkout một head không tin cậy trong ngữ cảnh đặc quyền.
Sau đó tạo một pull request nhỏ có lỗi khoảng trắng để kiểm tra bước diff thất bại như dự kiến, rồi sửa lỗi để kiểm tra lượt xanh. Bài mới kiểm tra cú pháp YAML và đối chiếu tài liệu; chưa chạy workflow này trên GitHub. Không dùng việc parse YAML thành công để thay cho kiểm tra quyền và hành vi trên runner thật.
Kết luận
Một job kiểm tra pull request nên có quyền tương ứng với công việc nó thực hiện. Rà sự kiện, nguồn mã và credential cùng nhau sẽ giúp việc thêm test hay nâng cấp action không vô tình mở rộng quyền của cả pipeline.


