CodeQL이 움직이는 재사용 워크플로 태그를 잡는다…경고 위치도 달라진다

GitHub는 2026년 9월 3일 CodeQL 2.26.4의 보안 탐지 개선을 공개했다. GitHub의 9월 3일 공지 에 따르면 재사용 가능한 GitHub Actions 워크플로의 변경 가능한 참조가 탐지 대상에 포함됐고, Rust 데이터 흐름 경고는 실제 소스와 싱크를 기준으로 위치가 달라질 수 있다. GitHub.com의 코드 스캐닝에는 새 버전이 자동 배포되지만, GitHub Enterprise Server(GHES)에는 향후 릴리스에 포함될 예정이다.
저장소 관리자가 보게 될 직접적인 변화는 두 가지다. 기존 워크플로에 남아 있던 이동 가능한 태그나 브랜치 참조가 새 경고로 나타날 수 있고, 같은 Rust 데이터 흐름 문제도 표시 위치가 바뀌면 종전 경고가 닫힌 뒤 새 경고로 보일 수 있다. 9월 4일 게시된 TECH NOISY의 발표 요약 도 9월 3일 공개와 재사용 워크플로의 변경 가능한 참조 탐지 확대를 확인한다.
재사용 워크플로의 이동 가능한 참조까지 잡는다

actions/unpinned-tag 쿼리는 이제 일반 액션뿐 아니라 재사용 가능한 워크플로를 호출하는 변경 가능한 참조도 탐지한다. 호출 구문이 태그나 브랜치를 가리키면 그 참조가 나중에 다른 커밋으로 이동할 수 있으므로, 호출하는 저장소의 파일이 그대로여도 실제 실행되는 워크플로 내용은 달라질 수 있다.
가령 여러 저장소가 조직의 공통 워크플로를 특정 버전 태그로 호출하는 경우를 생각할 수 있다. 그 태그가 새 커밋을 가리키도록 변경되면 호출 측의 검토 기록과 무관하게 실행 코드가 바뀐다. 반면 특정 커밋 SHA를 직접 지정한 호출은 같은 객체를 계속 가리키므로 이번 쿼리가 겨냥하는 변경 가능성과 구별된다.
따라서 업데이트 뒤 처음 열린 actions/unpinned-tag 경고가 반드시 최근 코드 변경으로 생긴 것은 아니다. 이미 존재하던 CI 설정을 넓어진 분석 범위가 새로 발견했을 수 있다. 확인할 지점은 애플리케이션 소스가 아니라 워크플로 호출의 uses 값과 그 뒤의 참조다.
Rust 경고는 같은 흐름이 새 위치에 다시 나타날 수 있다

Rust 데이터 흐름 쿼리는 경고를 배치하는 기준이 더 구체적으로 바뀌었다. 이전에는 함수 호출 같은 넓은 표현식에 표시되던 경고가 2.26.4에서는 실제 오염 소스나 위험한 싱크 노드에 더 가깝게 놓일 수 있다.
위치 변화는 코드 스캐닝에서 경고의 정체성을 판단하는 데 영향을 준다. 위치가 달라진 일부 결과는 새 경고로 나타나고 기존 경고는 닫히므로, 열린 항목과 닫힌 항목의 수만 보면 취약점이 새로 생겼다가 사라진 것처럼 오해할 수 있다. 이번 변경이 약속하는 것은 경고 위치의 정밀화이지, 기존 취약점의 자동 수정이 아니다.
업데이트 직후 결과를 비교할 때는 새 Rust 경고의 쿼리 ID와 파일, 소스·싱크, 주변 코드 문맥을 최근에 닫힌 경고와 함께 봐야 한다. 이 요소들이 이어진다면 실제 코드 변화보다 위치 재계산이 경고 교체의 원인일 가능성이 있다. 반대로 쿼리나 데이터 흐름 자체가 다르다면 별개의 발견으로 분리해 판단해야 한다.
2.26.4의 언어별 변화는 한꺼번에 적용되지 않는다
CodeQL 2.26.4 공식 변경 기록 은 버전의 릴리스 날짜를 2026년 8월 26일로 표시한다. 따라서 9월 3일은 버전 자체가 처음 나온 날이 아니라 GitHub가 주요 개선을 별도로 공지한 날이다. 변경 기록에 명시된 주요 분석 범위는 다음과 같다.
- GitHub Actions: actions/unpinned-tag가 재사용 워크플로의 변경 가능한 참조를 탐지한다. 이벤트 페이로드의 actor 필드는 해당 필드를 실제로 채우는 이벤트에서만 보호 조건으로 인정돼 ControlCheck 기반 쿼리 결과가 늘 수 있다.
- Rust: 데이터 흐름 경고 위치가 실제 소스와 싱크 노드에 맞춰진다. 특성 항목의 정규 경로 형식도 변경돼 이를 참조하는 사용자 정의 데이터 확장 모델은 갱신이 필요하다.
- Go: Go 1.27 지원이 추가됐다.
- Java/Kotlin: Spring R2DBC DatabaseClient와 R2DBC SPI가 SQL 삽입 싱크로 모델링됐다. CharSequence 인수를 받은 String.valueOf(Object)를 통한 오염 전파도 반영된다.
- JavaScript/TypeScript: 정규식의 d 플래그와 React Native Worklets의 worklet 지시문을 인식한다.
- Python·Ruby: Python은 list.extend와 list.insert를 통한 오염 흐름을 추가했다. Ruby는 정규식 추적 알고리즘 교체로 관련 쿼리 결과가 달라질 수 있다.
- C#: 위조 방지 토큰 검사와 생성자 내 가상 호출 등 여러 쿼리의 정확도가 조정됐고, build-mode: none 데이터베이스의 일부 오탐을 줄이는 변경이 포함됐다.
이 목록이 모든 저장소에서 동시에 새 경고를 만든다는 뜻은 아니다. 실제 결과는 저장소의 분석 언어와 실행한 쿼리 모음, 사용하는 프레임워크, 워크플로 구성에 따라 달라진다. EnvironmentCheck의 models-as-data 옵션도 별도 모델을 추가하지 않으면 기존 동작을 유지한다.
GitHub.com과 GHES에서는 변화 시점부터 나눠 봐야 한다

GitHub.com 코드 스캐닝 사용자는 서비스에 자동 배포된 CodeQL 버전으로 분석하게 된다. 반면 GHES는 2.26.4 기능이 포함된 향후 릴리스를 기다리거나, 구형 GHES에서 CodeQL을 수동으로 업그레이드해야 한다. 같은 조직의 저장소라도 호스팅 환경과 실제 분석 버전이 다르면 새 탐지와 Rust 위치 변경이 같은 시점에 나타나지 않는다.
첫 분석 결과를 비교할 때는 다음 순서로 원인을 나누면 경고 증감을 과장하지 않을 수 있다.
- 분석 로그에서 실제 CodeQL 버전을 확인하고 GitHub.com 자동 배포, GHES 번들, GHES 수동 업그레이드를 구분한다.
- 새 GitHub Actions 경고의 쿼리 ID가 actions/unpinned-tag인지 확인한 뒤 재사용 워크플로 호출이 태그나 브랜치처럼 변경 가능한 참조를 쓰는지 살핀다.
- 새 Rust 데이터 흐름 경고는 같은 쿼리와 파일의 최근 종료 항목에 대조하고, 소스·싱크와 코드 문맥이 이어지는지 확인한다.
- ControlCheck 관련 증가분은 워크플로를 시작한 이벤트가 검사 대상 actor 필드를 실제로 제공하는지 구분한다.
- 다른 언어의 결과는 저장소가 Spring R2DBC, Python 목록 연산, Ruby 정규식처럼 이번에 모델이나 추적 방식이 바뀐 대상을 사용하는지 확인한다.
현재 확정된 범위는 GitHub.com의 자동 배포와 GHES의 향후 포함 방침까지다. 공지에는 2.26.4가 기본으로 들어갈 구체적인 GHES 버전이나 일정이 제시되지 않았다. 그 전까지 경고 변화는 총건수보다 분석 버전, 쿼리 ID, 위치 이동 여부를 기준으로 해석해야 한다.
뉴스레터 구독
최신 Web3, AI, 암호화폐 뉴스를 이메일로 받아보세요.