Advanced SAST C/C++ 구성
GitLab v19.5Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
요약
프로젝트에서 GitLab Advanced SAST C/C++ 분석을 켜려면 다음을 수행합니다. 상단 바에서 Search or go to를 선택하고 프로젝트를 찾습니다. Build > Pipeline editor로 이동합니다.
GitLab Advanced SAST C/C++ 분석 켜기#
사전 요구 사항:
프로젝트에서 GitLab Advanced SAST C/C++ 분석을 켜려면 다음을 수행합니다.
-
상단 바에서 Search or go to를 선택하고 프로젝트를 찾습니다.
-
Build > Pipeline editor로 이동합니다.
-
SAST job 설정에 C/C++ 분석을 추가합니다. 여기의 안내는 컴파일 데이터베이스 이름이
compile_commands.json이라고 가정합니다.-
CI/CD 템플릿을 사용한다면
include:구문 뒤에 다음 CI/CD 변수를 설정에 추가합니다.include: - template: Jobs/SAST.gitlab-ci.yml variables: GITLAB_ADVANCED_SAST_CPP_ENABLED: "true" SAST_COMPILATION_DATABASE: "compile_commands.json" -
CI/CD 컴포넌트를 사용한다면 다음 입력 파라미터와 CI/CD 변수를 설정에 추가합니다.
include: - component: gitlab.com/components/sast/sast@main inputs: run_advanced_sast_cpp: "true" variables: SAST_COMPILATION_DATABASE: "compile_commands.json"
-
-
Validate 탭을 선택한 다음 Validate pipeline을 선택합니다.
Simulation completed successfully 메시지가 나오면 파일이 유효한 것입니다.
-
Edit 탭을 선택합니다.
-
다음 항목을 입력합니다.
- 커밋 메시지입니다.
- 브랜치입니다. 예를 들면
add-sast입니다.
-
Start a new merge request with these changes 체크박스를 선택한 다음 Commit changes를 선택합니다.
머지 리퀘스트 페이지가 열립니다.
-
평소 워크플로에 맞게 항목을 입력한 다음 Create merge request를 선택합니다.
-
평소 워크플로에 맞게 머지 리퀘스트를 검토하고 수정한 다음 Merge를 선택합니다.
컴파일 데이터베이스#
GitLab Advanced SAST CPP 분석기는 소스 파일을 정확히 파싱하고 분석하기 위해 컴파일 데이터베이스(CDB)를 필요로 합니다.
CDB는 번역 단위마다 항목 하나를 담은 JSON 파일(compile_commands.json)입니다.
각 항목에는 보통 다음이 들어 있습니다.
- 파일을 빌드할 때 사용한 컴파일러 명령
- 컴파일러 플래그와 include 경로
- 컴파일을 수행한 작업 디렉터리
CDB가 있으면 분석기가 빌드 환경을 그대로 재현할 수 있어 파싱과 의미 분석이 정확해집니다.
GitLab Advanced SAST CPP를 사용하려면 CDB를 만들어 분석기에 제공해야 합니다.
CDB 생성#
CDB를 생성하는 방법은 빌드 시스템에 따라 다릅니다. 아래는 흔히 쓰는 예시입니다.
예시: CMake#
CMake는 cmake 호출에 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 파라미터를 추가하면 CDB를 바로 생성할 수 있습니다.
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
이 옵션은 프로젝트를 구성하고 build 폴더에 compile_commands.json 파일을 생성하며, 이 파일에는 소스 파일별 컴파일러 명령이 기록됩니다.
GitLab Advanced SAST CPP 분석기는 이 파일로 빌드 환경을 정확히 재현합니다.
다음 빌드 job 예시처럼 생성된 compile_commands.json 파일을
job 아티팩트로 내보냅니다.
:
image: ubuntu:24.04
before_script:
- apt update -qq && apt install -y -qq cmake build-essential
script:
- mkdir -p build
- cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- make -j$(nproc)
artifacts:
paths:
- build/compile_commands.json # Pass the CDB file to the gitlab-advanced-sast-cpp job
여러 빌드 시스템 예시#
여러 빌드 시스템에서 CDB를 생성하고 GitLab Advanced SAST CPP를 실행하는 전체 예시도 확인할 수 있습니다.
분석기에 CDB 제공#
SAST_COMPILATION_DATABASE 변수로 GitLab Advanced SAST CPP 분석기에 CDB 위치를 알려 줍니다.
variables:
SAST_COMPILATION_DATABASE: YOUR_COMPILATION_DATABASE.json
SAST_COMPILATION_DATABASE를 지정하지 않으면 GitLab Advanced SAST CPP 분석기는 프로젝트 루트 디렉터리의 compile_commands.json 파일을 기본값으로 사용합니다.
compile_commands.json 파일을 생성하는 빌드 job은 gitlab-advanced-sast-cpp
job 이 사용할 수 있도록 그 파일을 job 아티팩트로
내보내야 합니다.
variables:
SAST_COMPILATION_DATABASE: build/compile_commands.json
:
image: ubuntu:24.04
before_script:
- apt update -qq && apt install -y -qq cmake build-essential
script:
- mkdir -p build
- cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- make -j$(nproc)
artifacts:
paths:
- build/compile_commands.json # Pass the CDB file to the gitlab-advanced-sast-cpp job
또는 CDB 캐싱을 참고합니다.
분석 실행 시간 최적화#
C/C++ 코드의 분석 실행 시간을 줄이려면 CDB를 여러 조각으로 나눠 분석을 병렬로 실행할 수 있습니다.
GitLab Advanced SAST CPP 리포지터리는 CDB를 나누고 병렬 분석을 실행하는 헬퍼 스크립트를 제공합니다.
C/C++ 코드 분석을 병렬로 실행하려면 다음을 수행합니다.
-
스크립트를 포함합니다.
include: - project: "gitlab-org/security-products/demos/sast/gitlab-advanced-sast-cpp-templates" file: "templates/scripts.yml" -
빌드 job에서 헬퍼 스크립트를 참조하고 CDB를 나눕니다.
: script: - <your-script to generate the CDB> - !reference [.gitlab-advanced-sast-cpp-scripts] - split_cdb "${BUILD_DIR}" 1 4 # Split into 4 fragments artifacts: paths: - ${BUILD_DIR} # Pass the split CDB files to the parallelized gitlab-advanced-sast-cpp jobs[!note]
split_cdb는${BUILD_DIR}/compile_commands.json을 읽도록 고정되어 있습니다.split_cdb를 호출하기 전에 빌드가 바로 이 위치에 CDB를 생성해야 합니다. -
병렬 분석기 job을 실행합니다.
gitlab-advanced-sast-cpp: parallel: 4 variables: SAST_COMPILATION_DATABASE: "${BUILD_DIR}/compile_commands${CI_NODE_INDEX}.json" needs: - job: artifacts: trueparallel: 4는 실행을 4개 job으로 분산합니다.${CI_NODE_INDEX}(1, 2, 3, 4)는 해당하는 CDB 조각을 선택합니다.needs는 분석기 job 이 빌드 job 이 만든 아티팩트를 받도록 보장합니다.
이렇게 설정하면 빌드 job 이 compile_commands.json 하나를 만듭니다.
split_cdb 스크립트가 여러 파티션을 만들고, 분석기 job 들이 파티션을 하나씩 맡아 병렬로 실행합니다.
룰셋 설정#
GitLab Advanced SAST CPP는 사용자 지정 룰셋을 지원하며, 여기서 "규칙"은 GitLab Advanced SAST CPP 체커를 뜻합니다.
사용자 지정 룰셋은
CodeChecker 설정 파일로 구성한
패스스루로
만들 수 있습니다.
패스스루 설정은 다음과 같이 처리됩니다.
targetDir과target은 무시됩니다. 패스스루를 처리한 뒤 만들어진 플래그는CodeChecker에 그대로 전달됩니다.overwrite모드는 설정 전체를 대체하고,append모드는 플래그를 덧붙입니다.- 분석기 플래그
-o,--output과 파싱 플래그-o, --output, -e, --export를 비롯한 일부CodeChecker플래그는 사용자 지정할 수 없습니다. server와store설정 항목은 무시됩니다.
다음 예시는 서로 다른 소스의 패스스루가 어떻게 결합되는지 보여 줍니다.
.gitlab/sast-ruleset.toml 파일:
[gitlab-advanced-sast-cpp]
description = "My ruleset"
[[gitlab-advanced-sast-cpp.passthrough]]
# replace the GitLab default configuration with my own
mode = "overwrite"
type = "url"
value = "https://example.com/gitlab-advanced-sast-cpp.yaml"
[[gitlab-advanced-sast-cpp.passthrough]]
# append flags from a file in the current repository
mode = "append"
type = "file"
value = "gitlab-advanced-sast-cpp.yml"
https://example.com/gitlab-advanced-sast-cpp.yaml의 원격 설정:
analyzer:
- --disable-all
- --enable=core.DivideZero
gitlab-advanced-sast-cpp.yml의 로컬 설정:
analyzer:
- --enable=core.CallAndMessage
결합된 최종 설정은 다음과 같습니다.
analyzer:
- --disable-all
- --enable=core.DivideZero
- --enable=core.CallAndMessage
오프라인 환경#
오프라인 환경에서 GitLab Advanced SAST C/C++ 를 실행하려면 스캔을 실행하기 전에 clangsa
분석기 이미지를 로컬 레지스트리에 미러링합니다.
표준 gitlab-advanced-sast 이미지에는 다른 언어에 사용하는
clangsa 이미지가 들어 있지 않습니다.
이 이미지는 별도로 미러링해야 합니다.
clangsa 이미지를 미러링하려면
Secure-Binaries.gitlab-ci.yml 템플릿을 사용합니다.
이 템플릿에는 clangsa 이미지를 가져와 레지스트리에 푸시하는
gitlab-advanced-sast-cpp job 이 들어 있습니다.
오프라인 환경 구성에 관한 자세한 내용은 오프라인 환경을 참고합니다.
문제 해결#
cdb-rebase로 경로 재지정#
CDB 안의 경로가 CI/CD job의 컨테이너 경로와 맞지 않으면 cdb-rebase로 경로를 조정합니다.
설치는 다음과 같이 합니다.
go install gitlab.com/gitlab-org/secure/tools/cdb-rebase@latest
바이너리는 $GOPATH/bin 또는 $HOME/go/bin에 설치됩니다. 이 디렉터리가 PATH에 있는지 확인합니다.
사용 예시는 다음과 같습니다.
cdb-rebase compile_commands.json /host/path /container/path > rebased_compile_commands.json
CDB 수정#
빌드 환경과 스캔 환경이 다르면 생성된 CDB를 조정해야 할 수 있습니다.
jq로 수정하거나,
미리 정의된 헬퍼 스크립트의 셸 함수 cdb_append를 사용할 수 있습니다.
cdb_append는 기존 CDB에 컴파일러 옵션을 덧붙입니다.
인자는 다음과 같습니다.
- 첫 번째 인자:
compile_commands.json이 있는 폴더 - 이후 인자: 덧붙일 추가 컴파일러 옵션
CI/CD 예시는 다음과 같습니다.
include:
- project: "gitlab-org/security-products/demos/sast/gitlab-advanced-sast-cpp-templates"
file: "templates/scripts.yml"
:
script:
- !reference [.gitlab-advanced-sast-cpp-scripts]
- <your-script to generate the CDB>
- cdb_append "${BUILD_DIR}" "-I'$PWD/include-cache'" "-Wno-error=register"
CDB 캐싱#
컴파일과 분석 과정을 빠르게 하려면 CDB를 캐시할 수 있습니다.
.cdb_cache:
cache: &cdb_cache
key:
files:
- Makefile
- src/
paths:
- compile_commands.json
:
script:
- <your-script to generate the CDB>
cache:
<<: *cdb_cache
policy: pull-push
gitlab-advanced-sast-cpp:
cache:
<<: *cdb_cache
policy: pull
전체 예시는 데모 프로젝트 cached-cdb에서 확인합니다.
CDB의 절대 경로 처리#
데모 프로젝트에서는 Docker job의 Builds 디렉터리에서 bear를 실행합니다.
CDB 경로는 /builds/$CI_PROJECT_PATH를 기준으로 하는 절대 경로입니다.
분석기 job gitlab-advanced-sast-cpp도 같은 위치에서 실행되므로 경로가 맞습니다.
분석 중에 사용할 수 없는 경로에서 CDB를 생성했다면 경로를 재지정해야 합니다.
분석기 이미지에 포함된 cdb-rebase 도구는 directory, file, output 경로를 다시 씁니다.
예시는 다음과 같습니다.
gitlab-advanced-sast-cpp:
before-script:
# Rebase the original CDB to be relative to the current directory.
#
# ORIGINAL_CDB_PATH - Path to the CDB artifact from a previous job (e.g., artifacts/compile_commands.json)
# ORIGINAL_CDB_BASEPATH - The absolute path to the project root when the ORIGINAL_CDB_PATH was generated.
# (e.g., /mnt/custom_build_area/my-project or /home/user/my-project)
- /cdb-rebase --input "$ORIGINAL_CDB_PATH" \
--output compile_commands.json \
--src "$ORIGINAL_CDB_BASEPATH" \
--dst .
전체 시연은 cdb-rebase-demo에서 확인합니다.
cdb-rebase는 단순한 경로 재지정 외에 빌드 환경과 스캔 환경 사이의 include 파일도 관리할 수 있습니다.
- 외부 헤더 캐싱:
--include-cache를 사용하면 소스 트리 밖의 헤더를 이식 가능한 캐시로 복사합니다. - include 경로 추가:
--include로 캐시할 추가 include 디렉터리를 지정합니다. - 파일 제외:
--exclude로 가져오지 않을 헤더를 건너뜁니다.
예시는 다음과 같습니다.
/cdb-rebase --src /my-project \
--dst /scan-env \
--input build/compile_commands.json \
--output rebased_cdb.json \
--include-cache include-cache \
--include third_party/include \
--exclude dummy.h
cdb-rebase 도구는 Go가 설치된 환경에서도 사용할 수 있으므로, CDB를 생성할 때 바로 경로를 재지정할 수 있습니다. 예를 들면 다음과 같습니다.
go install gitlab.com/gitlab-org/security-products/analyzers/clangsa/cmd/cdb-rebase@latest
bear -o compile_commands_abs.json -- make
cdb-rebase -i compile_commands_abs.json -o compile_commands.json -s "$PWD" -d .
위의 go install 명령은 cdb-rebase를 GOBIN 경로에 설치하며, 이 경로는 go env GOBIN으로 확인할 수 있습니다.
헤더 파일 누락으로 인한 부분 스캔 커버리지#
필요한 시스템 헤더나 서드파티 헤더 파일이 스캔 job에 없으면 스캔 커버리지가 일부에 그칠 수 있습니다. 빌드 job에서 설치한 헤더는 스캔 job으로 명시적으로 전달해야 하며, 컴파일 데이터베이스에 기록된 include 경로로 찾을 수 있어야 합니다.
흔히 쓰는 방법은 필요한 헤더를 캐시하고 컴파일 데이터베이스가 그 헤더를 참조하도록 갱신하는 것입니다.
# Create and populate an include cache
mkdir -p include-cache
dpkg -L <build-dependency-packages> | sed -n 's:^/usr/include/::p' > headers.txt
rsync -a --files-from=headers.txt /usr/include/ include-cache/
# Add cached headers to compile flags
cdb_append . "-I'$PWD/include-cache'"
전체 시연은 예시에서 확인합니다.