์ธํฐ๋ท๋ง์์ ํ๋ฃจ 1ํ ์๋ ์คํ๋์ด AI ์คํ์์ค(PyTorchยทvLLMยทLangChain ๋ฑ 55์ข
)์
์ ๊ท CVE๋ฅผ ์์ง โ ์์
(CVE_YYYYMMDD.xls) ์์ฑ โ ๋ฉ์ผ ๋ฐ์ก โ DB ์ ์ฌ โ
์น ํ์ด์ง ์ด๋๊น์ง ์ฒ๋ฆฌํฉ๋๋ค. ์ ๋ถ ๋ฌด๋ฃ ์๋น์ค๋ง ์๋๋ค(์ ์ฉ์นด๋ ๋ถํ์).
์์ฑ๋ ์์ ์ ํ์๋ง(๋ด๋ถ๋ง) SW๊ด๋ฆฌ๋๊ตฌ๋ก ๋ฐ์ ๋์ด ๊ทธ๋๋ก ํ์ฑ๋ฉ๋๋ค. ๊ทธ๋์ 5. ์์ ์ถ๋ ฅ ๊ท๊ฒฉ์ ํ ๊ธ์๋ ๋ฐ๊พธ๋ฉด ์ ๋ฉ๋๋ค.
GitHub Actions (๋งค์ผ 06:00 KST)
โโ ์์ง NVDยทOSVยทGHSAยทKEV
โโ ๋ณํฉยท์ ๊ทํ (๋ฒ์ ๋ฒ์ ๋ฌธ์์ด ๋ณํ)
โโ output/CVE_YYYYMMDD.xls โ ๋ด๋ถ๋ง ๋ฐ์
์ฉ ์ ๋ณธ
โโ ๋ฉ์ผ ๋ฐ์ก (๊ฐ๋ณ ๋ฐ์ก)
โโ data/cve.db (SQLite, ์ ์ฅ์์ ์ปค๋ฐ)
โโ docs/ (GitHub Pages ์ด๋ ํ์ด์ง)
- 5๋ถ ์ ์
- ๊ฐ์ ๋์(watchlist) ์ถ๊ฐํ๊ธฐ
- Gmail ์ฑ ๋น๋ฐ๋ฒํธ ๋ฐ๊ธ
- 60์ผ ์๋ ๋นํ์ฑํ ๋์
- ์์ ์ถ๋ ฅ ๊ท๊ฒฉ (๋ด๋ถ๋ง ๋ด๋น์ ์ ๋ฌ์ฉ)
- ๋ฌด๋ฃ ํฐ์ด ํ๊ณ์ Supabase ์ ํ
- ์์ง ๋ฒ์(Scope) ์กฐ์ ํ๊ธฐ
- CLI ์ฌ์ฉ๋ฒ
- ์ ์ฅ์ ๊ตฌ์กฐ
- ๋ฌธ์ ํด๊ฒฐ
- SUMMARY ํ๊ตญ์ด ๋ฒ์ญ
์์๋ฅผ ์ง์ผ ์ฃผ์ธ์. โ โกโขโฃ ๋ฅผ ๋ง์ณ ์ ์ฅ์์ ํ์ผ์ด ์ฌ๋ผ๊ฐ๊ณ
docs/๊ฐ ๋ง๋ค์ด์ง ๋ค์์ผ โค GitHub Pages ์ค์ ์ด ๊ฐ๋ฅํฉ๋๋ค. ๋น์ด ์๋ ์ ์ฅ์์์ Settings โ Pages ๋ก ๋จผ์ ๊ฐ๋ฉด "You must first add content to your repository" ๋ง ๋จ๊ณ Branch ๋๋กญ๋ค์ด์ดNoneํ๋๋ฟ์ด๋ผ/docs๋ฅผ ๊ณ ๋ฅผ ์ ์์ต๋๋ค.
์ด ์ ์ฅ์๋ฅผ Public ์ผ๋ก ํฌํฌํ๊ฑฐ๋, ์๋ก ๋ง๋ ๋ค ์ด ํด๋์ ๋ด์ฉ์ push ํฉ๋๋ค.
git init
git add .
git commit -m "init: ai-cve-watch"
git branch -M main
git remote add origin https://github.com/<์ฌ์ฉ์๋ช
>/<์ ์ฅ์๋ช
>.git
git push -u origin mainPublic ์ด์ด์ผ GitHub Actions ๊ฐ ๋ฌด์ ํ ๋ฌด๋ฃ์ ๋๋ค. Private ์ ์ 2,000๋ถ ์ ํ์ด ์์ต๋๋ค.
์ ์ฅ์ โ Settings โ Secrets and variables โ Actions โ New repository secret
| ์ด๋ฆ | ํ์ | ๊ฐ | ์ค๋ช |
|---|---|---|---|
SMTP_HOST |
โ | smtp.gmail.com |
๋ค์ด๋ฒ๋ smtp.naver.com |
SMTP_PORT |
โ | 587 |
SSL ์ ์ฐ๋ ค๋ฉด 465 |
SMTP_USER |
โ | your@gmail.com |
๋ณด๋ด๋ ๊ณ์ |
SMTP_PASS |
โ | ์ฑ ๋น๋ฐ๋ฒํธ 16์๋ฆฌ | ๊ณ์ ๋น๋ฐ๋ฒํธ ์๋ โ 3์ฅ |
MAIL_FROM |
โ | AI CVE Watch <your@gmail.com> |
๋ณด๋ด๋ ์ฌ๋ ํ์ |
NVD_API_KEY |
โฌ | NVD ๋ฐ๊ธ ํค | ์์ด๋ ๋์(์์ฒญ ๊ฐ 6.5์ด ๋๊ธฐ). ์์ผ๋ฉด 0.7์ด๋ก ๋จ์ถ |
GITHUB_TOKEN |
โ | ์๋ | Actions ๊ฐ ์๋ ์ฃผ์ . ์ง์ ๋ฑ๋ก ๋ถํ์ |
NVD_API_KEY๋ https://nvd.nist.gov/developers/request-an-api-key ์์ ๋ฌด๋ฃ๋ก ๋ฐ์ต๋๋ค. 55์ข ์ ๋งค์ผ ๋๋ ๋ฐ ํค๊ฐ ์์ผ๋ฉด ์ฝ 6๋ถ, ์์ผ๋ฉด ์ฝ 1๋ถ ๊ฑธ๋ฆฝ๋๋ค. ๋ฃ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
mail_list.txt ๋ฅผ ์ด์ด ๋ฐ์ ์ฌ๋์ ์ฃผ์๋ฅผ 1์ค์ 1๊ฐ์ฉ ์ ์ต๋๋ค.
security-team@example.com
sbom-admin@example.com
# ์์ #์ ๋ถ์ด๋ฉด ์ ์ ์ ์ธ๋ฉ๋๋ค
์ ์ฅ์ โ Actions โ daily-cve-collect โ Run workflow
- ์ฒซ ์คํ์
preset์backfill๋ก ๋๋ฉด ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๊น์ง ์ฑ์๋๋ค(์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฝ๋๋ค). - ๊ฐ๋ณ๊ฒ ํ์ธ๋ง ํ๋ ค๋ฉด
preset = urgent,sources = osv,kev๋ก ๋์ธ์ (2~3๋ถ).
์คํ์ด ๋๋๋ฉด output/CVE_YYYYMMDD.xls ๊ฐ ์ปค๋ฐ๋๊ณ , ๋ฉ์ผ์ด ๋ฐ์ก๋๊ณ , docs/ ํด๋๊ฐ ๋ง๋ค์ด์ง๋๋ค.
Code ํญ์์ docs ํด๋๊ฐ ๋ณด์ด๋์ง ํ์ธํ ๋ค โค๋ก ๋์ด๊ฐ์ธ์.
Actions ํญ์์ "Workflows aren't being run on this forked repository" ๋ฐฐ๋๊ฐ ๋ณด์ด๋ฉด
I understand my workflows, go ahead and enable them์ ๋๋ฌ ์ฃผ์ธ์(ํฌํฌํ ๊ฒฝ์ฐ์๋ง ๋์ต๋๋ค).
์ ์ฅ์ โ Settings โ Pages ๋ก ์ด๋ํด์
| ํญ๋ชฉ | ์ ํํ ๊ฐ |
|---|---|
| Source | Deploy from a branch |
| Branch | main โ โฃ๋ฅผ ๋ง์ณ์ผ ๋ชฉ๋ก์ ๋ํ๋ฉ๋๋ค |
| Folder | /docs โ โ
๋ฃจํธ๊ฐ ์๋๋ผ docs ์
๋๋ค. ๋ธ๋์น๋ฅผ ๊ณ ๋ฅด๋ฉด ์ค๋ฅธ์ชฝ์ ์๊น๋๋ค |
Save ๋ฅผ ๋๋ฅด๋ฉด 1~2๋ถ ๋ค https://<์ฌ์ฉ์๋ช
>.github.io/<์ ์ฅ์๋ช
> ์์ ์ด๋ ํ์ด์ง๊ฐ ์ด๋ฆฝ๋๋ค.
Branch ๋ชฉ๋ก์
main์ด ์๊ณNone๋ฟ์ด๋ผ๋ฉด ์์ง ์ ์ฅ์์ ์ฝ๋๊ฐ ์ ์ฌ๋ผ๊ฐ ๊ฒ์ ๋๋ค. โ ์ push ๊ฐ ๋๋ฌ๋์ง, Code ํญ์ ํ์ผ์ด ๋ณด์ด๋์ง ๋จผ์ ํ์ธํ์ธ์. ํด๋ ๋๋กญ๋ค์ด(/ (root),/docs)์ ๋ธ๋์น๋ฅผ ๋จผ์ ์ ํํด์ผ ๋ํ๋ฉ๋๋ค.
config/watchlist.yml ์ ํญ๋ชฉ์ ์ถ๊ฐํฉ๋๋ค.
- canonical_name: PyTorch # โ
๋ด๋ถ๋ง TB_SW.SW_NM ๊ณผ ๋ฐ๋์ ์ผ์น์ํฌ ๊ฒ
aliases: [torch, pytorch, "pytorch/pytorch"]
ecosystem: pypi # pypi / npm / github / other
pypi: torch # OSV ์กฐํ์ฉ (npm: / go: ๋ ๊ฐ๋ฅ)
github: pytorch/pytorch # GHSA ์กฐํยท์ฐธ๊ณ URL์ฉ
cpe_keyword: pytorch # NVD keywordSearch ๊ฒ์์ด
vendor: PyTorch Foundation # ์์
VENDOR ์ปฌ๋ผ ๊ธฐ๋ณธ๊ฐ
group: framework # framework/serving/library/app/ui/vectordb/mlops/base
enabled: true # false ๋ฉด ์์ง ๋์์์ ์์ ์ ์ธ
in_use: true # ๋ด๋ถ๋ง์ ์ค์ ๋ฐ์
๋ S/W ์ธ์ง
canonical_name์ ๋ด๋ถ๋งTB_SW.SW_NM๊ฐ๊ณผ ๊ธ์ ํ๋๊น์ง ๋๊ฐ์์ผ ํฉ๋๋ค.์์
SW_NAME์ปฌ๋ผ์๋ ์๋ณธ ํจํค์ง๋ช (torch)์ด ์๋๋ผ ์ดcanonical_name(PyTorch)์ด ๋ค์ด๊ฐ๋๋ค. ๋ด๋ถ๋ง ๊ด๋ฆฌ๋๊ตฌ๋ ์ด ๊ฐ์ผ๋ก ์์ฐ์ ๋งค์นญํ๊ธฐ ๋๋ฌธ์, ๋ด๋ถ๋ง์PyTorch๋ก ๋ฑ๋ก๋ผ ์๋๋ฐ ์ฌ๊ธฐ์Pytorch๋torch๋ก ์ ์ผ๋ฉด ์ทจ์ฝ์ ์ด ์ด๋ ์์ฐ์๋ ๋ถ์ง ์๊ณ ๊ทธ๋ฅ ์ฌ๋ผ์ง๋๋ค.์ S/W ๋ฅผ ์ถ๊ฐํ ๋๋ ๋จผ์ ๋ด๋ถ๋ง ๋ชฉ๋ก์์ ์ ํํ ์ด๋ฆ์ ํ์ธํ ๋ค ๊ทธ๋๋ก ์ฎ๊ฒจ ์ ์ผ์ธ์.
์ถ๊ฐ ํ ํ์ธ:
python -m src.aicve.main --show-scope --sw-names "์๋ก์ถ๊ฐํ์ด๋ฆ"
# ๋์ S/W 1์ข
: ์๋ก์ถ๊ฐํ์ด๋ฆ โ ์ด๋ ๊ฒ ๋์ค๋ฉด ์ ์in_use ๋ฅผ ์ ํํ ๊ด๋ฆฌํด ๋๋ฉด --only-in-use ๋ก ์ค์ ๋ฐ์
ํ S/W ๋ง ์ขํ ๋ณผ ์ ์์ต๋๋ค.
Gmail ์ 2023๋ ๋ถํฐ ์ผ๋ฐ ๋น๋ฐ๋ฒํธ๋ก SMTP ๋ก๊ทธ์ธ์ด ์ ๋ฉ๋๋ค. ์ฑ ๋น๋ฐ๋ฒํธ๊ฐ ํ์ํฉ๋๋ค.
- 2๋จ๊ณ ์ธ์ฆ์ ๋จผ์ ์ผญ๋๋ค โ https://myaccount.google.com/security
โ
2๋จ๊ณ ์ธ์ฆโ ์๋ด์ ๋ฐ๋ผ ์ค์ (์ฑ ๋น๋ฐ๋ฒํธ๋ 2๋จ๊ณ ์ธ์ฆ์ด ์ผ์ ธ ์์ด์ผ ๋์ต๋๋ค) - https://myaccount.google.com/apppasswords ์ ์
- ์ฑ ์ด๋ฆ์
ai-cve-watch์ ๋ ฅ โ ๋ง๋ค๊ธฐ abcd efgh ijkl mnopํํ์ 16์๋ฆฌ๊ฐ ๋น๋๋ค โ ๊ณต๋ฐฑ์ ๋นผ๊ณabcdefghijklmnop๋ก ๋ณต์ฌ- GitHub Secrets ์
SMTP_PASS์ ๋ถ์ฌ๋ฃ๊ธฐ (์ด ํ๋ฉด์ ๋ซ์ผ๋ฉด ๋ค์ ๋ณผ ์ ์์ต๋๋ค)
๋ค์ด๋ฒ ๋ฉ์ผ์ ์ด๋ค๋ฉด: ๋ค์ด๋ฒ ๋ฉ์ผ โ ํ๊ฒฝ์ค์ โ POP3/IMAP ์ค์ โ IMAP/SMTP ์ฌ์ฉํจ,
๊ทธ๋ฆฌ๊ณ SMTP_HOST=smtp.naver.com, SMTP_PORT=587, SMTP_USER ๋ ์์ด๋(@naver.com ํฌํจ).
GitHub ๊ท์น: Public ์ ์ฅ์์
schedule์ํฌํ๋ก๋ 60์ผ๊ฐ ์ ์ฅ์์ ํ๋(์ปค๋ฐ)์ด ์์ผ๋ฉด ์๋์ผ๋ก ๋นํ์ฑํ๋ฉ๋๋ค.
์ด ์ ์ฅ์๋ ๋๊ฐ ๋ฌธ์ ์์ต๋๋ค. ๋งค์ผ ์คํ๋ ๋๋ง๋ค data/cve.dbยทoutput/ยทdocs/ ๋ฅผ
์๋ ์ปค๋ฐํ๋ฏ๋ก ํ๋์ด ๊ณ์ ๊ธฐ๋ก๋๊ธฐ ๋๋ฌธ์
๋๋ค.
๋ค๋ง ์๋ ๊ฒฝ์ฐ์๋ ๋ฉ์ถ ์ ์์ต๋๋ค.
- ์ค๋ซ๋์ ์ ๊ท CVE๊ฐ 0๊ฑด์ด๋ผ ์ปค๋ฐํ ๋ณ๊ฒฝ์ด ์์๋ ๊ฒฝ์ฐ
- ์ํฌํ๋ก๊ฐ ๊ณ์ ์คํจํด์ ์ปค๋ฐ ๋จ๊ณ๊น์ง ๋ชป ๊ฐ๋ ๊ฒฝ์ฐ
- ์ ์ฅ์ โ Actions ํญ โ ๋ ธ๋ ๋ฐฐ๋ "This scheduled workflow is disabled because there hasn't been activity in this repository for 60 days"
- ๋ฐฐ๋์
Enable workflow๋ฒํผ ํด๋ฆญ โ ์ฆ์ ์ฌ๊ฐ๋ฉ๋๋ค. - ๋ฐฐ๋๊ฐ ์๋ค๋ฉด ์ผ์ชฝ ๋ชฉ๋ก์์
daily-cve-collect์ ํ โ ์ค๋ฅธ์ชฝEnable workflow
- ์ํฌํ๋ก์
workflow_dispatch(์๋ ์คํ) ํธ๋ฆฌ๊ฑฐ๊ฐ ์ด๋ฏธ ํฌํจ๋์ด ์์ต๋๋ค. ํ ๋ฌ์ ํ ๋ฒ์ฏค Run workflow ๋ฅผ ๋๋ฌ ์ฃผ๊ธฐ๋ง ํด๋ ํ๋์ผ๋ก ์ธ์ ๋ฉ๋๋ค. - ์๋ฆผ์ ๋ฐ์ผ๋ ค๋ฉด: ์ ์ฅ์ โ Watch โ Custom โ Actions ์ฒดํฌ
(์ํฌํ๋ก ์คํจ ์ ๋ฉ์ผ์ด ์ต๋๋ค. ์ข
๋ฃ ์ฝ๋
1๋ก ์คํจ๋ฅผ ์๋ฆฌ๋๋ก ๋ง๋ค์ด์ ธ ์์ต๋๋ค.)
์ด ์ฅ์ ๋ด๋ถ๋ง ๊ด๋ฆฌ๋๊ตฌ ๋ด๋น์์๊ฒ ๊ทธ๋๋ก ์ ๋ฌํ์ธ์. ์ด ๊ท๊ฒฉ์ ๊ณ ์ ์ด๋ฉฐ, ๋ณ๊ฒฝํ๋ ค๋ฉด ์์ชฝ(์์ง๊ธฐยท๋ด๋ถ๋ง ํ์)์ ๋์์ ๊ณ ์ณ์ผ ํฉ๋๋ค.
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| ํ์ผ๋ช | CVE_YYYYMMDD.xls โ ๋ฐ๋์ ๋๋ฌธ์ CVE_ ๋ก ์์ (๋ด๋ถ๋ง ๋๊ตฌ๊ฐ ์ ๋์ฌ๋ฅผ ๊ฒ์ฆ) |
| ํฌ๋งท | BIFF8 .xls (xlwt ์์ฑ) โ ์ด๊ฒ์ด ์ ๋ณธ |
| ๋ถ๊ฐ ํ์ผ | ๊ฐ์ ๋ด์ฉ์ .xlsx(openpyxl), .csv(UTF-8 BOM). ๋ฐ์
์ฉ์ ์ด๋๊น์ง๋ .xls |
| ์ํธ1 | CVE_LIST โ 1ํ ํค๋, 2ํ๋ถํฐ ๋ฐ์ดํฐ |
| ์ํธ2 | META โ KEY | VALUE 2์ด |
| # | ํค๋ | ๊ฐ ๊ท์น |
|---|---|---|
| 1 | CVE_ID |
CVE-YYYY-NNNN |
| 2 | SW_NAME |
watchlist ์ canonical_name (= ๋ด๋ถ๋ง TB_SW.SW_NM) |
| 3 | VENDOR |
์ ์์ฌ/์กฐ์ง |
| 4 | AFFECTED_RANGE |
์๋ ๋ฒ์ ๋ฒ์ ๋ฌธ๋ฒ, ๊ณต๋ฐฑ ์์ |
| 5 | FIXED_VERSION |
์์ผ๋ฉด ๋น ๋ฌธ์์ด |
| 6 | SEVERITY |
CRITICAL / HIGH / MEDIUM / LOW / NONE |
| 7 | CVSS_SCORE |
0.0~10.0, ๋ฌธ์์ด ์
|
| 8 | CVSS_VECTOR |
์: CVSS:3.1/AV:N/AC:L/... |
| 9 | PUBLISHED_DATE |
YYYYMMDD ๋ฌธ์์ด ์
|
| 10 | MODIFIED_DATE |
YYYYMMDD ๋ฌธ์์ด ์
|
| 11 | KEV_YN |
Y / N (CISA ์ค์ ์
์ฉ ํ์ธ ์ฌ๋ถ) |
| 12 | SUMMARY |
1000์ ์ด๋ด, ๊ฐํยทํญ์ ๊ณต๋ฐฑ์ผ๋ก ์นํ๋จ. ๊ธฐ๋ณธ๊ฐ์ ํ๊ตญ์ด ๋ฒ์ญ๋ฌธ (11์ฅ) |
| 13 | REFERENCE_URL |
๋ํ URL 1๊ฐ |
| 14 | SOURCE |
NVD / OSV / GHSA (๋ณํฉ ์ NVD+OSV) |
| 15 | ECOSYSTEM |
pypi / npm / github / other |
| 16 | COLLECTED_AT |
YYYYMMDDHHmmss ๋ฌธ์์ด ์
|
์ซ์ํ์ผ๋ก ์ฐ๋ฉด ๋ฒ์ 1.10 โ 1.1, ๋ ์ง โ ์๋ฆฌ์ผ๊ฐ(45123)์ผ๋ก ๊นจ์ ธ POI ํ์ฑ์์ ์ค๋ฅ๊ฐ ๋ฉ๋๋ค.
๊ทธ๋์ ์ ์ยท๋ ์ง๋ฅผ ํฌํจํ ๋ชจ๋ ์
์ ๋ฌธ์์ด๋ก ๊ธฐ๋กํฉ๋๋ค.
None/NaN ์ ๋น ๋ฌธ์์ด("")์
๋๋ค. ์ฝ๋ ์ชฝ์์๋ ๋ฌธ์์ด๋ก ๋ฐ์ ์ฃผ์ธ์.
, = AND | = OR ์ฐ์ฐ์: >= > <= < == ์ ์ฒด ์ํฅ = *
">=1.0.0,<1.4.2" 1.0.0 ์ด์ 1.4.2 ๋ฏธ๋ง
">=2.0.0,<2.3.1|>=3.0.0,<3.0.4" ๋ ๊ตฌ๊ฐ ์ค ํ๋๋ผ๋ ํด๋น๋๋ฉด ์ทจ์ฝ
"==1.2.3" ์ ํํ ๊ทธ ๋ฒ์ ๋ง
"<2.6.0" 2.6.0 ๋ฏธ๋ง ์ ๋ถ (ํํ์ด 0์ด๋ฉด ์๋ต)
"*" ์ ๋ฒ์ ์ํฅ ๋๋ ๋ฒ์ ๋ถ๋ช
- ๋ฌธ์์ด์ ๊ณต๋ฐฑ์ด ์์ต๋๋ค. ๊ธธ์ด๊ฐ 500์๋ฅผ ๋์ผ๋ฉด ๋ํ ๋ฒ์๋ง ๋จ๊ธฐ๊ณ ์๋ฆ ๋๋ค.
- ๋ฒ์๋ฅผ ์์๋ผ ์ ์์ผ๋ฉด
*๋ก ์ฐ๊ณ ,SUMMARY์์[๋ฒ์ ๋ฒ์ ๋ถ๋ช ํ]์ ๋ถ์ ๋๋ค. โ ์ด ์ ๋์ฌ๊ฐ ๋ณด์ด๋ฉด ๋ด๋น์๊ฐ ์๋ ํ์ธ์ด ํ์ํ๋ค๋ ๋ป์ ๋๋ค.
SEVERITY(CRITICALโNONE) โ CVSS_SCORE ๋ด๋ฆผ์ฐจ์ โ SW_NAME โ CVE_ID
RUN_ID, COLLECTED_AT, SCOPE_DESC, DATE_FROM, DATE_TO, MIN_SEVERITY, GROUPS,
SW_NAMES, ONLY_IN_USE, SOURCES, EXCEL_SCOPE, MAX_ROWS, TRUNCATED_CNT,
TOTAL_CNT, NEW_CNT, UPDATED_CNT, TOOL_VERSION
SCOPE_DESC์ ์ด ํ์ผ์ด ์ด๋ค ์กฐ๊ฑด์ผ๋ก ๋ฝํ๋์ง ํ ์ค๋ก ๋ค์ด ์์ต๋๋ค. ์:SCOPE lookback=3 sev>=MEDIUM groups=all sw=- in_use=False src=nvd,osv,ghsa,kev scope=delta max=300TRUNCATED_CNT๊ฐ 0๋ณด๋ค ํฌ๋ฉด ์ต๋ ํ์ ์ ํ์ผ๋ก ์๋ ค ๋๊ฐ ๊ฑด์ด ์๋ค๋ ๋ป์ ๋๋ค. ์ ์ฒด ๋ชฉ๋ก์ GitHub Pages ์ด๋ ํ์ด์ง์์ ํ์ธํ์ธ์.
xlwt(BIFF8)๋ ์ํธ๋น 65,536ํ์ด ํ๊ณ๋ผ, ์ด๊ณผ ์ CVE_LIST, CVE_LIST_2, CVE_LIST_3 โฆ
์ผ๋ก ๋๋๊ณ META ์ SHEET_COUNTยทSHEET_NAMES ๋ฅผ ์ ์ต๋๋ค.
(๊ธฐ๋ณธ max_rows: 300 ์ด๋ผ ์ค์ ๋ก๋ ๊ฑฐ์ ๋ฐ์ํ์ง ์์ต๋๋ค.)
| ํญ๋ชฉ | ํ๊ณ | ์ค์ ์ฌ์ |
|---|---|---|
| GitHub Actions | Public ๋ฌด์ ํ / Private ์ 2,000๋ถ | ํ๋ฃจ 5~10๋ถ โ Public ์ด๋ฉด ๊ฑฑ์ ์์ |
| ์ ์ฅ์ ์ฉ๋ | ๊ถ์ฅ 1GB, ๊ฒฝ๊ณ 5GB | CVE 1๊ฑด โ 1KB โ 10๋ง ๊ฑด์ด์ด๋ 100MB ์์ค |
| SQLite ํ์ผ ์ปค๋ฐ | ๋งค์ผ ๋ฐ์ด๋๋ฆฌ 1๊ฐ๊ฐ ์ปค๋ฐ๋์ด ํ์คํ ๋ฆฌ๊ฐ ์์ | ํ๋ฃจ ์์ญ~์๋ฐฑKB. ์๋ ์ ๋ฒํ๋๋ค |
| GitHub Pages | ์ ์ฅ์ 1GB, ์ 100GB ์ ์ก | ์ ์ HTML ์ด๋ผ ์ฌ์ |
| NVD API | ํค ์์ด 30์ด๋น 5์์ฒญ | 55์ข ร 1์์ฒญ โ 6๋ถ. ํค ์์ผ๋ฉด 1๋ถ |
| Gmail SMTP | ํ๋ฃจ 500ํต | ์์ ์ ์ ร 1ํต |
- CVE ๋์ ์ด 10๋ง ๊ฑด์ ๋์ด
data/cve.db๊ฐ ์๋ฐฑ MB๊ฐ ๋ ๋ - ์ ์ฅ์ ํ์คํ ๋ฆฌ ์ฉ๋์ด ๋ถ๋ด๋ ๋ (๋งค์ผ ์ปค๋ฐ๋๋ ๋ฐ์ด๋๋ฆฌ๋ผ ํ์คํ ๋ฆฌ๊ฐ ๋์ ๋ฉ๋๋ค)
- ์ฌ๋ฌ ์์คํ ์์ ๋์์ DB๋ฅผ ์ฝ๊ณ ์จ์ผ ํ ๋
store.py ๋ Store ์ถ์ ํด๋์ค + SqliteStore ๊ตฌํ์ผ๋ก ๋๋์ด ์์ด
ํ์ผ 1๊ฐ๋ง ์ถ๊ฐํ๋ฉด ๊ต์ฒด๋ฉ๋๋ค.
- https://supabase.com ๊ฐ์ โ ์ ํ๋ก์ ํธ โ Settings โ Database โ Connection string ๋ณต์ฌ (๋ฌด๋ฃ ํฐ์ด: 500MB DB, ํ๋ก์ ํธ 2๊ฐ)
requirements.txt์psycopg2-binary==2.9.9์ถ๊ฐsrc/aicve/store_supabase.py๋ฅผ ๋ง๋ค๊ณStore๋ฅผ ์์ํด ์๋ ๋ฉ์๋๋ฅผ ๊ตฌํํฉ๋๋ค.SQL ๋ฌธ๋ฒ ์ฐจ์ด๋ ์ธ ๊ตฐ๋ฐ๋ฟ์ ๋๋ค:from .store import Store class SupabaseStore(Store): def init_schema(self): ... # store.py ์ SCHEMA ๋ฅผ Postgres ๋ฌธ๋ฒ์ผ๋ก def start_run(self, run_id, scope): ... def finish_run(self, run_id, **fields): ... def upsert_findings(self, findings, run_id): ... # ON CONFLICT DO UPDATE def fetch_run_cves(self, run_id): ... def fetch_all_cves(self, limit=None): ... def fetch_runs(self, limit=30): ... def log_mail(self, run_id, recipient, subject, status, error_msg="", attach_file=""): ... def fetch_mail_logs(self, run_id=None): ... def close(self): ...
INTEGER PRIMARY KEY AUTOINCREMENTโSERIAL PRIMARY KEY,INSERT OR REPLACEโINSERT ... ON CONFLICT (cve_id, sw_name) DO UPDATE SET ..., ํ๋ ์ด์คํ๋?โ%s.main.py์์ ์์ฑ ๋ถ๋ถ ํ ์ค๋ง ๋ฐ๊ฟ๋๋ค.# store = SqliteStore(args.db) store = SupabaseStore(os.environ["DATABASE_URL"])
DATABASE_URL์ GitHub Secrets ์ ๋ฑ๋กํ๊ณ , ์ํฌํ๋ก์ ์ปค๋ฐ ๋จ๊ณ์์data/๋ฅผ ์ ์ธํฉ๋๋ค.
site.py๋ ํต๊ณ์ฉ ๋ฉ์๋(severity_countsยทsw_countsยทmail_stat_by_runยทtotal_cve_count)๋ ์๋๋ค. ํจ๊ป ์ฎ๊ธฐ๊ฑฐ๋, ์ฐ์SqliteStore๋ฅผ ์์ํด ํ์ํ ๊ฒ๋ง ๋ฎ์ด์จ๋ ๋ฉ๋๋ค.
์ ๋ ์์ง์ NVD ๋ ์ดํธ ๋ฆฌ๋ฐ ๋๋ฌธ์ ๋๋ฆฌ๊ณ , ์์ ์ด ์๋ฐฑ ํ์ด ๋๋ฉด ๋ด๋ถ๋ง์์ ๊ฒํ ๊ฐ ๋ฒ๊ฒ์ต๋๋ค. ๊ทธ๋์ 9๊ฐ ์ถ์ผ๋ก ๋ฒ์๋ฅผ ์ขํ ์ ์์ต๋๋ค.
| # | ์ถ | ํค / CLI | ๊ธฐ๋ณธ๊ฐ | ์ค๋ช |
|---|---|---|---|---|
| 1 | ๊ธฐ๊ฐ | lookback_days / --lookback |
3 |
์ต๊ทผ N์ผ |
| 2 | ์ ๋ ๊ตฌ๊ฐ | date_fromยทdate_to / --fromยท--to |
์์ | ์ง์ ์ โ ์ ๋ฌด์ |
| 3 | ์ฌ๊ฐ๋ ํํ | min_severity / --min-severity |
MEDIUM |
ALL ์ด๋ฉด ํํฐ ์ ํจ |
| 4 | ๋์ ๊ทธ๋ฃน | groups / --groups |
all |
framework,serving,ui โฆ |
| 5 | ๋์ S/W | sw_names / --sw-names |
์์ | ์ง์ ์ โฃโฅ์ ๋ฌด์ |
| 6 | ๋ฐ์ ๋ถ ํ์ | only_in_use / --only-in-use |
false |
in_use: true ๋ง |
| 7 | ์์ค | sources / --sources |
nvd,osv,ghsa,kev |
์ผ๋ถ๋ง ์ ํ ๊ฐ๋ฅ |
| 8 | ๊ฒฐ๊ณผ ๋ฒ์ | excel_scope / --excel-scope |
delta |
delta=์ ๊ทยท๋ณ๊ฒฝ๋ง / all=์ ๋ |
| 9 | ์ต๋ ํ์ | max_rows / --max-rows |
300 |
์ด๊ณผ๋ถ์ ์ฌ๊ฐ๋ยท์ ์ ์์ผ๋ก ์๋ผ๋ |
์ฐ์ ์์: settings.yml ๊ธฐ๋ณธ๊ฐ โ preset โ CLI/Actions ์
๋ ฅ (๋ค๊ฐ ์์ ๋ฎ์ด์).
๋น ๊ฐ("")์ ์ ๋จ๊ณ๋ฅผ ๋ฎ์ด์ฐ์ง ์์ต๋๋ค.
- KEV(์ค์ ์ ์ฉ ํ์ธ) ๋ฑ์ฌ ๊ฑด์ ์ฌ๊ฐ๋ ํํ๊ณผ ๋ฌด๊ดํ๊ฒ ํญ์ ํฌํจ๋ฉ๋๋ค.
sw_names๋ฅผ ์ฃผ๋ฉดgroupsยทonly_in_use๋ ๋ฌด์ํฉ๋๋ค(๊ฐ์ฅ ์ข์ ์กฐ๊ฑด์ด ์ด๊น๋๋ค).- ๋์์ด 0๊ฑด์ด๋ฉด ์ฆ์ ์ข
๋ฃํ๊ณ
run_log.status='SKIPPED'์ ์ฌ์ ๋ฅผ ๋จ๊น๋๋ค. - ํ์ ๋ ์กฐ๊ฑด์ ์คํ ๋ก๊ทธ ์ฒซ ์ค,
run_log.scope_desc, ์์ META, ๋ฉ์ผ ๋ณธ๋ฌธ ์๋จ์ ๋๊ฐ์ ํ ์ค๋ก ์ค๋ฆฝ๋๋ค.SCOPE lookback=3 sev>=HIGH groups=serving,ui sw=- in_use=False src=osv,kev scope=delta max=300
| ์ด๋ฆ | ์กฐํฉ | ์ฐ์์ |
|---|---|---|
daily |
3์ผ / MEDIUM ์ด์ / delta / 300ํ | ๊ธฐ๋ณธ ์ผ์ผ ์์ง |
urgent |
1์ผ / HIGH ์ด์ / delta / 50ํ | ๊ธํ ๋ ๋น ๋ฅด๊ฒ |
in_use |
7์ผ / LOW ์ด์ / ๋ฐ์ ๋ถ๋ง / 200ํ | ๋ด๋ถ๋ง ๋ฐ์ S/W ์ง์ค ์ ๊ฒ |
monthly |
30์ผ / HIGH ์ด์ / ์ ๋ / 1000ํ | ์๊ฐ ๋ณด๊ณ ์ฉ |
backfill |
2026-01-01~ / HIGH ์ด์ / ์ ๋ / 5000ํ | ์ต์ด 1ํ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ ์ฑ์ฐ๊ธฐ |
# ๋ก์ปฌ ์คํ ์ค๋น
python -m venv .venv && .venv/Scripts/activate # Linux/Mac: source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # ๊ฐ์ ์ฑ์ฐ๊ณ ํ๊ฒฝ๋ณ์๋ก export# ์กฐ๊ฑด๋ง ํ์ธ (API ํธ์ถ ์์) โ ์ค์ ๋ฐฉ์ง์ฉ์ผ๋ก ํญ์ ๋จผ์ ๋๋ ค ๋ณด์ธ์
python -m src.aicve.main --show-scope --preset urgent --groups serving,ui
# ๊ธฐ๋ณธ ์ผ์ผ ์์ง
python -m src.aicve.main --preset daily
# ํน์ S/W๋ง, ๋ฉ์ผ ์์ด
python -m src.aicve.main --sw-names "PyTorch,vLLM" --skip-mail
# ๋ฉ์ผ ๋ฐ์ก ์์ด ๋ณธ๋ฌธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ง ์ ์ฅ (output/mail_preview_*.html)
python -m src.aicve.main --preset daily --dry-run
# ๊ณผ๊ฑฐ ๊ตฌ๊ฐ ๋ฐฑํ (์ ๋ ์์
)
python -m src.aicve.main --from 2026-01-01 --to 2026-06-30 --excel-scope all --max-rows 5000
# ์ฒซ ์คํ 30์ผ์น
python -m src.aicve.main --backfill 30--preset {daily|urgent|in_use|monthly|backfill}
--lookback N --backfill N --from YYYY-MM-DD --to YYYY-MM-DD
--min-severity {CRITICAL|HIGH|MEDIUM|LOW|ALL}
--groups framework,serving --sw-names PyTorch,vLLM --only-in-use
--sources nvd,osv,ghsa,kev --excel-scope {delta|all} --max-rows N
--skip-mail --dry-run --show-scope --skip-site
--config / --watchlist / --db / --out-dir / --docs-dir / --templates / --mail-list
"๋ฉ์ผ์ด ์ค์ ๋ก ๋๊ฐ๋", "์ง๋์ฃผ์ ๋ญ ๋ณด๋๋" ๋ฅผ ํ์ธํ๋ ๋๊ตฌ์ ๋๋ค. ์ฝ๊ธฐ ์ ์ฉ์ด๋ผ ์์ ํฉ๋๋ค.
python -m src.aicve.report # ์ต๊ทผ ์คํ + ๋ฉ์ผ ์ฑ๊ณต/์คํจ ์์ฝ
python -m src.aicve.report --mails # ๋ฉ์ผ ๋ฐ์ก ๋ก๊ทธ ์ ์ฒด (์์ ์ยท์๊ฐยท์ํ)
python -m src.aicve.report --run 20260816211121 # ํน์ ํ์ฐจ ์์ธ (CVE ๋ชฉ๋ก + ๋ฐ์ก ๋ก๊ทธ)
python -m src.aicve.report --cves # ๋์ CVE ๋ชฉ๋ก
python -m src.aicve.report --stats # ์ฌ๊ฐ๋ยทS/W๋ณ ํต๊ณ
python -m src.aicve.report --sql "SELECT * FROM mail_log WHERE status='FAILED'"์ต์ DB ๋ฅผ ๋ณด๋ ค๋ฉด ์คํ ์ ์ git pull ๋ก ์ ์ฅ์๋ฅผ ๋ฐ์ ๋์ธ์(์ํฌํ๋ก๊ฐ ๋งค์ผ data/cve.db ๋ฅผ ์ปค๋ฐํฉ๋๋ค).
๊ฐ์ ๋ด์ฉ์ ์ด๋ ํ์ด์ง ์์๋ ๋ณผ ์ ์์ต๋๋ค. ์ค์น ์์ด ๋ณด๋ ค๋ฉด ๋์๋ณด๋ ์คํ ์ด๋ ฅ ํ์
๋ฉ์ผ(์ฑ๊ณต/์คํจ)์นธ๊ณผ๋ณด๊ธฐ๋งํฌ๋ฅผ ์ฐ์ธ์. GUI ๋ก DB ๋ฅผ ์ง์ ์ด์ด ๋ณด๊ณ ์ถ์ผ๋ฉด ๋ฌด๋ฃ ํ๋ก๊ทธ๋จ DB Browser for SQLite ์์data/cve.db๋ฅผ ์ด๋ฉด ๋ฉ๋๋ค.
python -m src.aicve.sources.kev
python -m src.aicve.sources.osv --sw-names "vLLM" --lookback 90
python -m src.aicve.sources.nvd --sw-names "PyTorch" --lookback 30
python -m src.aicve.sources.ghsa --groups ui --lookback 30 # GITHUB_TOKEN ํ์
python -m src.aicve.site # ์์ง ์์ด docs/ ๋ง ์ฌ์์ฑpython -m pytest tests/ -qtests/test_version_range.py ๋ ๋ฒ์ ๋ฒ์ ๋ณํ์ ์๋ฐฉํฅ์ผ๋ก ๊ฒ์ฆํฉ๋๋ค.
๋ฌธ์์ด ์์ฑ๋ฟ ์๋๋ผ "์ด ๋ฒ์์ 1.4.1 ์ ํฌํจ๋๊ณ 1.4.2 ๋ ๋น ์ง๋๊ฐ"๊น์ง ํ์ธํด,
ํ๊ธฐ๋ง ๋ง๊ณ ์๋ฏธ๊ฐ ํ๋ฆฌ๋ ์ฌ๊ณ ๋ฅผ ๋ง์ต๋๋ค.
| ์ฝ๋ | ์๋ฏธ |
|---|---|
0 |
SUCCESS / PARTIAL(์ผ๋ถ ์์ค ์คํจ) / SKIPPED(๋์ 0๊ฑด) |
1 |
FAILED โ Actions ๊ฐ ์คํจ ์๋ฆผ์ ๋ณด๋
๋๋ค |
์ด๋ค ๋จ๊ณ๊ฐ ์คํจํด๋ ๋๋จธ์ง๋ ์งํํฉ๋๋ค. ์์ค ํ๋๊ฐ ์ฃฝ์ผ๋ฉด ๋๋จธ์ง ์์ค๋ก ๊ณ์ํ๊ณ , ๋ฉ์ผ์ด ์คํจํด๋ ์์ ยทDBยทํ์ด์ง๋ ์ ์ ์์ฑ๋ฉ๋๋ค.
ai-cve-watch/
โโโ .github/workflows/daily.yml ๋งค์ผ 06:00 KST ์๋ ์คํ + ์๋ ์คํ
โโโ config/
โ โโโ watchlist.yml โ
๊ฐ์ ๋์ 55์ข
(ํต์ฌ ์ค์ )
โ โโโ settings.yml ๊ธฐ๋ณธ๊ฐ + ํ๋ฆฌ์
+ ์ฌ์๋ ์ค์
โโโ mail_list.txt ์์ ์ (1์ค 1๊ฐ, '#' ์ฃผ์ ํ์ฉ)
โโโ src/aicve/
โ โโโ main.py ์ค์ผ์คํธ๋ ์ด์
์ง์
์ (CLI)
โ โโโ scope.py โ
์์ง ๋ฒ์ ํ์ (settings โ preset โ CLI)
โ โโโ sources/
โ โ โโโ base.py ์ฌ์๋ยท๋ฐฑ์คํ๊ฐ ๋ถ์ HTTP ํด๋ผ์ด์ธํธ
โ โ โโโ nvd.py NVD CVE API 2.0 (CVSSยท์์ฝ์ด ์ ํ)
โ โ โโโ osv.py OSV.dev (โ
๋ฒ์ ๋ฒ์๊ฐ ๊ฐ์ฅ ์ ํ)
โ โ โโโ ghsa.py GitHub Advisory GraphQL
โ โ โโโ kev.py CISA ์ค์ ์
์ฉ ๋ชฉ๋ก โ KEV_YN
โ โโโ normalize.py โ
๊ณตํต ์คํค๋ง + ๋ฒ์ ๋ฒ์ ๋ณํ + CVSS ๊ณ์ฐ
โ โโโ excel.py โ
CVE_YYYYMMDD.xls / .xlsx / .csv
โ โโโ mailer.py SMTP ๊ฐ๋ณ ๋ฐ์ก + ๊ฒฐ๊ณผ ๊ธฐ๋ก
โ โโโ store.py Store ์ถ์ + SqliteStore (Supabase ๊ต์ฒด ์ง์ )
โ โโโ site.py Jinja2 โ docs/ ์ ์ ํ์ด์ง
โ โโโ logutil.py ๋ก๊น
+ ๋น๋ฐ์ ๋ณด ๋ง์คํน
โโโ templates/ base / index / run / cve / mail (.html.j2)
โโโ data/cve.db SQLite (์ปค๋ฐ ๋์)
โโโ output/ CVE_YYYYMMDD.xls (์ปค๋ฐ ๋์, 90์ผ ๋ณด๊ด)
โโโ docs/ GitHub Pages ์ฐ์ถ๋ฌผ (์ปค๋ฐ ๋์)
โโโ tests/ pytest
๋์ผ (CVE_ID, SW_NAME) ์ 1ํ์ผ๋ก ํฉ์นฉ๋๋ค.
| ํ๋ | ์ฐ์ ์์ | ์ด์ |
|---|---|---|
| ๋ฒ์ ๋ฒ์ ยท ์กฐ์น๋ฒ์ | OSV > GHSA > NVD | OSV ๊ฐ ํจํค์ง ๋ฒ์ ์ ๋ณด๋ฅผ ๊ฐ์ฅ ์ ํํ ๊ด๋ฆฌ |
| CVSS ยท ์์ฝ ยท ์ฐธ๊ณ URL | NVD > GHSA > OSV | NVD ๊ฐ ๊ณต์ ์ ์ยท์ค๋ช ์ ์ ๊ณต |
SEVERITY ๋ CVSS v3.1 ์ ์ ๊ธฐ์ค(9.0โ CRITICAL / 7.0โ HIGH / 4.0โ MEDIUM / 0.1โ LOW)์ด๋ฉฐ,
์ ์๊ฐ ์์ผ๋ฉด ๋ฒกํฐ์์ ์ง์ ๊ณ์ฐํ๊ณ , ๊ทธ๊ฒ๋ ์์ผ๋ฉด ์์ค๊ฐ ์ค ๋ฑ๊ธ ๋ฌธ์์ด์ ์๋๋ค.
content_hash = sha256(affected_range|fixed_version|severity|cvss_score|summary)
โ ๊ธฐ์กด ํ๊ณผ ๋ค๋ฅด๋ฉด ๋ณ๊ฒฝ(updated), ํ ์์ฒด๊ฐ ์์ผ๋ฉด ์ ๊ท(new).
์์
ยท๋ฉ์ผ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฒ ์คํ์ ์ ๊ทยท๋ณ๊ฒฝ ๊ฑด๋ง ๋ด๊น๋๋ค(excel_scope: delta).
| ์ฆ์ | ์์ธ๊ณผ ํด๊ฒฐ |
|---|---|
| ๋ฉ์ผ์ด ์ ์ด | โ ์ ๊ท 0๊ฑด์ด๋ฉด ๋ฐ์กํ์ง ์์ต๋๋ค(์ ์). settings.yml: send_when_empty: true ๋ก ๋ฐ๊พธ๋ฉด ํญ์ ๋ฐ์ก โก SMTP_PASS ์ ์ฑ ๋น๋ฐ๋ฒํธ ๋์ ๊ณ์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฃ์ง ์์๋์ง ํ์ธ โข ์คํ ์์ธ ํ์ด์ง์ ๋ฉ์ผ ๋ฐ์ก ๋ก๊ทธ ์์ ์คํจ ์ฌ์ ํ์ธ |
| Pages ๊ฐ 404 | Settings โ Pages ์ ํด๋๊ฐ /docs ์ธ์ง, docs/index.html ์ด ์ปค๋ฐ๋๋์ง ํ์ธ |
Pages ์ค์ ์์ /docs ๋ฅผ ๋ชป ๊ณ ๋ฆ |
Branch ๊ฐ None ๋ฟ์ด๋ฉด ์ ์ฅ์๊ฐ ๋น์ด ์๋ ๊ฒ์
๋๋ค. ์ฝ๋๋ฅผ push ํ๊ณ ์ํฌํ๋ก๋ฅผ 1ํ ๋๋ ค docs/ ๋ฅผ ๋ง๋ ๋ค ๋ค์ ์ค์ธ์ (1์ฅ โฃโค) |
| NVD ์์ง์ด ๋งค์ฐ ๋๋ฆผ | ์ ์์
๋๋ค(ํค ์์ผ๋ฉด ์์ฒญ ๊ฐ 6.5์ด). NVD_API_KEY ๋ฅผ ๋ฑ๋กํ๋ฉด 6๋ฐฐ ๋นจ๋ผ์ง๋๋ค |
| GHSA ๊ฐ ํญ์ 0๊ฑด | GITHUB_TOKEN ์ด ์์ผ๋ฉด ์ด ์์ค๋ง ๊ฑด๋๋๋๋ค. ๋ก์ปฌ ์คํ ์์๋ PAT ๋ฅผ ์ง์ ๋ฃ์ด์ผ ํฉ๋๋ค |
| ์ํฌํ๋ก๊ฐ ์ ๋์๊ฐ | 4์ฅ 60์ผ ์๋ ๋นํ์ฑํ ์ฐธ๊ณ |
| ์์ ํ์ด ์๋ฆผ | META ์ TRUNCATED_CNT ํ์ธ โ --max-rows ๋ฅผ ์ฌ๋ฆฌ๊ฑฐ๋ ์กฐ๊ฑด์ ์ขํ์ธ์ |
SUMMARY ์์ [๋ฒ์ ๋ฒ์ ๋ถ๋ช
ํ] |
์ด๋ ์์ค๋ ๋ฒ์ ๋ฒ์๋ฅผ ์ฃผ์ง ์์ * ๋ก ๊ธฐ๋ก๋ ๊ฑด์
๋๋ค. ์๋ ํ์ธ์ด ํ์ํฉ๋๋ค |
| ์ปค๋ฐ ์ถฉ๋๋ก ํธ์ ์คํจ | ์ํฌํ๋ก๊ฐ pull --rebase ๋ก 3ํ ์ฌ์๋ํฉ๋๋ค. ๊ณ์ ์คํจํ๋ฉด ์๋ ์คํ์ผ๋ก ์ฌ์๋ํ์ธ์ |
| ๋ก์ปฌ์์ ํ๊ธ์ด ๊นจ์ง | Windows ์ฝ์์ด๋ฉด set PYTHONIOENCODING=utf-8 ํ ์คํ |
- Actions ๋ก๊ทธ: ์ ์ฅ์ โ Actions โ ํด๋น ์คํ โ
์์ง ์คํ์คํ - ํ์ผ ๋ก๊ทธ: ์คํ ์ฐ์ถ๋ฌผ(artifact)
cve-output-*์์logs/run_*.log(30์ผ ๋ณด๊ด) - ๋น๋ฐ๋ฒํธยทํ ํฐยท๋ฉ์ผ ์ฃผ์๋ ๊ธฐ๋ก ์ง์ ์ ์๋ ๋ง์คํน๋๋ฏ๋ก ๋ก๊ทธ๊ฐ ๊ณต๊ฐ๋ผ๋ ์์ ํฉ๋๋ค.
์ทจ์ฝ์ ์ค๋ช
(SUMMARY)์ ์๋ ์๋ฌธ์
๋๋ค. ๊ธฐ๋ณธ์ผ๋ก ํ๊ตญ์ด ๋ฒ์ญ์ด ์ผ์ ธ ์์ต๋๋ค.
# config/settings.yml
output:
translate:
enabled: true
provider: google # google(ํค ๋ถํ์) / papago / deepl / none
mode: replace # replace = ํ๊ตญ์ด๋ง / append = ํ๊ตญ์ด + ์๋ฌธ ์๋ฌธ| ์์น | ๋ด์ฉ |
|---|---|
์์
SUMMARY |
ํ๊ตญ์ด (ํค๋ 16๊ฐ ๊ท๊ฒฉ์ ๊ทธ๋๋ก โ ์ปฌ๋ผ์ ์ถ๊ฐํ์ง ์์ต๋๋ค) |
| ๋ฉ์ผ ๋ณธ๋ฌธ | ํ๊ตญ์ด |
| ์ด๋ ํ์ด์ง | ํ๊ตญ์ด + ์๋ฌธ ์๋ฌธ ์ ํผ์ณ ๋ณด๊ธฐ ๊ฐ๋ฅ |
DB cve.summary_en |
์๋ฌธ ์๋ฌธ ํญ์ ๋ณด์กด |
๊ฒ์์ ํ๊ตญ์ดยท์๋ฌธ ์ด๋ ์ชฝ์ผ๋ก๋ ๋ฉ๋๋ค.
ํ ๋ฒ ๋ฒ์ญํ ๋ฌธ์ฅ์ DB(translation ํ)์ ์บ์๋์ด ๋ค์ ๋ฒ์ญํ์ง ์์ต๋๋ค.
๋งค์ผ ์๋ก ์กํ๋ ๊ฑด์๋ง ๋ฒ์ญํ๋ฏ๋ก(๋ณดํต ์ ๊ฑด~์์ญ ๊ฑด) ๋ฌด๋ฃ ํ๋๋ก ์ถฉ๋ถํฉ๋๋ค.
| ์ ๊ณต์ | ํค | ํ๋ | ๋น๊ณ |
|---|---|---|---|
google (๊ธฐ๋ณธ) |
๋ถํ์ | ๋ช ์ ์์ | ๋ฌด๋ฃ ์๋ํฌ์ธํธ. ๋๋ ์์ฒญ ์ ์ผ์ ์ฐจ๋จ๋ ์ ์์ |
papago |
NAVER_CLIENT_ID/NAVER_CLIENT_SECRET |
1๋ง์/์ผ | ๋ค์ด๋ฒ ๊ฐ๋ฐ์์ผํฐ ๋ฌด๋ฃ ๋ฑ๋ก(์นด๋ ๋ถํ์) |
deepl |
DEEPL_API_KEY |
50๋ง์/์ | ํ์ง์ด ๊ฐ์ฅ ์ข์. ๊ฐ์ ์ ์นด๋ ํ์ธ์ด ํ์ํ ์ ์์ |
์ ๊ณต์๋ฅผ ๋ฐ๊พธ๋ ค๋ฉด settings.yml ์ provider ๋ฅผ ๊ณ ์น๊ณ , ํด๋น ํค๋ฅผ GitHub Secrets ์ ๋ฃ์ผ๋ฉด ๋ฉ๋๋ค.
- ๋ฒ์ญ์ด ์คํจํด๋ ์คํ์ ๊ณ์๋ฉ๋๋ค. ์คํจํ ๊ฑด์ ์๋ฌธ ์๋ฌธ์ด ๊ทธ๋๋ก ๋ค์ด๊ฐ๋๋ค.
- CVE ๋ฒํธยทURL ์ ๋ฒ์ญ๊ธฐ์ ๋๊ธฐ์ง ์์ต๋๋ค.
CVE-2026-1234๊ฐ ๊นจ์ง์ง ์๋๋ก ์๋ฆฌํ์์๋ก ๋นผ๋จ๋ค๊ฐ ๋๋๋ฆฝ๋๋ค. [๋ฒ์ ๋ฒ์ ๋ถ๋ช ํ]์ ๋์ฌ๋ ๋ฒ์ญํ์ง ์๊ณ ๊ทธ๋๋ก ์ ์งํฉ๋๋ค.- ๋ฒ์ญ์ ์ผ๊ณ ๊บผ๋ "๋ณ๊ฒฝ" ์๋ฆผ์ด ์์์ง์ง ์์ต๋๋ค. ๋ณ๊ฒฝ ๊ฐ์ง ํด์๋ ๋ฒ์ญ๋ฌธ์ด ์๋๋ผ ์๋ฌธ ์๋ฌธ์ ๊ธฐ์ค์ผ๋ก ๊ณ์ฐํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
๋ฒ์ญ์ ๊ทธ ์คํ์์ ์์ง๋ ๊ฑด์๋ง ์ ์ฉ๋ฉ๋๋ค. ๋งค์ผ ์คํ์ ์ต๊ทผ ๋ฉฐ์น ์น๋ง ๋ค์ ํ๊ธฐ ๋๋ฌธ์, ๋ฒ์ญ์ ์ผ๊ธฐ ์ ์ ์์ฌ ์๋ ๊ฑด์ ๊ทธ๋๋ก ์๋ฌธ์ผ๋ก ๋จ์ต๋๋ค. ์๋ ๋ช ๋ น์ผ๋ก ํ ๋ฒ์ ์ฑ์๋๋ค.
git pull # ์ต์ DB ๋ฐ๊ธฐ
python -m src.aicve.translate --backfill # ์์ง ๋ฒ์ญ ์ ๋ ๊ฑด๋ง
python -m src.aicve.translate --retranslate # ์ ๊ณต์๋ฅผ ๋ฐ๊ฟ ์ ๋ถ ๋ค์ ๋ฒ์ญ
python -m src.aicve.translate "์๋ฌธ ๋ฌธ์ฅ" # ํ ๋ฌธ์ฅ๋ง ์ํ ๋ฒ์ญ์ด ์์ ์ ๋ณ๊ฒฝ ๊ฐ์ง ํด์๋ฅผ ๊ฑด๋๋ฆฌ์ง ์์ต๋๋ค. ๋ฒ์ญ๋ง์ผ๋ก ์ ๊ฑด์ด "๋ณ๊ฒฝ"์ผ๋ก ์กํ ์๋ฆผ์ด ์์์ง๋ ์ผ์ ์์ต๋๋ค.
output:
translate:
enabled: false # ์๋ฌธ ์๋ฌธ ๊ทธ๋๋ก์ค์ญ ๊ฐ๋ฅ์ฑ์ด ์์ผ๋ฏ๋ก ์กฐ์น ํ๋จ์
REFERENCE_URL์ ์๋ฌธ์ ํ์ธํ๊ณ ํ์๊ธธ ๊ถํฉ๋๋ค. ๋ฒ์ญ๋ฌธ์ ๋น ๋ฅด๊ฒ ํ์ด๋ณด๋ ์ฉ๋์ ๋๋ค.
- NVD (๋ฏธ๊ตญ NIST, ๊ณต๊ฐ ๋๋ฉ์ธ)
- OSV.dev (Google, CC-BY 4.0)
- GitHub Advisory Database (CC-BY 4.0)
- CISA KEV (๊ณต๊ฐ ๋๋ฉ์ธ)
๊ฐ ์์ค์ ์ด์ฉ์ฝ๊ด์ ์ค์ํด ์ฃผ์ธ์. ์์ ์ ์ฌ๋ฐฐํฌ ์์๋ ์ถ์ฒ ํ๊ธฐ๊ฐ ํ์ํฉ๋๋ค.