AI가 생성한 코드가 오픈소스 라이선스를 베꼈는지 PR에서 탐지하는 오픈소스 라이선스 게이트
Project description
Provenire
AI가 생성한 코드가 오픈소스 라이선스를 베꼈는지, PR에서 잡아내는 오픈소스 라이선스 게이트
provenire — 라틴어 "기원하다". 코드가 어디서 왔는지(provenance) 를 추적합니다.
문제
Copilot·Cursor·Claude는 GPL·AGPL 저장소로 학습되었습니다. 이들이 원본 코드를 출처 없이 그대로 재현하는 일이 실제로 벌어집니다 — 이를 "AI 라이선스 세탁" 이라 부릅니다.
카피레프트 코드가 섞여 들어가면 제품 소스 전체를 공개해야 할 수도 있습니다. 실제로 이 문제로 코드베이스를 전면 재작성한 기업 사례가 보고되었습니다.
그런데 이를 검사하는 도구는 FOSSA · Snyk · Black Duck — 전부 유료입니다. 학생·개인·소규모 팀은 무방비입니다.
핵심 아이디어
GitHub Copilot의 public code filter는 문자열이 거의 똑같을 때만 걸러냅니다.
변수명만 바꾸면 그대로 통과합니다.
Provenire는 지문을 뜨기 전에 식별자를 익명화합니다.
원본: def elide_filename(filename, length): -> def ID(ID, ID):
AI : def truncate_path(path_str, max_len): -> def ID(ID, ID):
^ 지문이 같아진다
이름은 지우고 구조(키워드·연산자·제어흐름)만 남긴 뒤 winnowing 지문을 뜹니다. → 이름을 아무리 바꿔도 잡힙니다.
실제 동작
AI가 GPL 코드를 변수명만 바꿔서 뱉었다고 해봅시다.
$ provenire compare ai_output.py gpl_origin.py
모드 유사도 지문
------------------------------------------
raw (baseline) 2.1% 1/48 <- Copilot 필터 수준: 놓친다
tokens (기본) 100.0% 21/21 <- Provenire: 잡아낸다
------------------------------------------
[!] 표절 의심 — 유사도 100.0% (임계값 30%)
종료 코드 1 → CI/PR 게이트로 바로 쓸 수 있습니다.
검증 결과
실제 GPL-3.0 코드(qutebrowser/utils/utils.py)로 검증했습니다.
| 변형 시나리오 | raw (baseline) | tokens (Provenire) |
|---|---|---|
| 그대로 복사 | 100% | 100% |
| 주석·독스트링 삭제 | 100% | 86.4% |
| 변수·함수명 전부 변경 | 0.0% (놓침) | 100.0% (탐지) |
| 이름변경 + 주석삭제 + 재포맷 | 0.0% (놓침) | 86.4% (탐지) |
| 무관한 코드 (음성대조) | 0% | 0% (오탐 없음) |
재현:
benchmarks/· 상세:benchmarks/RESULTS.md
설치
pip install provenire # (배포 예정)
# 지금은 소스에서
git clone https://github.com/4thIS/opensource_contest_provenire.git
cd opensource_contest_provenire
pip install -e ".[dev]"
사용법
provenire compare <의심코드> <원본> # 두 파일의 유사도
provenire fingerprint <파일> # 지문 미리보기
from provenire import compare
m = compare(ai_generated_code, gpl_source)
if m.is_suspicious:
print(f"표절 의심: {m.similarity:.1%}")
로드맵
- winnowing 핑거프린팅 엔진
- 토큰 정규화 (식별자 익명화) — 핵심 차별점
- 유사도 판정 (containment)
- CLI (
compare/fingerprint) - 카피레프트 코퍼스 인덱스 (LSH/MinHash)
-
provenire scan --against copyleft— 인덱스 대조 - GitHub Action — PR 인라인 코멘트
- LLM 2차 판정 (관용구 vs 구조적 재현)
- 다언어 지원 (Pygments 500+ 언어)
- pre-commit 훅
어떻게 동작하나
PR diff (추가된 줄)
|
|-- 1. 정규화 주석·공백 제거 -> 토큰화 -> 식별자를 ID로 익명화
|-- 2. 핑거프린팅 k-gram 해시 -> 슬라이딩 윈도우 최소해시 (winnowing)
|-- 3. 인덱스 검색 카피레프트 코퍼스 지문 DB 조회
|-- 4. 유사도 판정 containment = |공유 지문| / |의심 코드 지문|
`-- 5. 리포트 원본 링크 · 라이선스 · 위험도
알고리즘: Schleimer, Wilkerson, Aiken. "Winnowing: Local Algorithms for Document Fingerprinting" (SIGMOD 2003) — MOSS의 기반.
기여
새 언어·새 라이선스 규칙은 파일 하나 추가로 확장됩니다.
CONTRIBUTING.md를 봐주세요. good first issue 환영합니다.
라이선스
Apache-2.0 — 특허 조항 포함. 자유롭게 쓰시되 출처를 남겨주세요.
Provenire는 법률 자문이 아닙니다. 의심 구간을 알려주는 도구이지 판결이 아닙니다.
2026 오픈소스 개발자대회 출품작 · 팀 코드감식반 (이우진 · 김민수)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file provenire-0.0.1.tar.gz.
File metadata
- Download URL: provenire-0.0.1.tar.gz
- Upload date:
- Size: 31.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afcf70eabf96dea211c019165ba032ed69060f511611cacccc1d27ad3d802bf1
|
|
| MD5 |
5e48d3758fefe5e92d9c59fa582a823d
|
|
| BLAKE2b-256 |
fb95cf722d35de7fbae5a6b1160973c6ac0f30d1a22e1b4bdaf41504d011cc85
|
File details
Details for the file provenire-0.0.1-py3-none-any.whl.
File metadata
- Download URL: provenire-0.0.1-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cd139c3240a7133ffb8f307a8ad6ab7fbce15ed832b73f82708549f2df7408f
|
|
| MD5 |
0b584af569f752f0cf77b5f7ea8403f2
|
|
| BLAKE2b-256 |
db902b3563ce2b05a453feea4b3bdf4ed67e48a22af863bb5f2f01b7a88aa096
|