1 điểm bởi GN⁺ 2 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Vào khoảng 19:20~19:26 UTC ngày 2026-05-11, kẻ tấn công đã phát hành 84 phiên bản độc hại trên 42 gói npm @tanstack/
  • Chuỗi tấn công kết hợp “Pwn Request” pull_request_target, đầu độc bộ nhớ đệm GitHub Actions và trích xuất token OIDC từ bộ nhớ của runner
  • Token npm và quy trình publish không bị đánh cắp hay xâm phạm; mã độc đã trực tiếp POST lên registry bằng quyền OIDC trusted publisher
  • Khi cài đặt các phiên bản bị ảnh hưởng, thông tin xác thực AWS, GCP, Kubernetes, Vault, GitHub, npm, SSH có thể đã bị lộ và cần được thay thế
  • Tất cả các phiên bản bị ảnh hưởng đã bị đánh dấu deprecated, đang được gỡ tarball cùng với npm security, và đã công bố issue theo dõi cùng GitHub Security Advisory

Tổng quan sự cố

  • Trong khoảng 19:20~19:26 UTC ngày 2026-05-11, kẻ tấn công đã phát hành 84 phiên bản độc hại trên 42 gói npm @tanstack/*
  • Chuỗi tấn công kết hợp mẫu “Pwn Request” của pull_request_target, đầu độc bộ nhớ đệm GitHub Actions vượt qua ranh giới tin cậy giữa fork↔base, và trích xuất token OIDC từ bộ nhớ tiến trình của runner GitHub Actions
  • Đã xác nhận token npm không bị đánh cắp, và bản thân workflow npm publish cũng không bị xâm phạm
  • Các phiên bản độc hại được nhà nghiên cứu bên ngoài ashishkurmi phát hiện công khai từ stepsecurity trong vòng 20 phút
  • Tất cả các phiên bản bị ảnh hưởng đã bị đánh dấu deprecated, và đang được gỡ tarball khỏi registry cùng với npm security
  • Người dùng đã cài đặt các phiên bản bị ảnh hưởng trong ngày 2026-05-11 phải thay thế thông tin xác thực AWS, GCP, Kubernetes, Vault, GitHub, npm, SSH mà máy chủ cài đặt có thể truy cập
  • Issue theo dõi là TanStack/router#7383, GitHub Security Advisory là GHSA-g7cv-rxg3-hmpx

Phạm vi ảnh hưởng

  • Các gói bị ảnh hưởng

    • Phạm vi ảnh hưởng gồm 42 gói và 84 phiên bản, với mỗi gói có 2 phiên bản được phát hành cách nhau khoảng 6 phút
    • Danh sách đầy đủ có trong issue theo dõi
    • Các dòng sản phẩm đã được xác nhận không bị ảnh hưởng là @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, @tanstack/store, gói meta @tanstack/start
    • @tanstack/start-* không nằm trong danh sách đã xác nhận không bị ảnh hưởng
  • Hành vi của mã độc

    • Khi môi trường nhà phát triển hoặc CI chạy npm install, pnpm install, yarn install với các phiên bản bị ảnh hưởng, npm sẽ xử lý mục optionalDependencies độc hại và lấy orphan payload commit từ fork network
    • Sau đó script vòng đời prepare được thực thi, và router_init.js bị làm rối với kích thước khoảng 2.3MB được giấu trong tarball bị ảnh hưởng sẽ chạy
    • Script độc hại thu thập thông tin xác thực từ các vị trí phổ biến như AWS IMDS/Secrets Manager, metadata GCP, token service-account của Kubernetes, token Vault, ~/.npmrc, token GitHub, CLI gh, .git-credentials, khóa riêng SSH
    • Dữ liệu bị đánh cắp được rò rỉ qua mạng tải tệp của Session/Oxen messenger, với đích đến là filev2.getsession.org, seed{1,2,3}.getsession.org
    • Do mạng này được mã hóa đầu cuối và không có C2 do kẻ tấn công kiểm soát, biện pháp giảm thiểu ở lớp mạng chỉ giới hạn ở chặn IP/tên miền
    • Logic tự lây lan liệt kê các gói khác do nạn nhân quản lý bằng registry.npmjs.org/-/v1/search?text=maintainer:<user>, rồi phát hành lại bằng cùng phương thức chèn này
    • Vì payload chạy như một phần của vòng đời npm install, các máy chủ đã cài đặt các phiên bản bị ảnh hưởng trong ngày 2026-05-11 phải được coi là có khả năng đã bị xâm phạm

Dòng thời gian

  • Trước cuộc tấn công: giai đoạn đầu độc cache

    • Vào 17:16 UTC ngày 2026-05-10, kẻ tấn công tạo github.com/zblgg/configuration, một fork của TanStack/router, rồi đổi tên để tránh bị phát hiện trong danh sách fork
    • Vào 23:29 UTC ngày 2026-05-10, commit độc hại 65bf499d16a5e8d25ba95d69ec9790a6dd4a1f14 được tạo trên fork với danh tính giả mạo claude <claude@users.noreply.github.com>
    • Commit này thêm packages/history/vite_setup.mjs, một payload JS đã bundle dài khoảng 30.000 dòng, và gắn [skip ci] vào thông điệp commit để ngăn CI chạy trên sự kiện push
    • Khoảng 10:49 UTC ngày 2026-05-11, zblgg mở PR #7378 nhắm tới main của TanStack/router với tiêu đề “WIP: simplify history build”
    • bundle-size.ymllabeler.yml đều tự động chạy cho PR bằng pull_request_target, và vì pull_request_target bỏ qua cổng phê duyệt cho người đóng góp lần đầu nên không cần phê duyệt riêng
    • pr.yml dùng pull_request bị chặn ở trạng thái chờ phê duyệt và không được chạy
    • Trong khoảng 11:01~11:11 UTC ngày 2026-05-11, zblgg nhiều lần force-push vào PR head để kích hoạt thêm các lần chạy pull_request_target
    • Vào 11:11 UTC ngày 2026-05-11, commit độc hại 65bf499d được đưa vào PR head, và job benchmark-pr của bundle-size.yml checkout refs/pull/7378/merge, sau đó chạy pnpm installpnpm nx run @benchmarks/bundle-size:build, khiến vite_setup.mjs được thực thi
    • Vào 11:29 UTC ngày 2026-05-11, một cache GitHub Actions dung lượng 1.1GB có tên Linux-pnpm-store-6f9233a50def742c09fde54f56553d6b449a535adf87d4083690539f49ae4da11 được lưu vào TanStack/router
    • Cache này được lưu trong scope refs/heads/main và được cấu hình để khớp với khóa mà release.yml sẽ truy xuất ở lần push main tiếp theo
    • Vào 11:31 UTC ngày 2026-05-11, kẻ tấn công đưa PR trở lại b1c061af, tức HEAD của main khi đó, khiến PR hiển thị như một no-op 0 file, rồi đóng PR và xóa branch ngay trong cùng phút, nhưng cache đã bị đầu độc vẫn còn lại
  • Kích hoạt: giai đoạn phát hành

    • Vào 19:15 UTC ngày 2026-05-11, Manuel merge PR #7369, tạo ra một lần push lên main, và workflow run 25613093674 của release.yml bắt đầu lúc 19:15:44 rồi thất bại
    • Vào 19:20:39 UTC ngày 2026-05-11, npm registry nhận publish của @tanstack/history@1.161.9 cùng 41 package sibling khác
    • Tổng cộng khoảng 84 phiên bản trên 42 package đã được publish, nhưng ở đúng thời điểm này chỉ thấy khoảng một nửa; phần còn lại được publish trong lần chạy thứ hai
    • Việc xác thực publish được thực hiện qua OIDC trusted-publisher binding cho TanStack/router release.yml@refs/heads/main, nhưng không phải xảy ra trong step Publish Packages của workflow đã bị bỏ qua do test thất bại
    • Bên thực sự publish là malware chạy trong giai đoạn test/dọn dẹp, nó mint OIDC token với quyền id-token: write rồi POST trực tiếp tới registry.npmjs.org
    • Vào 19:20:47 UTC ngày 2026-05-11, run 25613093674 kết thúc với trạng thái failure
    • Vào 19:16 UTC ngày 2026-05-11, Manuel merge PR #7382, tạo ra lần push thứ hai lên main, và workflow run 25691781302 bắt đầu lúc 19:16:22
    • Lần chạy thứ hai cũng restore cùng cache đã bị đầu độc, và vào 19:26:14 UTC ngày 2026-05-11, bộ phiên bản thứ hai cho mỗi package, bao gồm @tanstack/history@1.161.12, được publish bằng cùng cơ chế OIDC
    • Vào 19:26:20 UTC ngày 2026-05-11, run 25691781302 cũng kết thúc với trạng thái failure
  • Phát hiện và ứng phó

    • Khoảng 19:50 UTC ngày 2026-05-11, nhà nghiên cứu bên ngoài carlini mở issue #7383 kèm fingerprint optionalDependencies độc hại và danh sách package
    • Danh sách ban đầu gồm 14 trong số 42 package, và nhà nghiên cứu cũng trực tiếp thông báo cho bộ phận bảo mật npm
    • Khoảng 20:00 UTC ngày 2026-05-11, Manuel xác nhận sự cố trong #7383 và bắt đầu ứng phó
    • Khoảng 20:10 UTC ngày 2026-05-11, Manuel gỡ quyền push GitHub của các thành viên khác trong nhóm để đề phòng khả năng máy người dùng đã bị xâm phạm
    • Khoảng 20:30 UTC ngày 2026-05-11, Tanner gửi toàn bộ danh sách IOC và yêu cầu gỡ tarball phía registry tới security@npmjs.com, đồng thời nộp báo cáo malware chính thức qua npm
    • Khoảng 21:00 UTC ngày 2026-05-11, sau khi quét toàn bộ 295 package @tanstack/*, phạm vi được xác nhận là 42 package và 84 phiên bản
    • Tanner bắt đầu deprecate toàn bộ 84 package bị ảnh hưởng trên npm, và @tan_stack cùng các maintainer phát đi cảnh báo công khai trên Twitter/X, LinkedIn và Bluesky
    • Vào 21:30 UTC ngày 2026-05-11, vector đầu độc cache pull_request_target trong bundle-size.yml và fork zblgg/configuration được xác định
    • Tất cả mục cache của mọi repository TanStack/* trên GitHub đã bị xóa qua API
    • PR hardening được merge, bundle-size.yml được cấu hình lại, thêm guard repository_owner, và ref của action bên thứ ba được ghim bằng SHA
    • GitHub Security Advisory chính thức được công bố và CVE đã được yêu cầu

Nguyên nhân gốc rễ

  • Sự kết hợp của ba lỗ hổng

    • Cuộc tấn công cần đồng thời cả ba lỗ hổng; chỉ một lỗ hổng riêng lẻ thì không đủ
    • Mỗi lỗ hổng nối liền ranh giới tin cậy của lỗ hổng khác theo chuỗi: mã fork PR đi vào cache của base repository, cache của base repository đi vào runtime của release workflow, rồi runtime của release workflow dẫn tới quyền ghi vào npm registry
  • Mẫu pull_request_target “Pwn Request”

    • bundle-size.yml được chạy bằng pull_request_target cho fork PR, và trong trigger context đó nó checkout PR merge ref của fork rồi chạy build
    • Cấu trúc cốt lõi như sau
    on:
      pull_request_target:
        paths: ['packages/**', 'benchmarks/**']
    
    jobs:
      benchmark-pr:
        steps:
          - uses: actions/checkout@v6.0.2
            with:
              ref: refs/pull/${{ github.event.pull_request.number }}/merge # fork's merged code
    
          - uses: TanStack/config/.github/setup@main # transitively calls actions/cache@v5
    
          - run: pnpm nx run @benchmarks/bundle-size:build # executes fork-controlled code
    
    • Tác giả workflow đã tách job comment-pr và job benchmark-pr để phân chia ranh giới tin cậy, và trong YAML comment có ghi rõ ý định giữ benchmark-pr là “untrusted with read-only permissions”
    • Tuy nhiên, thao tác lưu post-job của actions/cache@v5 không bị chặn bởi permissions:, và việc ghi cache không dùng GITHUB_TOKEN của workflow mà dùng token nội bộ của runner
    • Vì vậy, cấu hình permissions: contents: read không thể ngăn việc sửa đổi cache
    • Phạm vi cache ở cấp repository, và các lần chạy pull_request_target dùng cache scope của base repository chia sẻ cùng phạm vi đó với push lên main
    • PR chạy trong cache scope của base repository có thể làm nhiễm bẩn cache entry mà production workflow của main sẽ restore về sau
  • Đầu độc cache của GitHub Actions

    • vite_setup.mjs độc hại được thiết kế để ghi dữ liệu theo đúng pnpm-store key mà workflow release.yml hợp lệ sẽ tính toán và tra cứu
    • Key mục tiêu có dạng Linux-pnpm-store-${hashFiles('**/pnpm-lock.yaml')}
    • Khi job benchmark-pr kết thúc, post-step của actions/cache@v5 lưu pnpm store đã bị đầu độc đúng với key đó
    • Sau đó khi release.yml chạy trên push lên main, step Setup Tools khôi phục entry đã bị đầu độc đúng như thiết kế của kẻ tấn công
    • Kiểu tấn công này thuộc họ GitHub Actions cache poisoning mà Adnan Khan đã tài liệu hóa vào năm 2024; đây không phải lỗi chỉ riêng TanStack gặp phải mà là vấn đề thiết kế của GitHub Actions cần được giảm thiểu một cách có chủ đích
  • Trích xuất token OIDC từ bộ nhớ runner

    • release.yml khai báo id-token: write một cách chính đáng vì cần nó cho npm OIDC trusted publishing
    • Khi pnpm store đã bị đầu độc được restore vào runner, binary do kẻ tấn công kiểm soát sẽ hiện diện trên đĩa và được gọi trong bước build
    • Binary đó tìm tiến trình GitHub Actions Runner.Worker qua /proc/*/cmdline, rồi đọc /proc/<pid>/maps/proc/<pid>/mem để dump bộ nhớ của worker
    • Sau đó nó trích xuất token OIDC mà runner đã lazy mint dưới thiết lập id-token: write từ trong bộ nhớ
    • Với token đã trích xuất, kẻ tấn công xác thực yêu cầu POST trực tiếp tới registry.npmjs.org, qua đó bỏ qua hoàn toàn step Publish Packages của workflow
    • Cách trích xuất bộ nhớ này giống với phương thức dùng trong vụ compromise tj-actions/changed-files vào tháng 3/2025, và cùng một Python script có chứa attribution comment đã được sử dụng
    • Kẻ tấn công không phát minh ra kỹ thuật mới mà chỉ tái tổ hợp các nghiên cứu công khai
  • Vì sao từng thành phần riêng lẻ không đủ

    • Bản thân pull_request_target vẫn có thể dùng cho các tác vụ đáng tin cậy như label hoặc comment
    • Chỉ đầu độc cache bên trong một dependency đã bị xâm phạm thì vẫn cần một phương tiện publish riêng
    • Chỉ riêng việc trích xuất token OIDC vẫn cần sẵn khả năng thực thi mã trên runner

Phát hiện và IOC

  • Con đường phát hiện

    • Việc phát hiện không diễn ra từ nội bộ mà đến từ bên ngoài
    • carlini đã mở issue #7383 khoảng 20 phút sau khi gói được publish và cung cấp toàn bộ phân tích kỹ thuật
    • Ngay sau khi Tanner bắt đầu war room, anh nhận được cuộc gọi từ Socket.dev để xác nhận tình hình
  • Fingerprint cho maintainer downstream và công cụ bảo mật

    • Trong manifest của các gói @tanstack/*, mục optionalDependencies sau là IOC quan trọng
    "optionalDependencies": {
      "@tanstack/setup": "github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c"
    }
    
    • IOC ở cấp tệp là router_init.js tại root của package, có dung lượng khoảng 2.3MB và không nằm trong "files"
    • Cache key là Linux-pnpm-store-6f9233a50def742c09fde54f56553d6b449a535adf87d4083690539f49ae4da11
    • URL payload giai đoạn 2 là https://litter.catbox.moe/h8nc9u.js, https://litter.catbox.moe/7rrc6l.mjs
    • Mạng lưới dùng để rò rỉ dữ liệu là filev2.getsession.org, seed{1,2,3}.getsession.org
    • Danh tính commit giả mạo là claude <claude@users.noreply.github.com>; đây không phải Anthropic Claude thật mà là email GitHub no-reply bị ngụy tạo
    • Các tài khoản của kẻ tấn công thực sự là zblgg id 127806521, voicproducoes id 269549300
    • Fork của kẻ tấn công là github.com/zblgg/configuration, được đổi tên từ fork TanStack/router nhằm né tránh việc tìm kiếm
    • Payload commit orphan trong mạng fork là 79ac49eedf774dd4b0cfa308722bc463cfe5885c
    • Workflow run thực hiện publish độc hại là github.com/TanStack/router/actions/runs/25613093674 attempt 4 và github.com/TanStack/router/actions/runs/25691781302

Bài học rút ra

  • Những điểm làm tốt

    • Các nhà nghiên cứu bên ngoài đã phát hiện sự cố trong khoảng 20 phút sau khi xảy ra và báo cáo kèm đầy đủ chi tiết kỹ thuật
    • Đội ngũ maintainer đã lập tức phối hợp trên nhiều múi giờ
    • Cộng đồng phát hiện đã xác lập được các mẫu IOC công khai rõ ràng chỉ trong vài giờ
  • Những điểm cần cải thiện

    • Không có alerting nội bộ, và việc bị compromise được biết đến từ bên thứ ba
    • Cần có cơ chế theo dõi publish của chính mình, đồng thời có kế hoạch hợp tác chặt chẽ hơn với các công ty nghiên cứu bảo mật hệ sinh thái có thể phát hiện nhanh các vấn đề như vậy và rút ngắn feedback loop
    • Workflow pull_request_target từ lâu đã được biết là một mẫu rủi ro nhưng chưa được audit
    • Các floating ref của third-party action như @v6.0.2, @main tạo ra rủi ro supply-chain thường trực, tách biệt với chính sự cố lần này
    • Do chính sách của npm là “không thể unpublish nếu có dependent”, nên gần như tất cả các package bị ảnh hưởng đều không thể unpublish
    • Phải phụ thuộc vào npm security để gỡ tarball ở phía registry, khiến thời gian các tarball độc hại còn ở trạng thái có thể cài đặt kéo dài thêm vài giờ
    • Danh sách 7 maintainer trong npm scope đồng nghĩa với 7 mục tiêu đánh cắp credential riêng biệt cho cùng một blast radius
    • Binding trusted-publisher của OIDC không có bước review cho từng lần publish; một khi đã cấu hình, bất kỳ code path nào trong workflow cũng có thể mint token có quyền publish
    • Giải pháp thay thế cần thiết là chuyển sang classic token ngắn hạn có review thủ công, hoặc bổ sung provenance-source-verification để phát hiện publish từ các workflow step ngoài dự kiến
  • Những điểm may mắn

    • Kẻ tấn công đã chọn payload làm hỏng bài test, khiến bước publish bình thường bị skip và không tạo ra tarball trông “sạch” hơn
    • Vì vậy cuộc tấn công đã lộ ra đủ ồn ào để được phát hiện nhanh
    • Nếu kẻ tấn công thận trọng hơn và không làm hỏng bài test, họ có thể đã publish âm thầm thêm vài giờ nữa
    • Kẻ tấn công đã tái sử dụng một script memory-dump công khai có chứa attribution comment, không viết mã mới nên việc đối sánh IOC diễn ra nhanh hơn

Câu hỏi còn lại

  • Cần xác minh liệu step Setup Tools trong bundle-size.yml có thực sự gọi actions/cache@v5 hay không
  • Cần đọc và xác minh post-job log của một trong các lần chạy pull_request_target cho PR #7378; ví dụ run id là 25666610798
  • Cần xác minh commit head PR ban đầu chứa gì trước khi biến mất do force-push; có thể vẫn còn trong GitHub reflog
  • Cần xác minh cách commit độc hại đi vào git object store của fork là push git trực tiếp hay được tạo qua GitHub web UI, thứ vốn sẽ để lại audit-log entry
  • Cần đối chiếu lịch sử hoạt động để xác minh voicproducoes là tài khoản thật hay sock puppet
  • Cần xác minh cache npm, có vẻ là 6 entry linux-npm-store-* trùng lặp, có bị đầu độc hay không và liệu chúng có thực sự được dùng không
  • Cần xác minh liệu cuộc tấn công có cần Nx Cloud hay chỉ với GitHub Actions cache cũng đã đủ hoạt động
  • Cần xác định liệu có thể nhận diện các fork khác trong mạng fork của TanStack/router có chứa orphan payload commit hay không
  • Nếu các fork khác đang host commit đó, khả năng truy cập github:tanstack/router#79ac49ee... sẽ được duy trì và việc dọn dẹp sẽ khó hơn
  • Cần audit xem các repo TanStack khác như router, query, table, form, virtual có dùng cùng kiểu mẫu bundle-size.yml hay không
  • Cần lấy từ npm support số người dùng đã thực sự tải các phiên bản bị ảnh hưởng trong khoảng thời gian publish
  • Cần xác minh liệu máy của 7 maintainer có bị compromise riêng rẽ hay không
  • Publish độc hại không dùng npm token của maintainer, nhưng máy của maintainer có thể là mục tiêu thứ cấp của logic tự lây lan

Tài liệu tham khảo

1 bình luận

 
Ý kiến trên Hacker News
  • Cần cẩn thận khi thu hồi token. Có vẻ payload cài một dead-man's switch vào ~/.local/bin/gh-token-monitor.sh, rồi đăng ký làm dịch vụ người dùng systemd trên Linux và LaunchAgent com.user.gh-token-monitor trên macOS
    Nó dùng token đã đánh cắp để poll api.github.com/user mỗi 60 giây, và nếu token bị thu hồi khiến trả về HTTP 40x thì sẽ chạy rm -rf ~/
    https://github.com/TanStack/router/issues/7383#issuecomment-...

    • Thực tế mà nói, nếu đã cài malware thì đằng nào cũng phải xóa sạch và cài lại toàn bộ máy
    • Đáng kinh ngạc. Tình huống kiểu hủy diệt đảm bảo lẫn nhau
      Có cảm giác 5 năm tới thế giới phần mềm sẽ cực kỳ khốc liệt, và hệ thống air-gapped sẽ trở nên rất quan trọng
    • Đáng ra lúc nào cũng nên có backup, nhưng nếu sự cố này khiến mọi người bắt đầu thiết lập backup thì ít ra cũng là điều tốt
  • Gói npm @mistralai/mistralai cũng bị xâm phạm như một phần của worm này
    https://github.com/mistralai/client-ts/issues/217
    Hiện giờ nó đã bị gỡ khỏi npm registry

  • Thật không may, đây có vẻ là bằng chứng cho thấy chỉ Trusted Publishing thôi thì chưa đủ để triển khai an toàn từ CI. Nếu kẻ tấn công ở bên trong pipeline CI hoặc đã đánh cắp quyền quản trị repo, việc phát hành là quá dễ
    Đây không phải thông tin mới, và Trusted Publishing cũng không được thiết kế để đảm bảo điều đó, nhưng khi chuyển từ phát hành cục bộ kèm xác thực hai yếu tố sang Trusted Publishing, bạn sẽ mở ra kiểu đường tấn công thông qua xâm nhập CI như thế này. Yếu tố thứ hai từng chặn npm publish khi làm việc cục bộ sẽ biến mất
    Theo diễn biến hiện tại, có vẻ kẻ tấn công đã kiểm soát pipeline CI/CD, rồi do npm publish không có yếu tố thứ hai nên đã đánh cắp token OIDC để hoàn tất phát hành. Điều thú vị nhưng là chuyện riêng khác là bản thân job phát hành đã thất bại, nhưng payload trong commit độc hại dường như có thể tự phát hành bằng token OIDC của workflow
    Điều mong muốn là vẫn giữ mô hình Trusted Publisher không có token dài hạn, nhưng triển khai từ CI phải còn một yếu tố thứ hai nằm ngoài GitHub. Tức là cần triển khai theo giai đoạn, nơi ai đó ở phía npm phải dùng xác thực hai yếu tố để nâng artifact lên trạng thái công khai thực sự
    Nếu việc phát hành chỉ có thể diễn ra trong mô hình tin cậy của GitHub, thì bất kỳ ai lấy được token quản trị repo hoặc chèn mã độc vào pipeline đều có thể dễ dàng hoàn tất phát hành. Nếu có một yếu tố thứ hai thật sự nằm ngoài ngữ cảnh GitHub, họ vẫn có thể phá repo hoặc cài mã độc, nhưng sẽ không thể phát hành nếu thiếu yếu tố thứ hai cho registry

    • Tôi có một gói bán-phổ-biến và vẫn dùng phát hành cục bộ kèm xác thực hai yếu tố. Trusted Publishing trông quá phức tạp và có vẻ liên tục bị hack, nên tôi tự hỏi liệu nó có quá phức tạp để chúng tôi vận hành an toàn hay không, có khi phải thiết kế lại từ đầu
    • Tôi vẫn nghĩ Trusted Publishing là một cải tiến lớn, nhưng ý tưởng yêu cầu yếu tố thứ hai khi đánh dấu release là công khai thực sự rất hay. Như vậy việc chạy loại worm CI này sẽ trở nên rất khó
    • Tôi muốn ký bằng chạm với thứ như YubiKey. Ngay từ đầu, ý tưởng tin rằng cloud sẽ thay mình quản lý thông tin xác thực đã có vẻ là một sai lầm
    • Blog của astral gần đây có chỉ ra cách dùng Trusted Publishing mà vẫn có release gate, tức chèn bước phê duyệt thủ công vào workflow phát hành. Đáng tiếc là tài liệu Trusted Publishing của NPM/PyPI/Rubygems thậm chí không nhắc tới khả năng này, cũng không cung cấp mặc định
    • Tôi chưa bao giờ hiểu vì sao mọi người nói Trusted Publishing tạo ra khác biệt gì với kiểu tấn công chuỗi cung ứng này
  • Phân tích hậu kiểm: https://tanstack.com/blog/npm-supply-chain-compromise-postmo...

    • Cảm ơn TanStack vì bản phân tích hậu kiểm, nhưng tôi nghĩ các vấn đề bảo mật ở góc độ toàn bộ hệ sinh thái npm vẫn là mối lo đang tiếp diễn
      Tôi muốn biết liệu có bằng chứng nào cho thấy các gói phụ có thể đã import hoặc đóng gói các package TanStack thì cũng được xem là an toàn hay không
  • Script postinstall đúng là chí mạng. Mọi người nên dùng pnpm
    Việc một commit “mồ côi” được push lên FORK mà lại có thể gây ra chuyện này từ npm client thì thật vô lý. Tôi nghĩ GitHub cũng phải chịu trách nhiệm lớn. Việc commit từ một fork độc hại lại có thể truy cập bằng URI không thể phân biệt với repo hợp lệ thông qua shared object store của GitHub là một thiết kế hoàn toàn điên rồ

    • Khi chạy ứng dụng với dependency đã cập nhật thì kiểu gì đoạn mã đó cũng sẽ chạy. Root hay non-root không quan trọng, điều quan trọng là những thứ có giá trị đều truy cập được bằng quyền của người dùng đang chạy ứng dụng
    • Tôi không hiểu sao đây lại không phải sự cố P0 của GitHub. Có ai giải thích được không?
      Lúc đầu đọc tôi tưởng họ dùng sai từ “fork”, ý là thực ra một branch trong repo chính thức. Tôi đã nghĩ chuyện đó chắc không thể là thật, mà hóa ra đúng là như vậy
  • https://tanstack.com/blog/npm-supply-chain-compromise-postmo...
    TanStack vừa công bố phân tích hậu kiểm về sự cố này

  • Đây là lời nhắc phải cấu hình môi trường npm cho an toàn
    https://gajus.com/blog/3-pnpm-settings-to-protect-yourself-f...
    Chỉ vài cấu hình là có thể giảm rủi ro lớn

    • Trên npm v11 trở lên còn có allow-git=none: https://github.blog/changelog/2026-02-18-npm-bulk-trusted-pu...
    • Tôi nghĩ bài này sai về độ tuổi phát hành tối thiểu của npm. 1) Tên cấu hình là min-release-age. 2) Không hiểu vì sao nó lại được tính theo ngày chứ không phải phút: https://docs.npmjs.com/cli/v11/using-npm/config#min-release-...
      Theo tôi, không gian trình quản lý dependency đang bị phân mảnh một cách hoàn toàn không cần thiết
    • Khẳng định rằng đặt tuổi tối thiểu là 7 ngày thì “sẽ không bao giờ” dính lỗ hổng chuỗi cung ứng trên npm là quá đà
    • Mọi dependency đều phải được pin phiên bản
      Nếu dependency version của package đang là kiểu ^1.0.0 hay thậm chí "*", đừng đọc tiếp nữa mà hãy pin ngay về phiên bản an toàn
  • Tôi làm gấp bằng Claude để giúp hạn chế lây lan. Dĩ nhiên vẫn phải tự kiểm chứng, nhưng nó sẽ quét xem máy có các gói bị xâm phạm đã được nhắc tới hay không: https://github.com/PaulSinghDev/tanstack-shai-hulud-fix

  • Có vẻ giờ đã đến mức mọi người nên chạy từng project trong VM riêng biệt
    Với các lỗ hổng leo thang đặc quyền cục bộ gần đây, chỉ Docker thôi chắc chắn không đủ. Ngay từ đầu container cũng không được thiết kế như ranh giới bảo mật chính

    • Devcontainers là dạng “môi trường phát triển cách ly” nổi tiếng nhất, nhưng không phải VM hoàn chỉnh và cũng không bảo vệ trọn vẹn trong vụ này. Vì thông tin xác thực GitHub tự động được đưa vào container
      Nếu còn có dịch vụ cloud nào khác cần truy cập từ bên trong container thì trình đánh cắp credential này cũng sẽ lấy luôn. Dù vậy nó vẫn giúp giảm blast radius, nên ít nhất cũng là cải thiện
    • QubesOS đi đúng hướng. Người ta sẽ muốn có nhiều VM dưới root với nhiều lớp bảo mật chồng lớp
    • Nếu nhất định phải dùng container, bạn cũng có thể đặt một VM cho mỗi container. Mấy tuần gần đây tôi thấy khá yên tâm vì mọi thứ đều chạy trong VM thay vì bất kỳ dịch vụ Kubernetes ngẫu nhiên nào
    • May mắn là các project dùng hệ sinh thái ngôn ngữ an toàn hơn như C và C++ thì không gặp vấn đề này :-)
  • Trời ạ, lại thêm một package lớn nữa. Tôi đăng lại PSA đã viết sau khi Axios và LiteLLM bị xâm phạm. Phần về script vòng đời cũng áp dụng ở đây
    npm/bun/pnpm/uv giờ đều hỗ trợ đặt tuổi phát hành tối thiểu cho package. Tôi cũng đã thêm ignore-scripts=true vào ~/.npmrc, và theo phân tích thì riêng việc này cũng đã có thể giảm nhẹ lỗ hổng. bun và pnpm mặc định không chạy script vòng đời
    Cách đặt tuổi phát hành tối thiểu 7 ngày ở mức toàn cục như sau
    ~/.config/uv/uv.toml
    exclude-newer = "7 days"
    ~/.npmrc
    min-release-age=7 # days
    ignore-scripts=true
    ~/Library/Preferences/pnpm/rc
    minimum-release-age=10080 # minutes
    ~/.bunfig.toml
    [install]
    minimumReleaseAge = 604800 # seconds
    Nếu cần ghi đè cấu hình toàn cục thì dùng cờ CLI
    npm install --min-release-age 0
    pnpm add --minimum-release-age 0
    uv add --exclude-newer "0 days"
    bun add --minimum-release-age 0
    Nói thêm một điểm, dường như có lo ngại rằng nếu triển khai thời gian chờ dependency ở quy mô lớn thì sẽ làm chậm việc phát hiện lỗ hổng, hoặc rằng thời gian chờ dependency là một kiểu ăn theo người khác gánh rủi ro. Tôi không đồng ý. Thứ mà thời gian chờ dependency đánh đổi là sở thích theo thời gian, và sẽ luôn có những người có sở thích theo thời gian cao hơn tôi
    0: https://news.ycombinator.com/item?id=47582220
    1: https://news.ycombinator.com/item?id=47513932

    • Đồng ý. Tôi thật may vì đã bật các thiết lập này từ tháng 3, trước hai đợt sóng vừa rồi. Ngoài ra nên commit lockfile vào repo và cẩn thận khi thêm dependency mới
      Để tránh thay đổi ngoài ý muốn, có thể dùng pnpm install --frozen-lockfile. Cũng cần nhớ rằng nếu không đặt min-release-age, bạn vẫn có thể kéo vào các package bị ảnh hưởng thông qua dependency gián tiếp. Nếu có thể thì cũng nên pin luôn phiên bản của package manager