Skip to content

스캐너 연결하기 ​

autofix 는 취약점을 직접 찾지 않아요. 무엇이든 트리거 형식으로 POST /api/trigger 하면 돼요.

보내기 전에 거르기 ​

세션은 받은 목록을 전부 고치려고 해요. 보내기 전에 거르면 일이 줄어요.

  • 무시하기로 한 것은 빼요. 이미 검토하고 받아들인 위험은 스캐너 쪽에서 걸러요.
  • 남은 게 없으면 보내지 않아도 돼요. 빈 목록을 보내도 autofix 는 아무것도 하지 않지만(200), 굳이 부를 필요가 없어요.
  • fixed 를 채워요. 고친 버전을 알려 주면 세션이 바로 그 버전으로 올려요. 비면 "고친 버전 없음"으로 전해지고 대개 보류로 끝나요.
  • scope 를 채워요. runtime·dev 처럼 범위를 주면 세션이 배포 확인이 필요한지 판단해요.

어떤 스캐너든: curl ​

sh
curl -fsS -X POST "$AUTOFIX_URL/api/trigger" \
  -H "Authorization: Bearer $AUTOFIX_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary @findings.json

findings.json 의 모양은 HTTP API를 보세요. source 에 스캐너 이름을 적으면 기록 화면에 출처로 보여요.

매일 도는 스캔 작업에서 ​

스캔하고 메일이나 알림을 보내는 작업이 이미 있다면, 그 끝에 한 번 보내는 게 가장 간단해요. 예를 들어 Go 로 된 스캐너라면

go
// 남은 취약점이 있을 때만 autofix 에 넘긴다. 실패해도 리포트는 그대로 보낸다.
func notifyAutofix(ctx context.Context, findings []Finding) {
	url, tok := os.Getenv("AUTOFIX_URL"), os.Getenv("AUTOFIX_TOKEN")
	if url == "" || tok == "" || len(findings) == 0 {
		return
	}
	body, _ := json.Marshal(map[string]any{"source": "daily-scan", "findings": findings})
	req, _ := http.NewRequestWithContext(ctx, "POST", url+"/api/trigger", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+tok)
	req.Header.Set("Content-Type", "application/json")
	if res, err := http.DefaultClient.Do(req); err != nil {
		log.Printf("autofix: %v", err)
	} else {
		res.Body.Close()
	}
}

autofix 호출이 실패해도 원래 하던 리포트는 막지 않는 게 좋아요.

GitHub Actions 에서 ​

레포마다 따로 보내면 실행이 겹쳐 대부분 건너뜀이 돼요. 여러 레포를 모아 한 번에 보내는 쪽을 권해요. 레포 하나만 다룬다면 이렇게 할 수 있어요.

yaml
name: autofix
on:
  schedule: [{ cron: '30 0 * * *' }]
  workflow_dispatch:
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: stable }
      - name: 스캔하고 남은 것만 보내기
        env:
          AUTOFIX_URL: ${{ secrets.AUTOFIX_URL }}
          AUTOFIX_TOKEN: ${{ secrets.AUTOFIX_TOKEN }}
        run: |
          # 취약점이 있으면 0 이 아닌 값으로 끝나므로 || true
          go run github.com/google/osv-scanner/v2/cmd/osv-scanner@latest scan source --format json -r . > osv.json || true
          node scripts/osv-to-autofix.mjs "$GITHUB_REPOSITORY" osv.json > findings.json
          if [ "$(jq '.findings | length' findings.json)" -gt 0 ]; then
            curl -fsS -X POST "$AUTOFIX_URL/api/trigger" \
              -H "Authorization: Bearer $AUTOFIX_TOKEN" \
              -H 'Content-Type: application/json' --data-binary @findings.json
          fi

OSV-Scanner 결과 바꾸기 ​

OSV-Scanner 의 JSON 결과를 트리거 형식으로 바꾸는 예시예요. 위 워크플로의 scripts/osv-to-autofix.mjs 에 해당해요.

js
// 사용법: node osv-to-autofix.mjs owner/repo osv.json
import { readFileSync } from 'node:fs'

const [repo, file] = process.argv.slice(2)
const osv = JSON.parse(readFileSync(file, 'utf8'))
const rank = { CRITICAL: 4, HIGH: 3, MODERATE: 2, MEDIUM: 2, LOW: 1 }
const findings = []
for (const r of osv.results ?? []) {
  for (const p of r.packages ?? []) {
    for (const v of p.vulnerabilities ?? []) {
      const sev = (v.database_specific?.severity ?? 'MEDIUM').toUpperCase()
      const fixed = v.affected
        ?.filter((a) => a.package?.name === p.package.name)
        .flatMap((a) => a.ranges ?? [])
        .flatMap((x) => x.events ?? [])
        .find((e) => e.fixed)?.fixed
      findings.push({
        repo,
        file: r.source?.path?.split('/').pop() ?? '',
        package: p.package.name,
        version: p.package.version,
        fixed,
        id: v.id,
        aliases: v.aliases,
        severity: sev === 'MODERATE' ? 'MEDIUM' : rank[sev] ? sev : 'MEDIUM',
        summary: v.summary ?? '',
        url: `https://osv.dev/vulnerability/${v.id}`,
      })
    }
  }
}
console.log(JSON.stringify({ source: 'osv-scanner', findings }))

severity 는 CRITICAL·HIGH·MEDIUM·LOW 중 하나로 맞춰 주세요. 목록을 정렬할 때 이 네 값만 알아요.

언제 보내면 좋나요? ​

  • 하루 한 번이 알맞아요. 한 실행이 보통 몇 분에서 길면 한두 시간 걸리고, 그동안 온 트리거는 건너뛰어요.
  • 맥이 켜져 있고 agent-bridge 에 연결된 시간에 보내세요. 연결이 없으면 실행은 바로 실패로 끝나요.
  • 결과를 사람이 볼 수 있는 시간대(예: 출근 전)에 끝나도록 잡으면, 확인 대기가 생겨도 금방 답할 수 있어요.

agent-bridge 의 서비스 API 위에서 도는 자동 대응 서버예요.