InfoGrab DocsInfoGrab Docs

커버리지 기반 퍼즈 테스팅 (지원 중단됨)

요약

이 기능은 GitLab 18.0에서 지원 중단되었으며 19.0에서 제거될 예정입니다. 커버리지 기반 퍼즈 테스팅은 계측된 버전의 애플리케이션에 무작위 입력을 보내 예상치 못한 동작을 유발합니다. 퍼즈 테스팅은 GitLab Secure의 다른 보안 스캐너 및 자체 테스트 프로세스와 함께 사용해야 합니다.

Warning

이 기능은 GitLab 18.0에서 지원 중단되었으며 19.0에서 제거될 예정입니다. 이는 브레이킹 체인지입니다.

시작하기#

커버리지 기반 퍼즈 테스팅은 계측된 버전의 애플리케이션에 무작위 입력을 보내 예상치 못한 동작을 유발합니다. 이러한 동작은 수정해야 할 버그를 나타냅니다. GitLab은 파이프라인에 커버리지 기반 퍼즈 테스팅을 추가하는 기능을 제공합니다. 이를 통해 다른 QA 프로세스가 놓칠 수 있는 버그와 잠재적 보안 문제를 발견할 수 있습니다.

퍼즈 테스팅은 GitLab Secure의 다른 보안 스캐너 및 자체 테스트 프로세스와 함께 사용해야 합니다. GitLab CI/CD를 사용하는 경우 CI/CD 워크플로의 일부로 커버리지 기반 퍼즈 테스팅을 실행할 수 있습니다.

개요는 Coverage-guided Fuzzing - Advanced Security Testing을 참고합니다.

커버리지 기반 퍼즈 테스팅 상태 확인#

커버리지 기반 퍼즈 테스팅의 상태를 확인하려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션의 상태는 다음 중 하나입니다.
    • Not configured
    • Enabled
    • GitLab Ultimate로 업그레이드하라는 안내.

커버리지 기반 퍼즈 테스팅 활성화#

커버리지 기반 퍼즈 테스팅을 활성화하려면 .gitlab-ci.yml을 다음과 같이 편집합니다.

  1. Stage 목록에 fuzz Stage를 추가합니다.

  2. 애플리케이션이 Go로 작성되지 않았다면 해당 퍼징 엔진을 사용하는 Docker 이미지를 지정합니다. 예를 들면 다음과 같습니다.

    image: python:latest
    
  3. GitLab 설치에 포함되어 제공되는 Coverage-Fuzzing.gitlab-ci.yml 템플릿 을 포함합니다.

  4. 요구 사항에 맞게 my_fuzz_target job을 사용자 지정합니다.

커버리지 기반 퍼징 구성 예시#

stages:
  - fuzz

include:
  - template: Coverage-Fuzzing.gitlab-ci.yml

my_fuzz_target:
  extends: .fuzz_base
  script:
    # Build your fuzz target binary in these steps, then run it with gitlab-cov-fuzz
    # See our example repos for how you could do this with any of our supported languages
    - ./gitlab-cov-fuzz run --regression=$REGRESSION -- <your fuzz target>

Coverage-Fuzzing 템플릿에는 숨겨진 job .fuzz_base가 포함되어 있으며, 각 퍼징 대상마다 이를 확장해야 합니다. 각 퍼징 대상은 별도의 job을 가져야 합니다. 예를 들어 go-fuzzing-example 프로젝트 에는 단일 퍼징 대상에 대해 .fuzz_base를 확장하는 job 이 하나 있습니다.

숨겨진 job .fuzz_base는 사용자 job에서 재정의해서는 안 되는 여러 YAML 키를 사용합니다. 이러한 키를 사용자 job에 포함한다면 원래 내용을 그대로 복사해야 합니다.

  • before_script
  • artifacts
  • rules

결과 이해#

출력#

각 퍼징 단계는 다음 아티팩트를 출력합니다.

  • gl-coverage-fuzzing-report.json: 커버리지 기반 퍼즈 테스팅과 그 결과의 세부 정보가 담긴 보고서입니다.
  • artifacts.zip: 이 파일에는 두 개의 디렉터리가 들어 있습니다.
    • corpus: 현재 job과 이전 모든 job 이 생성한 테스트 케이스를 모두 포함합니다.
    • crashes: 현재 job 이 발견한 모든 크래시 이벤트와 이전 job에서 수정되지 않은 이벤트를 포함합니다.

JSON 보고서 파일은 CI/CD 파이프라인 페이지에서 내려받을 수 있습니다. 자세한 내용은 아티팩트 다운로드를 참고합니다.

코퍼스 레지스트리#

코퍼스 레지스트리는 코퍼스의 라이브러리입니다. 프로젝트 레지스트리에 있는 코퍼스는 해당 프로젝트의 모든 job에서 사용할 수 있습니다. 프로젝트 전체 레지스트리는 job 당 코퍼스 하나라는 기본 방식보다 코퍼스를 효율적으로 관리하는 방법입니다.

코퍼스 레지스트리는 패키지 레지스트리를 사용해 프로젝트의 코퍼스를 저장합니다. 레지스트리에 저장된 코퍼스는 데이터 무결성을 위해 숨겨집니다.

코퍼스를 내려받으면 처음 업로드할 때 사용한 파일 이름과 관계없이 파일 이름이 artifacts.zip 이 됩니다. 이 파일에는 코퍼스만 들어 있으며, CI/CD 파이프라인에서 내려받을 수 있는 아티팩트 파일과는 다릅니다. 또한 Reporter 이상의 권한을 가진 프로젝트 멤버는 직접 다운로드 링크로 코퍼스를 내려받을 수 있습니다.

코퍼스 레지스트리 세부 정보 보기#

코퍼스 레지스트리의 세부 정보를 보려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션에서 Manage corpus를 선택합니다.

코퍼스 레지스트리에 코퍼스 생성#

코퍼스 레지스트리에 코퍼스를 생성하는 방법은 다음 두 가지입니다.

  • 파이프라인에서 코퍼스 생성
  • 기존 코퍼스 파일 업로드
파이프라인에서 코퍼스 생성#

파이프라인에서 코퍼스를 생성하려면 다음 단계를 수행합니다.

  1. .gitlab-ci.yml 파일에서 my_fuzz_target job을 편집합니다.
  2. 다음 변수를 설정합니다.
    • COVFUZZ_USE_REGISTRY를 true로 설정합니다.
    • COVFUZZ_CORPUS_NAME에 코퍼스 이름을 설정합니다.
    • COVFUZZ_GITLAB_TOKEN을 개인 액세스 토큰 값으로 설정합니다.

my_fuzz_target job 이 실행되면 코퍼스는 COVFUZZ_CORPUS_NAME 변수에 지정한 이름으로 코퍼스 레지스트리에 저장됩니다. 코퍼스는 파이프라인이 실행될 때마다 갱신됩니다.

코퍼스 파일 업로드#

기존 코퍼스 파일을 업로드하려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션에서 Manage corpus를 선택합니다.
  4. New corpus를 선택합니다.
  5. 각 필드를 입력합니다.
  6. Upload file을 선택합니다.
  7. Add를 선택합니다.

이제 .gitlab-ci.yml 파일에서 해당 코퍼스를 참조할 수 있습니다. COVFUZZ_CORPUS_NAME 변수에 사용한 값이 업로드한 코퍼스 파일에 지정한 이름과 정확히 일치하는지 확인합니다.

코퍼스 레지스트리에 저장된 코퍼스 사용#

코퍼스 레지스트리에 저장된 코퍼스를 사용하려면 이름으로 참조해야 합니다. 해당 코퍼스의 이름은 코퍼스 레지스트리 세부 정보에서 확인합니다.

사전 요구 사항:

  1. .gitlab-ci.yml 파일에 다음 변수를 설정합니다.
    • COVFUZZ_USE_REGISTRY를 true로 설정합니다.
    • COVFUZZ_CORPUS_NAME을 코퍼스 이름으로 설정합니다.
    • COVFUZZ_GITLAB_TOKEN을 개인 액세스 토큰 값으로 설정합니다.

커버리지 기반 퍼즈 테스팅 보고서#

gl-coverage-fuzzing-report.json 파일 형식에 대한 자세한 내용은 스키마를 참고합니다.

커버리지 기반 퍼징 보고서 예시:

{
  "version": "v1.0.8",
  "regression": false,
  "exit_code": -1,
  "vulnerabilities": [
    {
      "category": "coverage_fuzzing",
      "message": "Heap-buffer-overflow\nREAD 1",
      "description": "Heap-buffer-overflow\nREAD 1",
      "severity": "Critical",
      "stacktrace_snippet": "INFO: Seed: 3415817494\nINFO: Loaded 1 modules   (7 inline 8-bit counters): 7 [0x10eee2470, 0x10eee2477), \nINFO: Loaded 1 PC tables (7 PCs): 7 [0x10eee2478,0x10eee24e8), \nINFO:        5 files found in corpus\nINFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes\nINFO: seed corpus: files: 5 min: 1b max: 4b total: 14b rss: 26Mb\n#6\tINITED cov: 7 ft: 7 corp: 5/14b exec/s: 0 rss: 26Mb\n=================================================================\n==43405==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000001573 at pc 0x00010eea205a bp 0x7ffee0d5e090 sp 0x7ffee0d5e088\nREAD of size 1 at 0x602000001573 thread T0\n    #0 0x10eea2059 in FuzzMe(unsigned char const*, unsigned long) fuzz_me.cc:9\n    #1 0x10eea20ba in LLVMFuzzerTestOneInput fuzz_me.cc:13\n    #2 0x10eebe020 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:556\n    #3 0x10eebd765 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470\n    #4 0x10eebf966 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:698\n    #5 0x10eec0665 in fuzzer::Fuzzer::Loop(std::__1::vector\u003cfuzzer::SizedFile, fuzzer::fuzzer_allocator\u003cfuzzer::SizedFile\u003e \u003e\u0026) FuzzerLoop.cpp:830\n    #6 0x10eead0cd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829\n    #7 0x10eedaf82 in main FuzzerMain.cpp:19\n    #8 0x7fff684fecc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)\n\n0x602000001573 is located 0 bytes to the right of 3-byte region [0x602000001570,0x602000001573)\nallocated by thread T0 here:\n    #0 0x10ef92cfd in wrap__Znam+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x50cfd)\n    #1 0x10eebdf31 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:541\n    #2 0x10eebd765 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470\n    #3 0x10eebf966 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:698\n    #4 0x10eec0665 in fuzzer::Fuzzer::Loop(std::__1::vector\u003cfuzzer::SizedFile, fuzzer::fuzzer_allocator\u003cfuzzer::SizedFile\u003e \u003e\u0026) FuzzerLoop.cpp:830\n    #5 0x10eead0cd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829\n    #6 0x10eedaf82 in main FuzzerMain.cpp:19\n    #7 0x7fff684fecc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)\n\nSUMMARY: AddressSanitizer: heap-buffer-overflow fuzz_me.cc:9 in FuzzMe(unsigned char const*, unsigned long)\nShadow bytes around the buggy address:\n  0x1c0400000250: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000260: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000270: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000280: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000290: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n=\u003e0x1c04000002a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa[03]fa\n  0x1c04000002b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\nShadow byte legend (one shadow byte represents 8 application bytes):\n  Addressable:           00\n  Partially addressable: 01 02 03 04 05 06 07 \n  Heap left redzone:       fa\n  Freed heap region:       fd\n  Stack left redzone:      f1\n  Stack mid redzone:       f2\n  Stack right redzone:     f3\n  Stack after return:      f5\n  Stack use after scope:   f8\n  Global redzone:          f9\n  Global init order:       f6\n  Poisoned by user:        f7\n  Container overflow:      fc\n  Array cookie:            ac\n  Intra object redzone:    bb\n  ASan internal:           fe\n  Left alloca redzone:     ca\n  Right alloca redzone:    cb\n  Shadow gap:              cc\n==43405==ABORTING\nMS: 1 EraseBytes-; base unit: de3a753d4f1def197604865d76dba888d6aefc71\n0x46,0x55,0x5a,\nFUZ\nartifact_prefix='./crashes/'; Test unit written to ./crashes/crash-0eb8e4ed029b774d80f2b66408203801cb982a60\nBase64: RlVa\nstat::number_of_executed_units: 122\nstat::average_exec_per_sec:     0\nstat::new_units_added:          0\nstat::slowest_unit_time_sec:    0\nstat::peak_rss_mb:              28",
      "scanner": {
        "id": "libFuzzer",
        "name": "libFuzzer"
      },
      "location": {
        "crash_address": "0x602000001573",
        "crash_state": "FuzzMe\nstart\nstart+0x0\n\n",
        "crash_type": "Heap-buffer-overflow\nREAD 1"
      },
      "tool": "libFuzzer"
    }
  ]
}

취약점 처리#

취약점이 발견되면 해당 취약점을 처리할 수 있습니다. 머지 리퀘스트의 Reports 탭에 취약점이 표시되고 퍼징 아티팩트를 내려받는 버튼이 함께 제공됩니다. 탐지된 취약점 중 하나를 선택하면 세부 정보를 확인할 수 있습니다.

Security Dashboard에서도 취약점을 확인할 수 있으며, 여기에서 그룹·프로젝트·파이프라인의 모든 보안 취약점을 한눈에 볼 수 있습니다.

취약점을 선택하면 해당 취약점에 대한 추가 정보를 제공하는 모달이 열립니다.

  • Status: 취약점의 상태입니다. 다른 유형의 취약점과 마찬가지로 커버리지 퍼징 취약점도 Detected, Confirmed, Dismissed, Resolved 중 하나가 됩니다.
  • Project: 취약점이 존재하는 프로젝트입니다.
  • Crash type: 코드에서 발생한 크래시 또는 결함의 유형입니다. 일반적으로 CWE에 대응됩니다.
  • Crash state: 스택 트레이스를 정규화한 형태로, 크래시 직전 세 개의 함수를 포함합니다 (무작위 주소는 제외됩니다).
  • Stack trace snippet: 스택 트레이스의 마지막 몇 줄로, 크래시의 세부 정보를 보여 줍니다.
  • Identifier: 취약점의 식별자입니다. CVE 또는 CWE에 대응됩니다.
  • Severity: 취약점의 심각도입니다. Critical, High, Medium, Low, Info, Unknown 중 하나입니다.
  • Scanner: 취약점을 탐지한 스캐너입니다(예: Coverage Fuzzing).
  • Scanner Provider: 스캔을 수행한 엔진입니다. Coverage Fuzzing의 경우 지원되는 퍼징 엔진 및 언어에 나열된 엔진 중 하나입니다.

최적화#

다음 사용자 지정 옵션을 사용해 프로젝트에 맞게 커버리지 기반 퍼즈 테스팅을 최적화합니다.

사용 가능한 CI/CD 변수#

CI/CD 파이프라인에서 커버리지 기반 퍼즈 테스팅을 구성할 때 다음 변수를 사용합니다.

Warning

GitLab 보안 스캐닝 도구의 사용자 지정은 기본 브랜치에 변경 사항을 병합하기 전에 머지 리퀘스트에서 먼저 테스트해야 합니다. 그렇지 않으면 다수의 오탐을 포함한 예상치 못한 결과가 발생할 수 있습니다.

CI/CD 변수 설명
COVFUZZ_ADDITIONAL_ARGS gitlab-cov-fuzz에 전달되는 인수입니다. 기반 퍼징 엔진의 동작을 사용자 지정하는 데 사용합니다. 전체 인수 목록은 퍼징 엔진의 문서를 참고합니다.
COVFUZZ_BRANCH 장기 실행 퍼징 job을 실행할 브랜치입니다. 다른 모든 브랜치에서는 퍼징 회귀 테스트만 실행됩니다. 기본값: 리포지터리의 기본 브랜치.
COVFUZZ_SEED_CORPUS 시드 코퍼스 디렉터리의 경로입니다. 기본값: 비어 있음.
COVFUZZ_URL_PREFIX 오프라인 환경에서 사용하기 위해 클론한 gitlab-cov-fuzz 리포지터리의 경로입니다. 이 값은 오프라인 환경을 사용할 때만 변경해야 합니다. 기본값: https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw.
COVFUZZ_USE_REGISTRY GitLab 코퍼스 레지스트리에 코퍼스를 저장하려면 true로 설정합니다. 이 변수를 true로 설정하면 COVFUZZ_CORPUS_NAME과 COVFUZZ_GITLAB_TOKEN 변수가 필요합니다. 기본값: false.
COVFUZZ_CORPUS_NAME job에서 사용할 코퍼스의 이름입니다.
COVFUZZ_GITLAB_TOKEN API 읽기/쓰기 액세스 권한이 있는 개인 액세스 토큰 또는 프로젝트 액세스 토큰으로 구성한 환경 변수입니다.

시드 코퍼스#

시드 코퍼스의 파일은 수동으로 갱신해야 합니다. 커버리지 기반 퍼즈 테스팅 job은 이 파일을 갱신하거나 덮어쓰지 않습니다.

커버리지 기반 퍼즈 테스팅 절차#

퍼즈 테스팅 절차는 다음과 같습니다.

  1. 대상 애플리케이션을 컴파일합니다.
  2. gitlab-cov-fuzz 도구를 사용해 계측된 애플리케이션을 실행합니다.
  3. 퍼저가 출력한 예외 정보를 파싱하고 분석합니다.
  4. 다음 중 한 곳에서 코퍼스를 내려받습니다.
  5. 이전 파이프라인에서 크래시 이벤트를 내려받습니다.
  6. 파싱한 크래시 이벤트와 데이터를 gl-coverage-fuzzing-report.json 파일로 출력합니다.
  7. 다음 중 한 곳에서 코퍼스를 갱신합니다.
    • 해당 job의 파이프라인.
    • COVFUZZ_USE_REGISTRY가 true로 설정된 경우 코퍼스 레지스트리.

커버리지 기반 퍼즈 테스팅의 결과는 CI/CD 파이프라인에서 확인할 수 있습니다.

확대 적용#

단일 프로젝트에서 커버리지 기반 퍼즈 테스팅에 익숙해지면 오프라인 환경에서의 테스트 활성화를 포함한 다음 고급 기능을 활용할 수 있습니다.

지원되는 퍼징 엔진 및 언어#

다음 퍼징 엔진을 사용해 지정된 언어를 테스트할 수 있습니다.

커버리지 기반 퍼즈 테스팅 실행 시간#

커버리지 기반 퍼즈 테스팅에 사용할 수 있는 실행 시간은 다음과 같습니다.

  • 10분(기본값): 기본 브랜치에 권장합니다.
  • 60분: 개발 브랜치와 머지 리퀘스트에 권장합니다. 실행 시간이 길수록 커버리지가 넓어집니다. COVFUZZ_ADDITIONAL_ARGS 변수에 --regression=true 값을 설정합니다.

전체 예시는 Go 커버리지 기반 퍼징 예시를 참고합니다.

지속적 커버리지 기반 퍼즈 테스팅#

메인 파이프라인을 차단하지 않으면서 커버리지 기반 퍼징 job을 더 오래 실행할 수도 있습니다. 이 구성은 GitLab의 상위-하위 파이프라인을 사용합니다.

이 시나리오에서 권장하는 워크플로는 메인 브랜치나 개발 브랜치에서 장기 실행 비동기 퍼징 job을 실행하고, 그 밖의 모든 브랜치와 MR에서는 짧은 동기 퍼징 job을 실행하는 것입니다. 이렇게 하면 커밋별 파이프라인을 빠르게 완료하면서도 퍼저가 애플리케이션을 충분히 탐색하고 테스트할 시간을 확보할 수 있습니다. 커버리지 기반 퍼저가 코드베이스의 깊은 버그를 찾으려면 대개 장기 실행 퍼징 job 이 필요합니다.

다음은 이 워크플로를 위한 .gitlab-ci.yml 파일의 일부입니다. 전체 예시는 Go 퍼징 예시 리포지터리를 참고합니다.


sync_fuzzing:
  variables:
    COVFUZZ_ADDITIONAL_ARGS: '-max_total_time=300'
  trigger:
    include: .covfuzz-ci.yml
    strategy: depend
  rules:
    - if: $CI_COMMIT_BRANCH != 'continuous_fuzzing' && $CI_PIPELINE_SOURCE != 'merge_request_event'

async_fuzzing:
  variables:
    COVFUZZ_ADDITIONAL_ARGS: '-max_total_time=3600'
  trigger:
    include: .covfuzz-ci.yml
  rules:
    - if: $CI_COMMIT_BRANCH == 'continuous_fuzzing' && $CI_PIPELINE_SOURCE != 'merge_request_event'

이 구성은 두 개의 job을 생성합니다.

  1. sync_fuzzing: 모든 퍼즈 대상을 차단 방식으로 짧게 실행합니다. 단순한 버그를 찾아내며, MR 이 새로운 버그를 들여오거나 예전 버그를 다시 발생시키지 않는다는 점을 확인할 수 있습니다.
  2. async_fuzzing: 브랜치에서 실행되며 개발 주기와 MR을 차단하지 않고 코드의 깊은 버그를 찾아냅니다.

covfuzz-ci.yml은 원래의 동기 방식 예시와 동일합니다.

FIPS 지원 바이너리#

커버리지 퍼징 바이너리는 Linux x86에서 golang-fips로 컴파일되며 암호화 백엔드로 OpenSSL을 사용합니다. 자세한 내용은 Go를 사용하는 GitLab의 FIPS 준수 문서를 참고합니다.

오프라인 환경#

오프라인 환경에서 커버리지 퍼징을 사용하려면 다음 단계를 수행합니다.

  1. gitlab-cov-fuzz를 오프라인 GitLab 인스턴스가 접근할 수 있는 비공개 리포지터리로 클론합니다.

  2. 각 퍼징 단계에서 COVFUZZ_URL_PREFIX를 ${NEW_URL_GITLAB_COV_FUZ}/-/raw로 설정합니다. 여기서 NEW_URL_GITLAB_COV_FUZ는 첫 번째 단계에서 준비한 비공개 gitlab-cov-fuzz 클론의 URL 입니다.

문제 해결#

오류 Unable to extract corpus folder from artifacts zip file#

COVFUZZ_USE_REGISTRY가 true로 설정된 상태에서 이 오류 메시지가 나타나면, 업로드한 코퍼스 파일이 corpus라는 이름의 폴더로 압축 해제되는지 확인합니다.

오류 400 Bad request - Duplicate package is not allowed#

COVFUZZ_USE_REGISTRY를 true로 설정하고 퍼징 job을 실행할 때 이 오류 메시지가 나타나면, 중복이 허용되어 있는지 확인합니다. 자세한 내용은 중복 Generic 패키지를 참고합니다.

Footnotes

  1. Gradle 지원은 이슈 409764에서 계획되어 있습니다. ↩

커버리지 기반 퍼즈 테스팅 (지원 중단됨)

GitLab v19.5
Tier: Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기

요약

이 기능은 GitLab 18.0에서 지원 중단되었으며 19.0에서 제거될 예정입니다. 커버리지 기반 퍼즈 테스팅은 계측된 버전의 애플리케이션에 무작위 입력을 보내 예상치 못한 동작을 유발합니다. 퍼즈 테스팅은 GitLab Secure의 다른 보안 스캐너 및 자체 테스트 프로세스와 함께 사용해야 합니다.

Warning

이 기능은 GitLab 18.0에서 지원 중단되었으며 19.0에서 제거될 예정입니다. 이는 브레이킹 체인지입니다.

시작하기#

커버리지 기반 퍼즈 테스팅은 계측된 버전의 애플리케이션에 무작위 입력을 보내 예상치 못한 동작을 유발합니다. 이러한 동작은 수정해야 할 버그를 나타냅니다. GitLab은 파이프라인에 커버리지 기반 퍼즈 테스팅을 추가하는 기능을 제공합니다. 이를 통해 다른 QA 프로세스가 놓칠 수 있는 버그와 잠재적 보안 문제를 발견할 수 있습니다.

퍼즈 테스팅은 GitLab Secure의 다른 보안 스캐너 및 자체 테스트 프로세스와 함께 사용해야 합니다. GitLab CI/CD를 사용하는 경우 CI/CD 워크플로의 일부로 커버리지 기반 퍼즈 테스팅을 실행할 수 있습니다.

개요는 Coverage-guided Fuzzing - Advanced Security Testing을 참고합니다.

커버리지 기반 퍼즈 테스팅 상태 확인#

커버리지 기반 퍼즈 테스팅의 상태를 확인하려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션의 상태는 다음 중 하나입니다.
    • Not configured
    • Enabled
    • GitLab Ultimate로 업그레이드하라는 안내.

커버리지 기반 퍼즈 테스팅 활성화#

커버리지 기반 퍼즈 테스팅을 활성화하려면 .gitlab-ci.yml을 다음과 같이 편집합니다.

  1. Stage 목록에 fuzz Stage를 추가합니다.

  2. 애플리케이션이 Go로 작성되지 않았다면 해당 퍼징 엔진을 사용하는 Docker 이미지를 지정합니다. 예를 들면 다음과 같습니다.

    image: python:latest
    
  3. GitLab 설치에 포함되어 제공되는 Coverage-Fuzzing.gitlab-ci.yml 템플릿 을 포함합니다.

  4. 요구 사항에 맞게 my_fuzz_target job을 사용자 지정합니다.

커버리지 기반 퍼징 구성 예시#

stages:
  - fuzz

include:
  - template: Coverage-Fuzzing.gitlab-ci.yml

my_fuzz_target:
  extends: .fuzz_base
  script:
    # Build your fuzz target binary in these steps, then run it with gitlab-cov-fuzz
    # See our example repos for how you could do this with any of our supported languages
    - ./gitlab-cov-fuzz run --regression=$REGRESSION -- <your fuzz target>

Coverage-Fuzzing 템플릿에는 숨겨진 job .fuzz_base가 포함되어 있으며, 각 퍼징 대상마다 이를 확장해야 합니다. 각 퍼징 대상은 별도의 job을 가져야 합니다. 예를 들어 go-fuzzing-example 프로젝트 에는 단일 퍼징 대상에 대해 .fuzz_base를 확장하는 job 이 하나 있습니다.

숨겨진 job .fuzz_base는 사용자 job에서 재정의해서는 안 되는 여러 YAML 키를 사용합니다. 이러한 키를 사용자 job에 포함한다면 원래 내용을 그대로 복사해야 합니다.

  • before_script
  • artifacts
  • rules

결과 이해#

출력#

각 퍼징 단계는 다음 아티팩트를 출력합니다.

  • gl-coverage-fuzzing-report.json: 커버리지 기반 퍼즈 테스팅과 그 결과의 세부 정보가 담긴 보고서입니다.
  • artifacts.zip: 이 파일에는 두 개의 디렉터리가 들어 있습니다.
    • corpus: 현재 job과 이전 모든 job 이 생성한 테스트 케이스를 모두 포함합니다.
    • crashes: 현재 job 이 발견한 모든 크래시 이벤트와 이전 job에서 수정되지 않은 이벤트를 포함합니다.

JSON 보고서 파일은 CI/CD 파이프라인 페이지에서 내려받을 수 있습니다. 자세한 내용은 아티팩트 다운로드를 참고합니다.

코퍼스 레지스트리#

코퍼스 레지스트리는 코퍼스의 라이브러리입니다. 프로젝트 레지스트리에 있는 코퍼스는 해당 프로젝트의 모든 job에서 사용할 수 있습니다. 프로젝트 전체 레지스트리는 job 당 코퍼스 하나라는 기본 방식보다 코퍼스를 효율적으로 관리하는 방법입니다.

코퍼스 레지스트리는 패키지 레지스트리를 사용해 프로젝트의 코퍼스를 저장합니다. 레지스트리에 저장된 코퍼스는 데이터 무결성을 위해 숨겨집니다.

코퍼스를 내려받으면 처음 업로드할 때 사용한 파일 이름과 관계없이 파일 이름이 artifacts.zip 이 됩니다. 이 파일에는 코퍼스만 들어 있으며, CI/CD 파이프라인에서 내려받을 수 있는 아티팩트 파일과는 다릅니다. 또한 Reporter 이상의 권한을 가진 프로젝트 멤버는 직접 다운로드 링크로 코퍼스를 내려받을 수 있습니다.

코퍼스 레지스트리 세부 정보 보기#

코퍼스 레지스트리의 세부 정보를 보려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션에서 Manage corpus를 선택합니다.

코퍼스 레지스트리에 코퍼스 생성#

코퍼스 레지스트리에 코퍼스를 생성하는 방법은 다음 두 가지입니다.

  • 파이프라인에서 코퍼스 생성
  • 기존 코퍼스 파일 업로드
파이프라인에서 코퍼스 생성#

파이프라인에서 코퍼스를 생성하려면 다음 단계를 수행합니다.

  1. .gitlab-ci.yml 파일에서 my_fuzz_target job을 편집합니다.
  2. 다음 변수를 설정합니다.
    • COVFUZZ_USE_REGISTRY를 true로 설정합니다.
    • COVFUZZ_CORPUS_NAME에 코퍼스 이름을 설정합니다.
    • COVFUZZ_GITLAB_TOKEN을 개인 액세스 토큰 값으로 설정합니다.

my_fuzz_target job 이 실행되면 코퍼스는 COVFUZZ_CORPUS_NAME 변수에 지정한 이름으로 코퍼스 레지스트리에 저장됩니다. 코퍼스는 파이프라인이 실행될 때마다 갱신됩니다.

코퍼스 파일 업로드#

기존 코퍼스 파일을 업로드하려면 다음 단계를 수행합니다.

  1. 상단 표시줄에서 Search or go to를 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Secure > Security configuration을 선택합니다.
  3. Coverage Fuzzing 섹션에서 Manage corpus를 선택합니다.
  4. New corpus를 선택합니다.
  5. 각 필드를 입력합니다.
  6. Upload file을 선택합니다.
  7. Add를 선택합니다.

이제 .gitlab-ci.yml 파일에서 해당 코퍼스를 참조할 수 있습니다. COVFUZZ_CORPUS_NAME 변수에 사용한 값이 업로드한 코퍼스 파일에 지정한 이름과 정확히 일치하는지 확인합니다.

코퍼스 레지스트리에 저장된 코퍼스 사용#

코퍼스 레지스트리에 저장된 코퍼스를 사용하려면 이름으로 참조해야 합니다. 해당 코퍼스의 이름은 코퍼스 레지스트리 세부 정보에서 확인합니다.

사전 요구 사항:

  1. .gitlab-ci.yml 파일에 다음 변수를 설정합니다.
    • COVFUZZ_USE_REGISTRY를 true로 설정합니다.
    • COVFUZZ_CORPUS_NAME을 코퍼스 이름으로 설정합니다.
    • COVFUZZ_GITLAB_TOKEN을 개인 액세스 토큰 값으로 설정합니다.

커버리지 기반 퍼즈 테스팅 보고서#

gl-coverage-fuzzing-report.json 파일 형식에 대한 자세한 내용은 스키마를 참고합니다.

커버리지 기반 퍼징 보고서 예시:

{
  "version": "v1.0.8",
  "regression": false,
  "exit_code": -1,
  "vulnerabilities": [
    {
      "category": "coverage_fuzzing",
      "message": "Heap-buffer-overflow\nREAD 1",
      "description": "Heap-buffer-overflow\nREAD 1",
      "severity": "Critical",
      "stacktrace_snippet": "INFO: Seed: 3415817494\nINFO: Loaded 1 modules   (7 inline 8-bit counters): 7 [0x10eee2470, 0x10eee2477), \nINFO: Loaded 1 PC tables (7 PCs): 7 [0x10eee2478,0x10eee24e8), \nINFO:        5 files found in corpus\nINFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes\nINFO: seed corpus: files: 5 min: 1b max: 4b total: 14b rss: 26Mb\n#6\tINITED cov: 7 ft: 7 corp: 5/14b exec/s: 0 rss: 26Mb\n=================================================================\n==43405==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000001573 at pc 0x00010eea205a bp 0x7ffee0d5e090 sp 0x7ffee0d5e088\nREAD of size 1 at 0x602000001573 thread T0\n    #0 0x10eea2059 in FuzzMe(unsigned char const*, unsigned long) fuzz_me.cc:9\n    #1 0x10eea20ba in LLVMFuzzerTestOneInput fuzz_me.cc:13\n    #2 0x10eebe020 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:556\n    #3 0x10eebd765 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470\n    #4 0x10eebf966 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:698\n    #5 0x10eec0665 in fuzzer::Fuzzer::Loop(std::__1::vector\u003cfuzzer::SizedFile, fuzzer::fuzzer_allocator\u003cfuzzer::SizedFile\u003e \u003e\u0026) FuzzerLoop.cpp:830\n    #6 0x10eead0cd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829\n    #7 0x10eedaf82 in main FuzzerMain.cpp:19\n    #8 0x7fff684fecc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)\n\n0x602000001573 is located 0 bytes to the right of 3-byte region [0x602000001570,0x602000001573)\nallocated by thread T0 here:\n    #0 0x10ef92cfd in wrap__Znam+0x7d (libclang_rt.asan_osx_dynamic.dylib:x86_64+0x50cfd)\n    #1 0x10eebdf31 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) FuzzerLoop.cpp:541\n    #2 0x10eebd765 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) FuzzerLoop.cpp:470\n    #3 0x10eebf966 in fuzzer::Fuzzer::MutateAndTestOne() FuzzerLoop.cpp:698\n    #4 0x10eec0665 in fuzzer::Fuzzer::Loop(std::__1::vector\u003cfuzzer::SizedFile, fuzzer::fuzzer_allocator\u003cfuzzer::SizedFile\u003e \u003e\u0026) FuzzerLoop.cpp:830\n    #5 0x10eead0cd in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) FuzzerDriver.cpp:829\n    #6 0x10eedaf82 in main FuzzerMain.cpp:19\n    #7 0x7fff684fecc8 in start+0x0 (libdyld.dylib:x86_64+0x1acc8)\n\nSUMMARY: AddressSanitizer: heap-buffer-overflow fuzz_me.cc:9 in FuzzMe(unsigned char const*, unsigned long)\nShadow bytes around the buggy address:\n  0x1c0400000250: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000260: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000270: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000280: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n  0x1c0400000290: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa\n=\u003e0x1c04000002a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa[03]fa\n  0x1c04000002b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\n  0x1c04000002f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa\nShadow byte legend (one shadow byte represents 8 application bytes):\n  Addressable:           00\n  Partially addressable: 01 02 03 04 05 06 07 \n  Heap left redzone:       fa\n  Freed heap region:       fd\n  Stack left redzone:      f1\n  Stack mid redzone:       f2\n  Stack right redzone:     f3\n  Stack after return:      f5\n  Stack use after scope:   f8\n  Global redzone:          f9\n  Global init order:       f6\n  Poisoned by user:        f7\n  Container overflow:      fc\n  Array cookie:            ac\n  Intra object redzone:    bb\n  ASan internal:           fe\n  Left alloca redzone:     ca\n  Right alloca redzone:    cb\n  Shadow gap:              cc\n==43405==ABORTING\nMS: 1 EraseBytes-; base unit: de3a753d4f1def197604865d76dba888d6aefc71\n0x46,0x55,0x5a,\nFUZ\nartifact_prefix='./crashes/'; Test unit written to ./crashes/crash-0eb8e4ed029b774d80f2b66408203801cb982a60\nBase64: RlVa\nstat::number_of_executed_units: 122\nstat::average_exec_per_sec:     0\nstat::new_units_added:          0\nstat::slowest_unit_time_sec:    0\nstat::peak_rss_mb:              28",
      "scanner": {
        "id": "libFuzzer",
        "name": "libFuzzer"
      },
      "location": {
        "crash_address": "0x602000001573",
        "crash_state": "FuzzMe\nstart\nstart+0x0\n\n",
        "crash_type": "Heap-buffer-overflow\nREAD 1"
      },
      "tool": "libFuzzer"
    }
  ]
}

취약점 처리#

취약점이 발견되면 해당 취약점을 처리할 수 있습니다. 머지 리퀘스트의 Reports 탭에 취약점이 표시되고 퍼징 아티팩트를 내려받는 버튼이 함께 제공됩니다. 탐지된 취약점 중 하나를 선택하면 세부 정보를 확인할 수 있습니다.

Security Dashboard에서도 취약점을 확인할 수 있으며, 여기에서 그룹·프로젝트·파이프라인의 모든 보안 취약점을 한눈에 볼 수 있습니다.

취약점을 선택하면 해당 취약점에 대한 추가 정보를 제공하는 모달이 열립니다.

  • Status: 취약점의 상태입니다. 다른 유형의 취약점과 마찬가지로 커버리지 퍼징 취약점도 Detected, Confirmed, Dismissed, Resolved 중 하나가 됩니다.
  • Project: 취약점이 존재하는 프로젝트입니다.
  • Crash type: 코드에서 발생한 크래시 또는 결함의 유형입니다. 일반적으로 CWE에 대응됩니다.
  • Crash state: 스택 트레이스를 정규화한 형태로, 크래시 직전 세 개의 함수를 포함합니다 (무작위 주소는 제외됩니다).
  • Stack trace snippet: 스택 트레이스의 마지막 몇 줄로, 크래시의 세부 정보를 보여 줍니다.
  • Identifier: 취약점의 식별자입니다. CVE 또는 CWE에 대응됩니다.
  • Severity: 취약점의 심각도입니다. Critical, High, Medium, Low, Info, Unknown 중 하나입니다.
  • Scanner: 취약점을 탐지한 스캐너입니다(예: Coverage Fuzzing).
  • Scanner Provider: 스캔을 수행한 엔진입니다. Coverage Fuzzing의 경우 지원되는 퍼징 엔진 및 언어에 나열된 엔진 중 하나입니다.

최적화#

다음 사용자 지정 옵션을 사용해 프로젝트에 맞게 커버리지 기반 퍼즈 테스팅을 최적화합니다.

사용 가능한 CI/CD 변수#

CI/CD 파이프라인에서 커버리지 기반 퍼즈 테스팅을 구성할 때 다음 변수를 사용합니다.

Warning

GitLab 보안 스캐닝 도구의 사용자 지정은 기본 브랜치에 변경 사항을 병합하기 전에 머지 리퀘스트에서 먼저 테스트해야 합니다. 그렇지 않으면 다수의 오탐을 포함한 예상치 못한 결과가 발생할 수 있습니다.

CI/CD 변수 설명
COVFUZZ_ADDITIONAL_ARGS gitlab-cov-fuzz에 전달되는 인수입니다. 기반 퍼징 엔진의 동작을 사용자 지정하는 데 사용합니다. 전체 인수 목록은 퍼징 엔진의 문서를 참고합니다.
COVFUZZ_BRANCH 장기 실행 퍼징 job을 실행할 브랜치입니다. 다른 모든 브랜치에서는 퍼징 회귀 테스트만 실행됩니다. 기본값: 리포지터리의 기본 브랜치.
COVFUZZ_SEED_CORPUS 시드 코퍼스 디렉터리의 경로입니다. 기본값: 비어 있음.
COVFUZZ_URL_PREFIX 오프라인 환경에서 사용하기 위해 클론한 gitlab-cov-fuzz 리포지터리의 경로입니다. 이 값은 오프라인 환경을 사용할 때만 변경해야 합니다. 기본값: https://gitlab.com/gitlab-org/security-products/analyzers/gitlab-cov-fuzz/-/raw.
COVFUZZ_USE_REGISTRY GitLab 코퍼스 레지스트리에 코퍼스를 저장하려면 true로 설정합니다. 이 변수를 true로 설정하면 COVFUZZ_CORPUS_NAME과 COVFUZZ_GITLAB_TOKEN 변수가 필요합니다. 기본값: false.
COVFUZZ_CORPUS_NAME job에서 사용할 코퍼스의 이름입니다.
COVFUZZ_GITLAB_TOKEN API 읽기/쓰기 액세스 권한이 있는 개인 액세스 토큰 또는 프로젝트 액세스 토큰으로 구성한 환경 변수입니다.

시드 코퍼스#

시드 코퍼스의 파일은 수동으로 갱신해야 합니다. 커버리지 기반 퍼즈 테스팅 job은 이 파일을 갱신하거나 덮어쓰지 않습니다.

커버리지 기반 퍼즈 테스팅 절차#

퍼즈 테스팅 절차는 다음과 같습니다.

  1. 대상 애플리케이션을 컴파일합니다.
  2. gitlab-cov-fuzz 도구를 사용해 계측된 애플리케이션을 실행합니다.
  3. 퍼저가 출력한 예외 정보를 파싱하고 분석합니다.
  4. 다음 중 한 곳에서 코퍼스를 내려받습니다.
  5. 이전 파이프라인에서 크래시 이벤트를 내려받습니다.
  6. 파싱한 크래시 이벤트와 데이터를 gl-coverage-fuzzing-report.json 파일로 출력합니다.
  7. 다음 중 한 곳에서 코퍼스를 갱신합니다.
    • 해당 job의 파이프라인.
    • COVFUZZ_USE_REGISTRY가 true로 설정된 경우 코퍼스 레지스트리.

커버리지 기반 퍼즈 테스팅의 결과는 CI/CD 파이프라인에서 확인할 수 있습니다.

확대 적용#

단일 프로젝트에서 커버리지 기반 퍼즈 테스팅에 익숙해지면 오프라인 환경에서의 테스트 활성화를 포함한 다음 고급 기능을 활용할 수 있습니다.

지원되는 퍼징 엔진 및 언어#

다음 퍼징 엔진을 사용해 지정된 언어를 테스트할 수 있습니다.

커버리지 기반 퍼즈 테스팅 실행 시간#

커버리지 기반 퍼즈 테스팅에 사용할 수 있는 실행 시간은 다음과 같습니다.

  • 10분(기본값): 기본 브랜치에 권장합니다.
  • 60분: 개발 브랜치와 머지 리퀘스트에 권장합니다. 실행 시간이 길수록 커버리지가 넓어집니다. COVFUZZ_ADDITIONAL_ARGS 변수에 --regression=true 값을 설정합니다.

전체 예시는 Go 커버리지 기반 퍼징 예시를 참고합니다.

지속적 커버리지 기반 퍼즈 테스팅#

메인 파이프라인을 차단하지 않으면서 커버리지 기반 퍼징 job을 더 오래 실행할 수도 있습니다. 이 구성은 GitLab의 상위-하위 파이프라인을 사용합니다.

이 시나리오에서 권장하는 워크플로는 메인 브랜치나 개발 브랜치에서 장기 실행 비동기 퍼징 job을 실행하고, 그 밖의 모든 브랜치와 MR에서는 짧은 동기 퍼징 job을 실행하는 것입니다. 이렇게 하면 커밋별 파이프라인을 빠르게 완료하면서도 퍼저가 애플리케이션을 충분히 탐색하고 테스트할 시간을 확보할 수 있습니다. 커버리지 기반 퍼저가 코드베이스의 깊은 버그를 찾으려면 대개 장기 실행 퍼징 job 이 필요합니다.

다음은 이 워크플로를 위한 .gitlab-ci.yml 파일의 일부입니다. 전체 예시는 Go 퍼징 예시 리포지터리를 참고합니다.


sync_fuzzing:
  variables:
    COVFUZZ_ADDITIONAL_ARGS: '-max_total_time=300'
  trigger:
    include: .covfuzz-ci.yml
    strategy: depend
  rules:
    - if: $CI_COMMIT_BRANCH != 'continuous_fuzzing' && $CI_PIPELINE_SOURCE != 'merge_request_event'

async_fuzzing:
  variables:
    COVFUZZ_ADDITIONAL_ARGS: '-max_total_time=3600'
  trigger:
    include: .covfuzz-ci.yml
  rules:
    - if: $CI_COMMIT_BRANCH == 'continuous_fuzzing' && $CI_PIPELINE_SOURCE != 'merge_request_event'

이 구성은 두 개의 job을 생성합니다.

  1. sync_fuzzing: 모든 퍼즈 대상을 차단 방식으로 짧게 실행합니다. 단순한 버그를 찾아내며, MR 이 새로운 버그를 들여오거나 예전 버그를 다시 발생시키지 않는다는 점을 확인할 수 있습니다.
  2. async_fuzzing: 브랜치에서 실행되며 개발 주기와 MR을 차단하지 않고 코드의 깊은 버그를 찾아냅니다.

covfuzz-ci.yml은 원래의 동기 방식 예시와 동일합니다.

FIPS 지원 바이너리#

커버리지 퍼징 바이너리는 Linux x86에서 golang-fips로 컴파일되며 암호화 백엔드로 OpenSSL을 사용합니다. 자세한 내용은 Go를 사용하는 GitLab의 FIPS 준수 문서를 참고합니다.

오프라인 환경#

오프라인 환경에서 커버리지 퍼징을 사용하려면 다음 단계를 수행합니다.

  1. gitlab-cov-fuzz를 오프라인 GitLab 인스턴스가 접근할 수 있는 비공개 리포지터리로 클론합니다.

  2. 각 퍼징 단계에서 COVFUZZ_URL_PREFIX를 ${NEW_URL_GITLAB_COV_FUZ}/-/raw로 설정합니다. 여기서 NEW_URL_GITLAB_COV_FUZ는 첫 번째 단계에서 준비한 비공개 gitlab-cov-fuzz 클론의 URL 입니다.

문제 해결#

오류 Unable to extract corpus folder from artifacts zip file#

COVFUZZ_USE_REGISTRY가 true로 설정된 상태에서 이 오류 메시지가 나타나면, 업로드한 코퍼스 파일이 corpus라는 이름의 폴더로 압축 해제되는지 확인합니다.

오류 400 Bad request - Duplicate package is not allowed#

COVFUZZ_USE_REGISTRY를 true로 설정하고 퍼징 job을 실행할 때 이 오류 메시지가 나타나면, 중복이 허용되어 있는지 확인합니다. 자세한 내용은 중복 Generic 패키지를 참고합니다.

Footnotes

  1. Gradle 지원은 이슈 409764에서 계획되어 있습니다. ↩