오픈소스 취약점 제보를 하다 보면 메인테이너와 "어디까지가 라이브러리의 책임인가?"를 두고 토론하게 됩니다.
이번 NiceGUI 취약점 제보 과정은 단순한 '신고-패치'를 넘어, 메인테이너와 함께 더 안전한 구조를 고민하고 코드를 개선해나가는 과정이 인상 깊어 기록을 남깁니다.
결과적으로 2건의 CVE(CVE-2025-66469, 66470)를 획득했고, NiceGUI v4.0의 보안 로드맵에도 영향을 주게 되었습니다.
ui.interactive_image (CVE-2025-66470)이름은 '이미지'지만 실제로는 SVG를 렌더링하기 위해 Vue의 v-html을 사용합니다.
단순히 이미지를 띄우는 용도로 생각하기 쉽지만, SVG의 <foreignObject>를 이용하면 스크립트 실행이 가능했습니다.
ui.add_css (CVE-2025-66469)CSS 주입 함수가 내부적으로 Python repr()을 사용하여 JS로 데이터를 넘기고 있었습니다. 이 과정에서 </style> 태그 탈출(Breakout)이 가능했습니다.
제보 직후, 메인테이너 팀과 흥미로운 논의가 있었습니다.
메인테이너 측은 ui.add_head_html 같은 함수에 검증되지 않은 입력을 넣는 것은 기본적으로 "개발자의 실수(User Error)" 영역이라는 입장이었습니다.
저도 일정 부분 동의했습니다. 하지만 ui.add_css를 이용한 테마 변경(Dynamic Theming)처럼, 개발자가 의도치 않게 취약점을 만들 수 있는 시나리오는 라이브러리 차원에서 막아주어야 한다고 설득했습니다.
다행히 메인테이너분도 이 점에 공감해주셨고, 오히려 "외부 입력 시나리오로 PoC를 구성하면 더 설득력이 높을 것"이라며 팁을 주시기도 했습니다. 덕분에 더 명확한 리포트를 작성할 수 있었습니다.
가장 인상 깊었던 건 패치 방식을 정하는 과정이었습니다.
처음엔 textContent를 이용한 DOM API 방식을 제안했지만, 메인테이너는 한 발 더 나아가 Base64 인코딩 방식을 제안했습니다. ("Dull knife approach" - 칼날을 무디게 해서 전송하자)
이때 제가 코드를 검토하다가 한 가지 기술적 보완점을 발견했습니다. JS의 atob()는 UTF-8(한글, 이모지) 처리에 취약하여 글자가 깨질 수 있다는 점이었습니다.
저는 단순히 지적하는 데 그치지 않고, Uint8Array와 TextDecoder를 활용한 패치 코드를 제안했습니다.
// 제안한 UTF-8 호환 디코딩 로직
const binString = atob(encodedContent);
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
style.textContent = new TextDecoder().decode(bytes);
메인테이너는 이를 흔쾌히 수락("Fair point")했고, 최종 패치에 반영되었습니다. 단순한 '제보자'가 아니라 함께 문제를 해결하는 '기여자'가 된 느낌이라 뿌듯했습니다.
여담으로, 제보 과정에서 실수로 크레딧(Credit) 수락 버튼을 잘못 눌러 'Declined(거절됨)' 상태가 되는 사고(?)가 있었습니다.
다행히 Declined 로그 옆에 Enable 버튼이 있었습니다. (혹시 버튼 잘못 누르신 분들은 당황하지 말고 찾아보세요찾아보세요!)

sanitize 파라미터 도입 (현재는 경고, v4.0부터는 강제화).이번 제보를 통해 "오픈소스 보안은 싸워서 이기는 게 아니라, 함께 안전한 방법을 찾아가는 과정"이라는 점을 다시 한번 배웠습니다.
보통 CVE 제보 대상을 고를 때 가장 먼저 보는 것이 '최근 커밋 날짜(Last commit)'입니다. 이미 죽은 프로젝트(Abandonware)에 제보해봤자 패치될 리가 없으니까요.
하지만 "업데이트가 활발하다 = 보안 대응이 빠르다"는 절대 성립하지 않습니다.
실제로 이번 NiceGUI 건과 같은 날, 스타(Star)도 훨씬 많고 커밋도 자주 올라오는 다른 유명 프로젝트에도 훨씬 치명적인 취약점을 제보했습니다.
결과는 어땠을까요?
기능 개발 속도와 보안 대응 속도는 완전히 별개입니다. 보안 담당자가 따로 없거나, 기능 구현에 밀려 보안 이슈는 뒷전인 경우가 허다하거든요.
결론:
답장이 늦다고 초조해하거나 "내가 잘못 찾았나?" 의심하지 마세요. 이번 NiceGUI 메인테이너분들이 유독 소통이 빠르고 젠틀했던 것일 뿐, "무응답"과 "기다림"도 버그헌팅의 일부입니다. 완벽한 케바케(Case-by-Case)니 인내심을 가집시다!
프라이빗 포크에서 개발자들끼리 구현 방식으로 싸우는거 보면 은근 재밌어요..ㅋㅋㅋ
역시 강 건너 불구경 만한게 없죠 ㅋㅋㅋㅋㅋ
개발자 싸움 직관하고 싶으시면 취약점 헌팅 강추합니다!
개추드립니다.