Chạy CodeQL trong cơ sở dữ liệu
Với mã được trích xuất vào cơ sở dữ liệu, giờ đây bạn có thể phân tích mã bằng cách sử dụng truy vấn CodeQL. Các chuyên gia GitHub, nhà nghiên cứu bảo mật và những người đóng góp cho cộng đồng viết và duy trì các truy vấn CodeQL mặc định. Bạn cũng có thể viết truy vấn của riêng mình.
Bạn có thể sử dụng các truy vấn CodeQL trong phân tích quét mã để tìm các vấn đề trong mã nguồn của bạn và tìm các lỗ hổng bảo mật tiềm ẩn. Bạn cũng có thể viết truy vấn tùy chỉnh để xác định sự cố cho từng ngôn ngữ mà bạn đang sử dụng trong mã nguồn của mình.
Có hai loại truy vấn quan trọng:
- báo các truy vấn sáng các vấn đề ở vị trí cụ thể của mã của bạn.
- truy vấn Path mô tả dòng thông tin giữa nguồn và vùng chìm trong mã của bạn.
Truy vấn CodeQL đơn giản
Cấu trúc truy vấn CodeQL cơ bản có phần mở rộng .ql tệp và chứa một mệnh đề select cơ bản. Đây là cấu trúc truy vấn ví dụ:
/**
*
* Query metadata
*
*/
import /* ... CodeQL libraries or modules ... */
/* ... Optional, define CodeQL classes and predicates ... */
from /* ... variable declarations ... /
where / ... logical formula ... /
select / ... expressions ... */
Siêu dữ liệu truy vấn
Việc sử dụng CodeQL với quét mã sẽ chuyển đổi kết quả theo cách làm nổi bật các vấn đề tiềm ẩn mà truy vấn được thiết kế để tìm. Truy vấn chứa thuộc tính siêu dữ liệu cho biết kết quả sẽ được diễn giải như thế nào. Sử dụng siêu dữ liệu truy vấn để:
- Xác định truy vấn tùy chỉnh của bạn khi bạn thêm truy vấn vào kho GitHub của mình.
- Cung cấp thông tin về mục đích của truy vấn.
Thông tin siêu dữ liệu có thể bao gồm mô tả về truy vấn, ID duy nhất và loại sự cố đó là (cảnh báo hoặc đường dẫn). Siêu dữ liệu cũng xác định cách diễn giải và hiển thị kết quả truy vấn.
GitHub có hướng dẫn kiểu được đề xuất cho siêu dữ liệu truy vấn. Bạn có thể tìm thấy nó trong tài CodeQL của.
Ví dụ này hiển thị siêu dữ liệu cho một trong các truy vấn Java chuẩn:
CodeQL không diễn giải các truy vấn không có siêu dữ liệu. Nó hiển thị những kết quả đó dưới dạng bảng và không hiển thị chúng trong mã nguồn.
Cú pháp QL
QL là một ngôn ngữ truy vấn khai báo, theo định hướng đối tượng. Nó được tối ưu hóa để cho phép phân tích hiệu quả các cấu trúc dữ liệu phân cấp và đặc biệt là các cơ sở dữ liệu đại diện cho các thành phần lạ của phần mềm.
Cú pháp của QL tương tự như SQL, nhưng ngữ pháp của QL dựa trên Datalog. Datalog là một ngôn ngữ lập trình lô-gic khai báo, thường được sử dụng làm ngôn ngữ truy vấn. Vì QL chủ yếu là một ngôn ngữ lô-gic, tất cả các thao tác trong QL đều là các thao tác lô-gic. QL cũng kế thừa các tính từ đệ trình từ Datalog. QL thêm hỗ trợ cho tập hợp để làm cho các truy vấn thậm chí phức tạp ngắn gọn và đơn giản.
Ngôn ngữ QL bao gồm các công thức lô-gic. Hàm sử dụng các kết nối lô-gic phổ biến như and, orvà not, cùng với các định lượng như forall và exists. Vì QL kế thừa các vị từ đệ quy, bạn cũng có thể viết các truy vấn đệ quy phức tạp bằng cách sử dụng cú pháp QL cơ countbản và các tập hợp như , sumvà average.
Để biết thêm thông tin về ngôn ngữ QL, hãy xem hướng dẫn CodeQL.
Truy vấn đường dẫn
Cách thông tin di chuyển qua một chương trình rất quan trọng. Dữ liệu có vẻ lành tính có thể chảy theo những cách không mong muốn cho phép sử dụng dữ liệu một cách độc hại.
Tạo truy vấn đường dẫn có thể giúp bạn trực quan hóa dòng thông tin thông qua cơ sở mã. Truy vấn có thể theo dõi đường dẫn mà dữ liệu đưa từ các điểm bắt đầu có thể xảy ra (source) đến các điểm cuối khả thi (sink). Để lập mô hình đường dẫn, truy vấn của bạn phải cung cấp thông tin về nguồn, bồn rửa và các bước dòng dữ liệu liên kết chúng.
Cách dễ nhất để bắt đầu viết truy vấn đường dẫn của riêng bạn là sử dụng một trong các truy vấn hiện có làm mẫu. Để nhận các truy vấn này cho các ngôn ngữ được hỗ trợ, hãy xem hướng dẫn sử dụng CodeQL.
Truy vấn đường dẫn của bạn yêu cầu một số siêu dữ liệu, vị từ truy vấn và select cấu trúc câu lệnh. Nhiều truy vấn đường dẫn dựng sẵn trong CodeQL tuân theo cấu trúc cơ bản. Cấu trúc phụ thuộc vào cách CodeQL làm mô hình ngôn ngữ mà bạn đang phân tích.
Đây là mẫu ví dụ cho truy vấn đường dẫn:
/**
* ...
* @kind path-problem
* ...
*/
import <language>
// For some languages (Java/C++/Python/Swift), you need to explicitly import the data-flow library, such as
// import semmle.code.java.dataflow.DataFlow or import codeql.swift.dataflow.DataFlow
...
module Flow = DataFlow::Global<MyConfiguration>;
import Flow::PathGraph
from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink.getNode(), source, sink, "<message>"
Trong mẫu đó:
-
MyConfigurationlà một mô-đun chứa các vị từ xác định cách dữ liệu di chuyển giữasourcevàsink. -
Flowlà kết quả của tính toán dòng dữ liệu dựa trênMyConfiguration. -
Flow::Pathgraphlà mô-đun đồ thị dòng dữ liệu thu được mà bạn cần nhập để bao gồm giải thích đường dẫn trong truy vấn. -
sourcevàsinklà các nút trong đồ thị như được xác định trong cấu hình vàFlow::PathNodelà loại của chúng. -
DataFlow::Global<..>là gọi dòng dữ liệu. Thay vào đó,TaintTracking::Global<..>bạn có thể sử dụng để đưa vào một tập hợp các bước đơn giản mặc định.
Cách viết truy vấn đường dẫn
Truy vấn của bạn cần tính toán đồ thị đường dẫn để tạo giải thích đường dẫn. Để làm như vậy, hãy xác định một vị từ truy vấn được gọi là edges. Vị từ truy vấn là một vị từ không phải của đối tượng với một query chú thích khác. Chú giải truy vấn trả về tất cả các bộ mà vị từ định trị.
Vị edges xác định mối quan hệ cạnh của đồ thị mà bạn đang tính toán. Nó được sử dụng để tính toán đường dẫn liên quan đến mỗi kết quả mà truy vấn của bạn tạo ra. Bạn cũng có thể nhập xác định edges xác định trước từ mô-đun biểu đồ đường dẫn ở một trong các thư viện dòng dữ liệu chuẩn.
Các thư viện dòng dữ liệu chứa các lớp, xác lập và mô-đun khác thường được sử dụng trong phân tích dòng dữ liệu, ngoài mô-đun biểu đồ đường dẫn. Các thư viện dòng dữ liệu CodeQL hoạt động bằng cách mô hình hóa đồ thị dòng dữ liệu hoặc thực hiện phân tích dòng dữ liệu. Thư viện dòng dữ liệu thông thường được sử dụng để phân tích dòng thông tin trong đó giá trị dữ liệu được giữ nguyên ở mỗi bước.
Đây là một câu lệnh ví dụ nhập mô-đun pathgraph từ thư viện dòng dữ liệu (DataFlow.qll), trong đó edges được xác định:
import DataFlow::PathGraph
Bạn có thể nhập nhiều thư viện khác có trong CodeQL. Bạn cũng có thể nhập các thư viện được thiết kế đặc biệt để thực hiện phân tích dòng dữ liệu trong các khuôn khổ và môi trường chung khác nhau.
Lớp này được PathNode thiết kế để thực hiện phân tích dòng dữ liệu. Nó Node được tăng cường với ngữ cảnh cuộc gọi (ngoại trừ bồn rửa), đường dẫn truy cập và cấu hình. Chỉ PathNode giá trị có thể tiếp cận từ nguồn được tạo ra.
Đây là ví dụ về đường dẫn nhập:
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl
Bạn có thể tùy ý xác nodes vị từ truy vấn, xác định các nút của đồ thị đường dẫn cho tất cả các ngôn ngữ. Khi bạn xác định nodes, các nút đã chọn chỉ xác định các cạnh với điểm cuối. Khi bạn không xác định được nodes, bạn cần chọn tất cả các điểm cuối có thể có của edges.
Phân tích cơ sở dữ liệu
Khi bạn sử dụng truy vấn để phân tích cơ sở dữ liệu CodeQL, bạn sẽ nhận được kết quả có ý nghĩa trong ngữ cảnh của mã nguồn. Kết quả được tạo kiểu như cảnh báo hoặc đường dẫn trong SARIF hoặc định dạng diễn giải khác.
Đây là ví dụ về lệnh cơ sở dữ liệu CodeQL phân tích cơ sở dữ liệu bằng cách chạy các truy vấn đã chọn đối với lệnh đó và diễn giải kết quả:
codeql database analyze --format=<format> ---output=<output> [--threads=<num>] [--ram=<MB>] <options>... -- <database> <query|dir|suite>...
Lệnh này kết hợp hiệu ứng của các lệnh codeql database run-queries và codeql database interpret-results dẫn nước.
Ngoài ra, bạn có thể chạy các truy vấn không đáp ứng các yêu cầu để được hiểu là cảnh báo mã nguồn. Để làm như vậy, hãy sử dụng codeql-database run-queries hoặc codeql query run. Sau đó, codeql bqrs decode để chuyển đổi kết quả thô thành ký hiệu có thể đọc được.
Bạn có thể nhận được một danh sách đầy đủ các lệnh CodeQL CLI có sẵn trong hướng dẫn sử dụng CodeQL CLI.
Sử dụng tệp SARIF với các danh mục
CodeQL hỗ trợ SARIF để chia sẻ kết quả phân tích tĩnh. SARIF được thiết kế để đại diện cho đầu ra của một loạt các công cụ phân tích tĩnh.
Bạn cần xác định danh mục khi sử dụng đầu ra SARIF để phân tích CodeQL. Thể loại có thể phân biệt nhiều phân tích được thực hiện trên cùng một kho chứa cam kết và trên các ngôn ngữ khác nhau hoặc các phần khác nhau của mã. Tuy nhiên, các tệp SARIF có cùng danh mục sẽ ghi đè lẫn nhau.
Bạn có thể quét từng tệp đầu ra SARIF bằng cách sử dụng CodeQL để phân tích các ngôn ngữ khác nhau trong cùng một cơ sở mã khi giá trị thể loại nhất quán giữa các lần chạy phân tích. Chúng tôi khuyên bạn nên sử dụng ngôn ngữ được quét làm mã định danh cho thể loại.
Dưới đây là một ví dụ. Giá trị thể loại xuất hiện (với dấu gạch chéo ở sau được gắn thêm nếu chưa có) dưới dạng thuộc tính <run>.automationId trong SARIF v1, thuộc tính <run>.automationLogicalId trong SARIF v2 và thuộc tính <run>.automationDetails.id trong SARIF v2.1.0.
Đăng kết quả SARIF lên GitHub
Sau khi cơ sở dữ liệu đã sẵn sàng, bạn có thể truy vấn nó theo cách tương tác. Hoặc bạn có thể chạy một bộ truy vấn để tạo một tập hợp các kết quả ở định dạng SARIF và tải kết quả lên kho mục tiêu trên GitHub.com:
codeql github upload-results --sarif=<file> [--github-auth-stdin] [--github-url=<url>] [--repository=<repository-name>] [--ref=<ref>] [--commit=<commit>] [--checkout-path=<path>] <options>...
Để tải kết quả lên GitHub, hãy đảm bảo rằng mỗi máy chủ tích hợp liên tục (CI) có một Ứng dụng GitHub hoặc mã thông báo truy cập cá nhân cho CodeQL CLI để sử dụng. Bạn phải sử dụng mã thông báo truy nhập hoặc Ứng dụng GitHub với quyền security_events ghi.
Bạn có thể cho phép CodeQL CLI sử dụng cùng một mã thông báo nếu máy chủ CI đã sử dụng mã thông báo với phạm vi này để kiểm xuất các kho lưu trữ từ GitHub. Nếu không, hãy tạo mã thông báo mới security_events quyền ghi và thêm mã thông báo này vào kho lưu trữ bí mật của hệ thống CI. Cách tốt nhất để bảo mật là đặt cờ --github-auth-stdin và truyền mã thông báo cho lệnh thông qua đầu vào tiêu chuẩn.
Tải lên kết quả SARIF
Để quét mã để hiển thị kết quả từ một công cụ phân tích tĩnh không phải của Microsoft trong kho GitHub, kết quả của bạn phải được lưu trữ trong tệp SARIF hỗ trợ một tập con cụ thể của sơ đồ JSON SARIF 2.1.0. Bạn có thể tải lên kết quả bằng cách sử dụng API quét mã hoặc CodeQL CLI.
Mỗi lần bạn tải lên kết quả quét mã mới, CodeQL sẽ xử lý kết quả và thêm cảnh báo vào kho. Để ngăn chặn các cảnh báo trùng lặp cho cùng một vấn đề, quét mã sử dụng thuộc tính SARIF partialFingerprints để phù hợp với kết quả trên chạy khác nhau để chúng chỉ xuất hiện một lần trong lần chạy mới nhất cho nhánh được chọn. Việc loại bỏ các mục trùng lặp giúp bạn có thể khớp cảnh báo với đúng dòng mã khi tệp được chỉnh sửa.
ID quy tắc cho kết quả phải giống nhau qua các phân tích. Dữ liệu vân tay được tự động bao gồm trong các tệp SARIF được tạo thông qua dòng công việc phân tích CodeQL hoặc trình chạy CodeQL.
Thông số kỹ thuật của SARIF sử dụng tên thuộc tính JSON partialFingerprints, một từ điển từ các loại dấu vân tay được đặt tên đến dấu vân tay. Thuộc tính này chứa giá trị tối thiểu cho primaryLocationLineHash, cung cấp dấu vân tay dựa trên ngữ cảnh của vị trí chính.
GitHub cố gắng điền trường partialFingerprints từ tệp nguồn nếu bạn tải lên tệp SARIF bằng cách sử dụng hành động upload-sarif và thiếu dữ liệu này. Ngoài ra, nếu bạn tải lên một tệp SARIF mà không có dữ liệu vân tay bằng cách sử dụng điểm cuối API /code-scanning/sarifs, người dùng có thể thấy các cảnh báo trùng lặp khi các cảnh báo quét mã được xử lý và hiển thị.
Để tránh thấy các cảnh báo trùng lặp trong khi bạn đang làm việc với các công cụ phân tích tĩnh, hãy tính toán dữ liệu vân tay và điền thuộc tính partialFingerprints trước khi tải lên tệp SARIF. Điểm khởi đầu hữu ích là sử dụng cùng một tập lệnh như hành upload-sarif của bạn.