Kết quả cần đạt: một pipeline có thể nhận cảnh báo package độc hại, lưu bằng chứng gốc, xác minh nguồn, tìm package và phiên bản bị ảnh hưởng, phát hành quyết định có phiên bản rồi hoàn tác mà không xóa lịch sử. Thiết kế này phù hợp với đội ngũ đã có manifest, lockfile, SBOM hoặc inventory artifact nhưng hiện xử lý cảnh báo rời rạc.
Đây là bài hướng dẫn kiến trúc và quy trình, không phải một sản phẩm cài sẵn. Bạn vẫn phải chọn kho sự kiện, cơ sở dữ liệu, hàng đợi và hệ thống thực thi chính sách phù hợp với môi trường của mình. Không nên nối hành động block vào production ngay từ đầu; hãy bắt đầu bằng quarantine và monitor, sau đó kiểm thử đường phát hiện, đường từ chối an toàn và đường khôi phục.
1. Xác định đầu ra trước khi chọn công nghệ
Đầu ra của pipeline không nên chỉ là “đã nhận cảnh báo”. Nó phải là một quyết định bảo mật có phiên bản, có thể truy ngược đến bằng chứng và áp dụng cho một phạm vi cụ thể.
allow: chưa có bằng chứng đủ để hạn chế package hoặc phiên bản;monitor: có tín hiệu cần theo dõi nhưng chưa đủ cơ sở chặn;quarantine: dữ liệu chưa đạt kiểm tra, chưa được phép làm thay đổi policy;investigate: dữ liệu mâu thuẫn, thiếu provenance hoặc chưa xác định được phạm vi;block: package hoặc phiên bản bị ngăn trong bước cài đặt, build, phát hành hoặc truy cập registry;withdrawn: cảnh báo đã bị rút lại hoặc điều chỉnh, nhưng bằng chứng và lịch sử vẫn được giữ.
Mỗi quyết định nên trả lời được các câu hỏi sau:
| Câu hỏi | Trường dữ liệu nên có |
|---|---|
| Package nào đang được cảnh báo? | ecosystem, package_name, purl |
| Phiên bản nào bị ảnh hưởng? | versions hoặc khoảng introduced/fixed |
| Nguồn nào đưa ra kết luận? | source, source_url, source_record_id |
| Dữ liệu có đáng tin để xử lý không? | provenance, hash payload, trạng thái chữ ký, thời điểm nhận |
| Quyết định nào đang có hiệu lực? | decision, decision_version, effective_at |
| Có thể quay lại trạng thái nào? | previous_snapshot_id, rollback_state |
Không biến mức độ nghiêm trọng thành quyết định tự động duy nhất. Package độc hại trong dependency production, trong script cài đặt của CI runner hoặc chỉ nằm trong tài liệu thử nghiệm có phạm vi xử lý khác nhau.
2. Tách pipeline thành các vùng tin cậy
Thực hiện sơ đồ này ở tầng kiến trúc hoặc workflow engine. Tên công cụ có thể là Kafka, SQS, Pub/Sub, PostgreSQL, object storage hay một hệ thống khác; điều quan trọng là ranh giới quyền và trạng thái.
- Ingest zone: nhận webhook, file hoặc phản hồi API. Dữ liệu tại đây chưa được tin cậy.
- Evidence store: lưu payload gốc, header cần thiết, thời điểm nhận, hash và trạng thái xác minh.
- Normalization zone: chuyển dữ liệu về mô hình chung nhưng vẫn giữ trường riêng của nguồn.
- Decision zone: đối chiếu package, phiên bản, manifest, SBOM hoặc inventory artifact.
- Action zone: phát hành lệnh chặn, tạo ticket, gửi thông báo hoặc yêu cầu xoay vòng credential.
- Audit and rollback zone: lưu snapshot, lịch sử thay đổi, lý do điều chỉnh và trạng thái có thể phục hồi.
Không cho phép webhook hoặc file tải về ghi thẳng vào bảng blocked_packages. Nếu nguồn bị giả mạo, gửi bản ghi trùng hoặc sửa ngược thời gian, nó có thể thay đổi hành vi của hệ thống trước khi được kiểm tra.
3. Chuẩn bị đầu vào và quyền truy cập
Trước khi viết adapter, hãy xác định nơi thực hiện từng thao tác:
- Trong hệ thống nguồn: tạo token hoặc webhook có phạm vi tối thiểu, bật chữ ký nếu nhà cung cấp hỗ trợ và ghi nhận phiên bản API.
- Trong ingest service: chỉ nhận dữ liệu, kiểm tra kích thước và xác thực chữ ký; không có quyền sửa policy production.
- Trong evidence store: bật quyền chỉ ghi thêm hoặc cơ chế khóa đối tượng; chỉ nhóm điều tra được đọc payload gốc.
- Trong worker: thực hiện parse, chuẩn hóa và đối chiếu bằng tài khoản riêng; retry có giới hạn.
- Trong hệ thống tiêu thụ: chỉ nhận quyết định đã được phát hành, có phiên bản và correlation ID.
Đầu vào tối thiểu gồm một nguồn cảnh báo ổn định, một nơi lưu append-only hoặc object lock, inventory dependency và một đích nhận quyết định như CI/CD, registry proxy, hệ thống ticket hoặc kênh thông báo. Nếu chưa có inventory, pipeline chỉ có thể nói “nguồn đã cảnh báo package này”, chưa thể kết luận package đó đang tồn tại trong hệ thống của bạn.
4. Thiết kế mô hình dữ liệu giữ được provenance
Provenance là chuỗi thông tin giúp truy ngược payload nào đã được nhận, từ nguồn nào, vào thời điểm nào, qua parser và rule nào, rồi sinh ra quyết định nào. Trong ngữ cảnh supply-chain security, provenance hỗ trợ truy nguyên nguồn gốc và cách một artifact được tạo ra; nó không tự chứng minh package là an toàn (theo SLSA Provenance: https://slsa.dev/spec/v1.2/provenance).
Đây là một event minh họa. Các giá trị evt_demo_001 và MAL-DEMO-001 chỉ là dữ liệu giả định, không phải mã định danh của một sự cố thực tế:
{
"event_id": "evt_demo_001",
"idempotency_key": "openssf:mal-demo-001:sha256:abc123",
"correlation_id": "corr_demo_001",
"source": {
"name": "openssf-malicious-packages",
"record_id": "MAL-DEMO-001",
"retrieved_at": "2024-06-01T10:15:00Z",
"url": "https://source.example/advisory/MAL-DEMO-001",
"payload_sha256": "abc123",
"signature_status": "verified"
},
"package": {
"ecosystem": "npm",
"name": "example-package",
"purl": "pkg:npm/example-package",
"affected_versions": ["1.4.2", "1.4.3"]
},
"classification": {
"kind": "malicious_package",
"confidence": "confirmed",
"reason_codes": ["install_script", "credential_exfiltration"]
},
"processing": {
"parser_version": "parser-1.0.0",
"rule_set_version": "rules-1.0.0"
},
"decision": {
"state": "block",
"version": 7,
"previous_snapshot_id": "snapshot_6"
}
}
Giữ ba nguyên tắc:
- Không ghi đè payload gốc: bản sửa phải tạo event hoặc revision mới, liên kết với bản trước.
- Không dùng tên package làm định danh duy nhất: kết hợp ecosystem với định danh phù hợp như purl; cùng một tên có thể xuất hiện ở nhiều hệ sinh thái.
- Phân biệt các loại phiên bản:
1.4.3là phiên bản package, revision là lần advisory thay đổi, còndecision_version: 7là lần hệ thống thay đổi kết luận.
5. Xây dựng luồng tiếp nhận cảnh báo
5.1. Nhận dữ liệu và chống xử lý trùng
Trong adapter hoặc handler, tạo idempotency_key. Ưu tiên ID ổn định do nguồn cung cấp. Nếu nguồn không có ID, có thể tạo khóa từ source + record_id + payload_sha256. Hai payload giống hệt nhau phải cho ra cùng một kết quả xử lý; payload có hash khác phải tạo revision mới thay vì sửa event cũ.
Webhook nên xác thực chữ ký theo tài liệu của nhà cung cấp, giới hạn kích thước payload, đặt thời gian chờ và chống replay bằng timestamp hoặc nonce khi cơ chế nguồn hỗ trợ. Handler chỉ nên ghi nhận sự kiện rồi trả lời nhanh; worker phía sau mới parse và đối chiếu.
5.2. Lưu bằng chứng trước khi phân tích
Trong evidence store, lưu payload gốc cùng:
- thời điểm nhận và thời điểm nguồn công bố nếu có;
- HTTP status, content type và phiên bản API;
- hash của payload;
- chữ ký hoặc kết quả xác minh;
- tài khoản, workload identity hoặc credential đã nhận dữ liệu;
- phiên bản parser và rule set được dùng;
- trạng thái xử lý như
received,validated,quarantinehoặcfetch_failed.
Nếu tải dữ liệu thất bại, giữ trạng thái fetch_failed; không diễn giải trạng thái này thành “không có cảnh báo”. Đây là hai kết luận bảo mật khác nhau.
5.3. Chuẩn hóa dữ liệu kiểu OSV nhưng không làm mất trường nguồn
Phần chuẩn hóa nên có hai lớp:
- Canonical fields: ecosystem, package, phiên bản hoặc khoảng phiên bản, loại cảnh báo, thời gian hiệu lực và trạng thái.
- Source-specific fields: các trường riêng của OpenSSF, GitHub hoặc nguồn khác, được lưu dưới JSON có phiên bản.
Các báo cáo theo mô hình OSV thường biểu diễn package, phiên bản hoặc khoảng phiên bản bị ảnh hưởng, nội dung chi tiết và tham chiếu. Tuy nhiên, không phải advisory nào cũng có CVSS, phiên bản sửa hoặc phạm vi hoàn chỉnh. Vì vậy, giá trị thiếu phải được giữ là “chưa biết”, không tự động chuyển thành “không có”.
Ví dụ biểu diễn một khoảng phiên bản trong mô hình nội bộ:
{
"range_type": "ECOSYSTEM",
"introduced": "1.4.2",
"fixed": null,
"source_expression": "versions: 1.4.2, 1.4.3"
}
Giữ source_expression để điều tra khi parser diễn giải khác với nguồn. Không tự suy ra fixed chỉ vì một phiên bản mới hơn chưa xuất hiện trong báo cáo.
5.4. Kiểm tra trước khi phát hành
Worker chuẩn hóa phải chạy ít nhất bốn lớp kiểm tra:
- Schema validation: JSON hợp lệ, trường bắt buộc đúng kiểu, enum thuộc danh sách cho phép.
- Semantic validation: ecosystem khớp định danh package, phiên bản không mâu thuẫn, khoảng phiên bản hợp lệ theo quy tắc của ecosystem.
- Provenance validation: nguồn, hash, chữ ký hoặc quyền truy cập có thể kiểm tra được.
- Change validation: bản sửa có liên kết đến revision trước, actor hoặc workload tạo thay đổi và lý do thay đổi.
Nếu một lớp thất bại, đưa event vào quarantine. Không bỏ qua toàn bộ nguồn chỉ vì một record lỗi; cũng không phát hành record chưa kiểm tra như thể nó đã hợp lệ.
6. Đối chiếu cảnh báo với inventory thực tế
Thực hiện bước này trong matcher, sau khi event đã qua validation. Cảnh báo chỉ trở thành rủi ro cụ thể khi đối chiếu với inventory của bạn. Nguồn inventory có thể là:
- SBOM của artifact hoặc image;
- manifest và lockfile của repository;
- dependency graph;
- kho package nội bộ;
- danh sách artifact đã triển khai;
- cache của runner hoặc máy build.
Đối chiếu theo ba tầng:
- Định danh: ecosystem, package name và purl có khớp không?
- Phiên bản: phiên bản đang dùng có nằm trong tập hoặc khoảng bị ảnh hưởng không?
- Bối cảnh: dependency là runtime, development, test, build tool hay chỉ xuất hiện trong cache?
GitHub Dependabot cung cấp dữ liệu cảnh báo qua giao diện và REST API, trong đó có các trường như package, ecosystem, manifest path, scope và quan hệ direct/transitive (theo REST API endpoints for Dependabot alerts: https://docs.github.com/en/rest/dependabot/alerts). Tuy nhiên, cảnh báo Dependabot không bao quát mọi vấn đề, và advisory độc hại không nhất thiết được tạo alert theo cùng cơ chế vulnerability advisory thông thường (theo Global security advisories: https://docs.github.com/en/code-security/concepts/vulnerability-reporting-and-management/global-security-advisories).
Bạn có thể bắt đầu từ cảnh báo repository rồi mở rộng sang nguồn chuyên biệt. Xem thêm cách bật cảnh báo mã độc Dependabot trên GitHub để hoàn thiện lớp phát hiện phía repository; bài này tập trung vào ingestion, provenance và rollback.
7. Chọn hành động theo độ chắc chắn và phạm vi ảnh hưởng
Policy engine nên nhận cả độ chắc chắn của nguồn, phạm vi package và bối cảnh sử dụng. Không dùng một rule duy nhất kiểu “malware = xóa package”.
| Tình huống | Hành động an toàn ban đầu | Khi nào nâng mức |
|---|---|---|
| Đã xác minh package và phiên bản độc hại trong production | Chặn phiên bản mới, cô lập artifact liên quan, mở incident | Điều tra lịch sử thực thi, credential và artifact đã phát hành |
| Package chỉ nằm trong development dependency | Chặn CI, tạo ticket nâng cấp hoặc gỡ bỏ | Nâng mức nếu script cài đặt chạy trên runner có secret |
| Nguồn mâu thuẫn hoặc thiếu provenance | Đưa vào quarantine, thông báo nhóm bảo mật | Cân nhắc chặn tạm thời nếu package sắp phát hành và rủi ro cao |
| Cảnh báo bị rút lại | Tạo correction event và chuyển decision sang withdrawn | Khôi phục allow hoặc monitor sau khi kiểm tra rule khác |
Nếu package từng chạy trên runner có token, gỡ package không có nghĩa là máy đã sạch. Có thể phải thu hồi và cấp lại credential, kiểm tra log truy cập, rà soát artifact sinh ra và đánh giá persistence. Đây là ứng phó sự cố, không nên tự động hóa hoàn toàn chỉ từ một trường severity.
8. Thiết kế rollback mà không xóa bằng chứng

Rollback phải phục hồi trạng thái phục vụ, không xóa lịch sử. Thực hiện theo trình tự sau:
- Trước khi phát hành decision mới, tạo snapshot bất biến, chẳng hạn
snapshot_42. - Gắn decision mới với
previous_snapshot_id. - Phát hành chỉ mục mới với số phiên bản tăng dần.
- Khi phát hiện lỗi, tạo correction event trỏ đến advisory và snapshot liên quan.
- Chạy lại matcher trên inventory hiện tại và kiểm tra các rule khác.
- Phê duyệt snapshot mục tiêu, ghi actor, lý do và thời điểm.
- Phát hành snapshot đó đến hệ thống tiêu thụ và gửi thông báo correction.
Ví dụ giả định:
snapshot_41: allow example-package@1.4.3
snapshot_42: block example-package@1.4.3
snapshot_43: withdrawn MAL-DEMO-001
active_snapshot: snapshot_43
Trong ví dụ này, hệ thống không xóa snapshot_42. Nó chỉ chuyển trạng thái đang áp dụng sang snapshot_43. Vì vậy, đội ngũ vẫn biết tại sao phiên bản từng bị chặn, ai phát hành quyết định và khi nào advisory được điều chỉnh.
Kiểm tra trước khi rollback
- Advisory đã thực sự bị rút lại hay chỉ thay đổi phạm vi phiên bản?
- Rule hiện tại có đang chặn package vì một nguồn khác không?
- Có artifact nào đã phát hành trong thời gian cảnh báo có hiệu lực không?
- Rollback chỉ đổi nhãn inventory hay còn mở quyền cài đặt trong CI/CD?
- Có cần giữ deny rule tạm thời cho package hoặc registry cụ thể không?
Rollback chỉ thay đổi dữ liệu và quyết định của pipeline. Nó không khôi phục credential đã lộ, xóa payload đã chạy hoặc bảo đảm máy đã sạch.
9. Dùng workflow hướng sự kiện nhưng giữ quyền kiểm soát
Trong workflow engine hoặc hệ thống hàng đợi, luồng xử lý có thể là:
source webhook/file/API
|
v
raw evidence store
|
v
schema + provenance validation
|
v
normalizer
|
v
package/version matcher
|
v
policy decision
|
v
quarantine | monitor | block | investigate
|
v
notification, ticket, CI/CD gate, audit log
Mỗi bước cần khai báo:
- input contract: nhận loại dữ liệu nào;
- output contract: tạo event nào;
- retry policy: lỗi tạm thời được thử lại bao nhiêu lần;
- dead-letter path: dữ liệu không xử lý được đi đâu;
- correlation ID: dùng để lần theo từ advisory đến hành động cuối;
- owner: nhóm chịu trách nhiệm khi parser hoặc policy lỗi.
Không để worker retry vô hạn với cùng payload. Sau số lần thử đã định trước, lưu lỗi có cấu trúc và chuyển event sang dead-letter queue để xử lý thủ công. Nếu không, một advisory sai định dạng có thể làm nghẽn toàn bộ pipeline.
10. Bảo vệ chính pipeline khỏi bị khai thác
Pipeline có thể thay đổi policy, gọi API repository và gửi thông báo nên là một thành phần cần bảo vệ riêng. Các kiểm soát tối thiểu gồm:
- tài khoản ingest chỉ có quyền ghi vào raw evidence và đọc metadata cần thiết;
- worker chuẩn hóa không được tự cấp quyền thay đổi production policy;
- quyền phát hành
blockhoặc rollback tách khỏi quyền sửa parser; - secret không xuất hiện trong payload, log hoặc thông báo;
- URL tham chiếu trong advisory được xử lý như dữ liệu không tin cậy; tránh SSRF khi tự động truy cập;
- parser giới hạn kích thước, độ sâu JSON, số lượng phiên bản và thời gian xử lý;
- mọi thay đổi policy có review, version và đường quay về policy trước;
- quyết định được đánh giá lại khi nguồn cập nhật advisory.
Provenance giúp truy nguyên artifact được tạo từ đâu và bằng cách nào, nhưng không tự chứng minh package không độc hại. Vẫn cần kết hợp provenance với inventory, phân tích hành vi, quyền runtime và thông tin ứng phó sự cố.
11. Kiểm thử nghiệm thu trước khi bật chặn tự động
Thực hiện các ca kiểm thử trong môi trường thử nghiệm bằng fixture giả lập, không bắt đầu bằng advisory thật:
- Gửi hai bản tin giống hệt nhau và xác minh kết quả idempotent, không tạo hai hành động.
- Sửa nội dung advisory để xác minh revision mới không ghi đè raw evidence cũ.
- Gửi record thiếu package hoặc ecosystem và xác minh record vào quarantine.
- Đối chiếu một package ở phiên bản bị ảnh hưởng với một phiên bản không bị ảnh hưởng.
- Rút lại advisory và xác minh correction event, active snapshot mới và audit trail.
- Chạy rollback rồi xác minh snapshot cũ vẫn đọc được và rule khác vẫn được áp dụng.
- Gửi hai nguồn cùng cảnh báo một package và xác minh chúng được gộp theo khóa chuẩn nhưng vẫn giữ danh sách nguồn.
- Làm cho parser hoặc nguồn hết thời gian chờ và xác minh retry/dead-letter path.
- Kiểm tra thông báo có correlation ID, package, phiên bản, lý do và hành động đề xuất.
- Đưa một package khác ecosystem vào fixture và xác minh rule không chặn nhầm toàn bộ ecosystem.
- Kiểm tra quyền để bảo đảm ingest không thể tự sửa production policy nếu chưa qua bước phê duyệt.
Chỉ bật hành động chặn tự động sau khi ba đường đi đã có kết quả quan sát được: phát hiện đúng, từ chối an toàn khi dữ liệu lỗi và khôi phục được khi quyết định sai.
12. Lộ trình triển khai phù hợp cho người mới
- Giai đoạn 1 — bằng chứng: nhận một nguồn, lưu raw payload bất biến, tạo hash và correlation ID.
- Giai đoạn 2 — chuẩn hóa: hỗ trợ ecosystem, package, phiên bản, trạng thái và provenance; chưa tự động chặn.
- Giai đoạn 3 — đối chiếu: kết nối SBOM, lockfile hoặc dependency graph để biết package có thực sự xuất hiện trong hệ thống không.
- Giai đoạn 4 — quyết định: phát hành
monitor,investigatevàblocktheo policy có version. - Giai đoạn 5 — rollback: thêm snapshot, correction event, phê duyệt và kiểm thử phục hồi.
- Giai đoạn 6 — mở rộng: thêm GitHub, OpenSSF, registry hoặc nguồn nội bộ; mỗi nguồn có adapter và bộ fixture riêng.
Cách triển khai này chậm hơn việc viết một webhook ghi thẳng vào danh sách block, nhưng giảm nguy cơ mất dấu vết và cho phép sửa parser, policy hoặc nguồn dữ liệu mà không phải xây lại toàn bộ hệ thống.
13. Checklist hoàn thành
- Raw payload được lưu ở vùng chỉ ghi thêm, kèm hash, thời điểm nhận và trạng thái xác minh.
- Mỗi event có idempotency key, correlation ID và phiên bản parser/rule.
- Mô hình dữ liệu phân biệt package version, advisory revision và decision version.
- Schema, semantic, provenance và change validation đều có trạng thái lỗi rõ ràng.
- Cảnh báo được đối chiếu với SBOM, lockfile, dependency graph hoặc inventory artifact.
- Policy có các trạng thái quarantine, monitor, investigate, block và withdrawn phù hợp.
- Rollback dùng snapshot hoặc correction event, không xóa evidence.
- Retry có giới hạn, có dead-letter path và có owner xử lý.
- Đã kiểm thử duplicate event, advisory correction, parser failure, version range và rollback.
- Đã tách xử lý rollback dữ liệu khỏi ứng phó sự cố credential hoặc máy đã thực thi package.
Kết luận
Một pipeline cảnh báo package độc hại có thể phát hiện và hoàn tác là hệ thống quản lý bằng chứng và quyết định, không phải bộ chuyển tiếp thông báo. Thiết kế tối thiểu cần có raw evidence bất biến, schema chuẩn hóa, provenance, định danh package rõ ràng, đối chiếu inventory, policy có phiên bản, snapshot và audit trail.
Nguyên tắc quan trọng nhất là: không xóa kết luận cũ để sửa kết luận mới. Hãy phát hành trạng thái mới có liên kết đến trạng thái trước, kiểm tra phạm vi ảnh hưởng rồi mới thay đổi hành động mà các hệ thống khác đang áp dụng.

