자율 개선 일일 보고 — 2026-09-02
요약. 2026-09-02에 자율 개선 에이전트가 27개 프로젝트에서 50회차를 돌려 28건을 릴리즈하고 16건은 머지만 했으며 3건은 변경이 없었고 실패는 0건이다. 릴리즈: AgentHub v0.226.0, appstore v2.1.1, kanpic v0.227.0, moina v0.1.16, moyro v0.2.10, muni v0.22.0, releasedock v0.5.1, relio v1.11.8….
- 50회차
- 27프로젝트
- 28배포 준비 완료
- 0릴리즈 진행 중
- 16병합 완료
- 0검토 대기
- 0검증 실패
- 6변경 없음
- 0실행 오류
회차
| 시각 | 프로젝트 | 결과 |
|---|---|---|
| 12:43 | ReSSO merged PR #2; weekly released v0.281.0 (수동 동기화 | 변경 없음 러너 git 식별자 미설정 수정) |
| 12:45 | Clustara | 병합 완료 merged (옛 러너 경로에서 수행된 회차 원장 이관) |
| 12:46 | AgentHub | 배포 준비 완료 release-only, released v0.226.0 |
| 13:08 | Vendra | 병합 완료 merged PR #101, release released |
| 13:17 | ai-admin | 병합 완료 merged PR #1, release released |
| 13:24 | aiportal-front-admin | 병합 완료 merged PR #1, release skipped |
| 13:39 | aiportal-java | 병합 완료 merged PR #1, release skipped |
| 13:45 | aiportal-py | 병합 완료 merged PR #1, release skipped |
| 13:56 | appstore | 배포 준비 완료 merged PR #1, released v2.1.1 |
| 14:00 | dataworks | 변경 없음 no change |
| 14:10 | git-ctx | 변경 없음 no change |
| 14:20 | igame | 변경 없음 no change |
| 14:38 | kanpic | 배포 준비 완료 merged PR #1, released v0.227.0 |
| 14:48 | moina | 배포 준비 완료 merged PR #1, released v0.1.16 |
| 15:00 | moyro | 배포 준비 완료 merged PR #1, released v0.2.10 |
| 15:18 | muni | 배포 준비 완료 merged PR #1, released v0.22.0 |
| 15:26 | pii-masker | 병합 완료 merged PR #1, release released |
| 15:50 | ptium | 병합 완료 merged PR #1, release released |
| 16:07 | releasedock | 배포 준비 완료 merged PR #1, released v0.5.1 |
| 16:17 | relio | 배포 준비 완료 merged PR #1, released v1.11.8 |
| 16:30 | umm | 병합 완료 merged PR #131, release released |
| 16:32 | Vendra/ai-admin/pii-masker/ptium | 변경 없음 태그 사후 푸시 및 Release 생성 |
| 16:40 | ReSSO | 배포 준비 완료 release-only, released v0.9.66 |
| 17:02 | visitflow | 병합 완료 merged PR #1, release missing |
| 17:27 | weekly | 배포 준비 완료 merged PR #2, released v0.282.0 |
| 17:38 | AgentHub | 배포 준비 완료 merged PR #2, released v0.227.0 |
| 17:41 | Clustara | 배포 준비 완료 release-only, released v0.9.262 |
| 18:00 | Clustara | 배포 준비 완료 merged PR #2, released v0.9.263 |
| 18:20 | Invenqor | 배포 준비 완료 merged PR #2, released v0.2.19 |
| 19:01 | Vendra | 배포 준비 완료 merged PR #102, released v0.7.36 |
| 19:17 | ai-admin | 배포 준비 완료 merged PR #2, released v1.2.2 |
| 19:26 | aiportal-front-admin | 병합 완료 merged PR #2, release skipped |
| 19:35 | aiportal-java | 병합 완료 merged PR #2, release skipped |
| 19:48 | aiportal-py | 병합 완료 merged PR #2, release skipped |
| 19:56 | appstore | 배포 준비 완료 merged PR #2, released v2.1.2 |
| 20:32 | git-ctx | 병합 완료 merged PR #14, release missing |
| 20:47 | igame | 병합 완료 merged PR #1, release missing |
| 20:56 | kanpic | 배포 준비 완료 merged PR #2, released v0.229.0 |
| 21:08 | moina | 배포 준비 완료 merged PR #2, released v0.1.17 |
| 21:20 | moyro | 배포 준비 완료 merged PR #2, released v0.2.11 |
| 21:39 | moyro | 배포 준비 완료 merged PR #3, released v0.2.12 |
| 21:47 | muni | 배포 준비 완료 merged PR #2, released v0.23.0 |
| 21:55 | pii-masker | 배포 준비 완료 merged PR #2, released v1.0.5 |
| 22:08 | ptium | 병합 완료 merged PR #2, release missing |
| 22:19 | releasedock | 배포 준비 완료 merged PR #2, released v0.5.2 |
| 22:29 | relio | 배포 준비 완료 merged PR #2, released v1.11.9 |
| 22:44 | umm | 배포 준비 완료 merged PR #132, released v0.66.0 |
| 23:03 | vibe-coders | 변경 없음 PR |
| 23:18 | visitflow | 배포 준비 완료 merged PR #2, released v2.6.1 |
| 00:01 | weekly | 배포 준비 완료 merged PR #3, released v0.283.0 |
무엇을 왜 바꿨나 (원장 발췌)
Clustara
- 선택: 스캔/SBOM 정규화기가 버리던 필드 복구 + 첫 단위 테스트 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
internal/analyzer/vulnerability.go(576줄, 단위 테스트 0개)가 Trivy·Grype·trivy-operator 스캔과 CycloneDX·SPDX SBOM import의 유일한 파싱 경로인데, 아티팩트에 실제로 들어 있는 값 4가지를 못 읽고 있었다 — Grype CVSS(cvss[].metrics.baseScore가 두 단계 아래라 항상 0), SBOMgenerated_at(존재하지 않는 최상위created를 보고creationInfo객체에 문자열 변환을 걸어 두 포맷 모두 빈 값), CycloneDX 1.4 generator(metadata.tools배열 형태 미지원 + 빈 값을 그대로 return해 SPDX fallback 차단), Grype EPSS(객체 배열). 기존 Trivy V3Score 경로와 요청이 준 defaults 우선순위는 그대로 두고 누락분만 추가했으며, 신규 테스트 6개를 고치기 전 코드에 되돌려 붙여 4개가 정확히 해당 값을 지목하며 실패하는 것을 확인했다. 검증:go build ./...,go vet ./...,go test ./...전부 통과(17 패키지). 저장소 관례대로 AppVersion·changelog·docs/K8S_OPERATIONS_HUB.md버전 마커를 v0.9.262로 올렸다(release gate 테스트가 강제함). - 보류 아이디어: ①
collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨서 kube-bench 결과의 Section이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ② Grype의Negligible심각도가Unknown으로 접혀 Low보다 낮은 등급 정보가 사라짐 (가치 2 / 위험 2 / S) ③.github에 FUNDING.yml만 있고 CI 워크플로가 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ④detectScanner가 아무것도 못 맞히면scanner="unknown"으로 저장하면서 trivy 파서를 돌림 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ⑤internal/harbor,internal/servicecatalog테스트 0개 — 커버리지 공백 (가치 3 / 위험 1 / M) - 릴리즈: v0.9.262 (2026-09-02)
AgentHub
- 선택: 관리자 서버 로그 검색이 화면에 보이는 필드를 찾지 못하던 문제 수정 +
internal/logging첫 테스트 (가치 4 / 위험 1 / 작업량 S) - 결과: 성공 — PR https://github.com/hkjang/AgentHub/pull/1 (main 머지됨)
- 요약: 운영 화면은 로그 한 줄에 message와 structured fields를 함께 출력하는데 서버의
Ring.Entries는 message와 source만 훑고 있어, 화면에 보이는 agent 이름·에러 문자열을 그대로 입력하면 결과가 0건이었습니다.matches()로 필드 키·값까지 검색하도록 고치고, 저장소에서 유일하게 테스트가 0개였던internal/logging에 링 랩어라운드·limit·레벨 필터·검색·capture 핸들러·동시성 테스트 9개를 추가했습니다. 검증: go vet, go test -race ./cmd/… ./internal/…, web npm ci+lint+build 모두 통과. - 보류 아이디어:
- dlp.notPhone의 앞 두 조건이 죽은 코드이고 0으로 시작하는 실제 계좌번호를 전부 놓침 (3/2/S)
- korean.EndsInConsonant가 괄호·따옴표로 끝나는 값에서 조사를 잘못 고름 (2/2/S)
- captureHandler.WithGroup이 그룹 이름을 버려 서로 다른 그룹의 같은 키가 충돌 (2/1/S)
- runInfoCommand가 version 외 인자를 run()으로 흘려보내
agenthub --help가 DB 오류로 실패 (2/1/S)
- 릴리즈: v0.226.0 (2026-09-02)
Vendra
- 선택: 요청 본문의 날짜를 검증 없이
$n::date로 캐스팅하던 네 개의 쓰기 경로 수정 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공 (commit e5cef01)
- 요약:
validDateFields는 business object의 세 날짜에만 적용돼 있었고, 같은 결함이 견적 유효일(포털 입찰), 예정일(포털 납품/인보이스/문의), 거래 시작일(공급업체 등록), 거래일(구매 원장) 네 곳에 남아 있었다. 잘못된 날짜가 PostgreSQL 캐스트까지 도달해 “…저장하지 못했습니다”라는 필드를 지목하지 않는 오류로 돌아왔고, 특히 포털 입찰은 마감 직전에 견적 전체(금액·품목·조건)가 저장되지 않으면서 공급업체에게 아무 단서도 주지 못했다. 철자가 아니라 연산(요청 파라미터의 date 캐스트)을 기준으로 훑어 찾았고, 같은 기준으로 패키지를 파싱해 검증 누락을 잡는TestEveryDateCastIsGuarded와 네 엔드포인트를 실제로 호출하는 통합 테스트를 추가했다. 검증: docker postgres:16-alpine을 띄워 CI와 동일한 세 DSN(VENDRA_TEST_DSN / MIGRATE / UPGRADE)으로go test ./internal/... ./cmd/...전체 통과,gofmt -l·go vet무결. 가드 테스트는 수정 하나를 되돌리면 실패하는 것까지 확인했다. - 보류 아이디어:
- CI의 go job이
go test ./internal/...만 돌려./cmd/...를 빼놓음 — Makefile/README와 불일치 (가치 2 / 위험 1 / S) web프론트엔드 테스트 커버리지가 9개 파일뿐 — Sourcing/Objects/Admin 페이지에 테스트 없음 (가치 3 / 위험 1 / L)- 공급업체 수정(PATCH /suppliers/{id})은 tradingSince·annualSpend를 아예 갱신하지 않음 — 의도인지 누락인지 확인 필요 (가치 3 / 위험 2 / S)
POST /spend/transactions의 quantity·unitPrice·amount에 상한이 없음 — 폼에만 존재하는 제약 (가치 3 / 위험 2 / M)Makefile의 VERSION(0.6.21)이 README의 릴리스 예시(0.7.26)와 어긋남 — 릴리스 문서 정합성 (가치 2 / 위험 1 / S)
- CI의 go job이
- 릴리즈: v0.7.35 (2026-09-02, 태그 사후 푸시)
ai-admin
- 선택: 내장 SPA 번들 캐시 헤더 적용과 디렉터리 목록 노출 차단 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
internal/server/server.go의spa핸들러가embed.FS파일을 아무 캐시 헤더 없이 제공해(embed는 ModTime이 0이라 Last-Modified/ETag도 없음) 화면을 열 때마다 약 1MB 번들을 전부 다시 내려받았고,/assets·/fonts같은 디렉터리 경로는http.FileServer가 파일 목록을 노출했다. 내용 해시가 붙은assets/는max-age=31536000, immutable, 이름이 고정된fonts/는max-age=604800,index.html과 SPA fallback은no-cache로 나누고fs.Stat로 일반 파일만 서빙하도록 고쳤다. 실제 내장 번들을 대상으로 하는 회귀 테스트를 추가해 수정 전 코드에서 실패하는 것까지 확인했고,go vet·go test ./...·scripts/verify-version.sh·npm test(55개)·npm run build를 모두 통과시켰다. 저장소 관례에 따라 VERSION을 1.2.1로 올리고 CHANGELOG·README·docs·web 버전 메타데이터를 함께 맞췄다. - 보류 아이디어:
safeCSVCell이 OWASP가 함께 권고하는 tab(0x09)·CR(0x0D) 선행 문자를 중화하지 않음 (가치 2 / 위험 1 / S)- 여러 핸들러의 길이 검증이 rune이 아닌 byte 기준이라 한글 입력이 의도한 한도의 약 1/3에서 거부됨(예: 공급자 이름 160) (가치 3 / 위험 2 / M)
clearSessionCookies가setSessionCookies와 달리Secure플래그를 설정하지 않아security.cookie_secure활성 배포에서 비대칭 (가치 2 / 위험 2 / S)- CI에 정적 분석 단계(
go vet,golangci-lint, eslint)가 없어 회귀를 테스트로만 잡고 있음 (가치 3 / 위험 1 / M) r.NotFound(s.spa)가 GET 외 메서드도 받아 알 수 없는 경로로의 POST가 200 + index.html을 반환 (가치 2 / 위험 2 / S)
- 릴리즈: v1.2.1 (2026-09-02, 태그 사후 푸시)
aiportal-front-admin
- 선택: admin-v2 runtime config의 scheme-relative URL 우회 차단 (가치 5 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
validateRuntimeConfig와scripts/validate-runtime-config.mjs가//evil.test,/\evil.test를 “상대 경로”로 보고 통과시켜allowedHostsallowlist를 우회할 수 있었다.backendBaseUrl은access_token헤더·withCredentials와 함께 쓰이고authSsoUrl은window.location.assign대상이라 토큰 유출·오픈 리다이렉트로 이어진다. 상대 경로 값을 sentinel origin(https://relative.invalid/)에 resolve해 origin이 유지되는지 확인하도록 두 검증 지점을 동일하게 고치고 회귀 테스트 3개를 추가했다. 검증은npm run verify(typecheck + vitest 24개 통과, 기존 21개)와npm run build(build → runtime-config 검증 → offline 검사 → integrity manifest 18개 검증) 전체 통과, 그리고 악성 값을 넣은 임시 runtime.json으로 build-time script가 exit 1을 내는지 직접 확인했다. 커밋 fdb7ab2. - 보류 아이디어:
ensureAdminSession(force=true)가 진행 중인 비강제 요청 promise를 그대로 반환하는 재진입 버그 수정 (가치 3 / 위험 2 / S)- 절대 URL allowlist를 hostname뿐 아니라 port·scheme까지 비교하도록 강화 (가치 3 / 위험 2 / S)
- 루트 앱의
crypto-jslocal tarball 의존성 제거로 clean install 복구 — 현재npm ci자체가 불가해 검증 비용 큼 (가치 4 / 위험 4 / M) monitoringParser.findRows재귀에 깊이 제한·순환 참조 방어 추가 및 테스트 (가치 2 / 위험 1 / S)- 루트 앱 Playwright 설정만 있고
@playwright/testdependency와 test script가 없는 상태 정비 (가치 3 / 위험 2 / M)
aiportal-java
- 선택: 인증정보 로그 마스킹 (SEC-002) (가치 5 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
JwtAuthenticationFilter가 모든 요청 헤더와access_token을 INFO로 평문 기록하고 있었고,AuthServiceImpl(SSO), 배치 job의 adminToken, 토큰 발급 API 응답 본문에도 같은 누출이 있어LogMaskUtil(민감 헤더 판별 / 값 전체 마스킹 / JSON token·password 필드 마스킹)을 추가하고 7개 파일의 로그 호출에 적용했습니다. 로드맵 단계 0의 P0 항목이며 위험이 낮고 한 세션에 끝낼 수 있어 선택했습니다. 검증은LogMaskUtilTest(5) + logback ListAppender 기반JwtAuthenticationFilterLoggingTest(1)를 추가한 뒤./gradlew check build실행으로 했고 28개 테스트 전부 통과했습니다. 커밋 36950f8. - 보류 아이디어:
- SEC-004:
/sample,/batch/sample공개 경로를 운영 프로필에서 비활성화하거나 관리자 권한으로 제한 (가치 4 / 위험 3 / 작업량 M) — 운영 배포 영향 확인 필요 ControllerLogAspect.logControllerCud가getAuthentication()null 일 때 NPE 발생 가능 (가치 3 / 위험 1 / 작업량 S)JwtAuthenticationFilter의 CORS 허용 Origin 30여 개 하드코딩을 설정(yaml)으로 외부화 (가치 3 / 위험 3 / 작업량 M)getDataMap이 null 반환 시 호출부에서dataMap.get("active")NPE — 명시적 예외 처리로 정리 (가치 3 / 위험 2 / 작업량 S)- 공통 util(
ValidUtil,PagingUtil,ApiCallUtil) 단위 테스트 공백 보강 (가치 2 / 위험 1 / 작업량 S)
- SEC-004:
환경 메모
- 이 머신에는 JRE만 설치되어 있어(
/usr/lib/jvm/java-21-openjdk-amd64에 javac 없음) Gradle toolchain이 실패합니다.~/.gradle/jdks/jdk-21.0.12.1+1(Temurin 21)을 내려받아JAVA_HOME으로 지정해야 빌드가 됩니다. ./gradlew에 실행 권한이 없어sh gradlew ...로 실행해야 합니다.
aiportal-py
- 선택: mask_sensitive_data NameError 수정 + pytest 테스트 기반 도입 (가치 5 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
util/sensitive_data_masker.py의mask_sensitive_data()가 dict/list 입력에서 존재하지 않는_mask_sensitive_data()를 호출해 NameError 로 실패하는 실제 버그를 재현·수정했다. 동시에 저장소에 전혀 없던 테스트 기반(pytest.ini,tests/conftest.py,requirements-dev.txt)을 만들고 외부 의존성 없는 순수 unit 테스트(sensitive_data_masker, parse_promptmanager, dpe_merge, text_renderer)와 저장소 전체 Python AST parse 테스트를 추가했다.python -m pytest로 177 passed 확인, README/docs/TESTING.md 를 실제 상태에 맞게 갱신. 커밋8122984. - 보류 아이디어:
- GitLab CI 에 deploy 앞단 test stage 추가 — 가치 4 / 위험 3 / L (러너가 shell 태그 기반이라 python 실행 환경 불확실, 실패 시 배포 차단 위험)
collection_script/*의 import-time collection create/drop 부작용 제거 (감사 A-006) — 가치 4 / 위험 3 / M- feedback loop 의 undefined
ENV수정 (감사 A-003) — 가치 4 / 위험 2 / S pipeline/law/library/xml_parser_core.py,json_formatter.py단위 테스트 (fixture 필요) — 가치 3 / 위험 1 / Mutil/extract_minor.py의except Exception: return e(예외 객체 반환) 정리 및 테스트 — 가치 3 / 위험 2 / S
appstore
- 선택: 로그인 returnTo의 open redirect 우회 차단 (가치 5 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
auth.SafeReturnTo와 프런트엔드safeReturnTo가 선행 슬래시 하나만 검사해서/\evil.test,/<TAB>/evil.test같은 값이 통과했고, 브라우저가 backslash를 slash로 정규화하고 tab·CR·LF를 제거하면서//evil.test(protocol-relative)로 해석되어 OIDC 로그인 후 외부 리디렉션이 가능했다. 양쪽 모두 backslash와 제어문자를 거부하고 Go 쪽은url.Parse로 scheme·host·userinfo가 없는지 재확인하도록 고쳤으며, 프런트엔드 guard는web/src/lib/utils.ts로 옮겨 단위 테스트를 붙였다. 검증:go test -race(전체 패키지),npm test,npm run lint, prettier check,npm run build+check-offline-assets.sh/check-env-contract.sh/check-docs.sh모두 통과. 커밋 53a7c5d. - 보류 아이디어:
AccessLog의responseRecorder가http.Flusher·http.Hijacker를 직접 구현하지 않음(현재는Unwrap()+http.NewResponseController덕에 SSE 동작). 가치 2 / 위험 2 / Sinternal/httpapi커버리지 5.5% — DB 없이 테스트 가능한SPAHandler, middleware,DecodeJSON,parseID등에 단위 테스트 추가. 가치 3 / 위험 1 / Mhttpapi.validHTTPURL강화(호스트의 공백·제어문자 거부, IDN/포트 검증). 가치 2 / 위험 2 / S- rate limiter의
Retry-After: 60고정값을 현재 분 윈도우 잔여 시간으로 계산. 가치 2 / 위험 1 / S httpapi.decodeSetting이 Unmarshal 실패 시 부분 변형된 fallback을 반환할 수 있음 — 임시 값으로 디코드 후 성공 시에만 대입. 가치 2 / 위험 1 / S
- 릴리즈: v2.1.1 (2026-09-02)
dataworks
- 선택: API surface audit 복구 + 미문서화 라우트 86건 카탈로그 등록 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
cmd/api-surface-audit가 리브랜딩으로 사라진cmd/vibe/main.go·sdk/typescript/vibe.ts를 읽으며 경고 후 빈 문자열을 반환해, CLI/SDK 계약 검사가 0건 기준으로 항상 통과하는 무력 상태였다. 소스 경로를 정정하고 읽기 실패를 즉시 실패로 바꿨으며, mux 에 등록됐지만apiEndpoints에 없어/openapi.json에서 빠져 있던 라우트 86건을 핸들러가 실제 허용하는 메서드대로 추가했다. 같은 불변식을go test ./...안의 회귀 테스트(라우트 커버리지·죽은 항목·항목 형식)로 고정했고, 가짜 라우트를 넣어 가드가 실제로 실패하는 것까지 확인했다. 검증:go build ./...,go test ./...전체 통과,go run ./cmd/api-surface-auditgap 0 종료. - 보류 아이디어: GitHub Actions CI 부재(build/vet/test/api-surface-audit 게이트 추가) /
go vet ./...잔여 4건(테스트의 err 미검사 3건, json 태그 중복 1건) 정리 /gofmt -l미정렬 64개 파일 일괄 포맷 /parseWindow가 음수·과대 duration 을 그대로 받아 미래 시각 since 를 만드는 엣지케이스 /internal/dataworks패키지 테스트 커버리지 보강 - 릴리즈: v0.9.36 (2026-09-02)
git-ctx
- 선택: MCP 도구 인자의 JSON 타입 불일치 처리 — 따옴표 친 숫자·문자열 리스트를 읽고, 읽은 방식을 응답에 기록 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공 (commit b9ffcac)
- 요약:
additionalProperties:false위반(오탈자 인자)은 이미 응답에 기록되고 있었지만, 이름은 맞고 타입만 다른 인자는 조용히 버려지고 기본값으로 응답했습니다."startLine":"400"을 보낸 read-file은 파일 전체를 예산까지 잘라 돌려주면서 무시한 줄 범위를 언급하지 않았고, export-context는 배열이 아닌 단일 문자열 libraryIds를 거부했으며, 숫자로 온 query는 ““로 검색됐습니다.internal/mcp/args.go의stringArg/intArg/stringSliceArg가 선언된 타입을 철자하는 값을 그 타입으로 읽도록 하고(float→int 변환 범위도 정의되게 제한),internal/mcp/dispatch.go에 기존unknownArgumentNote옆에argumentTypeNote를 추가해 “read as declared / ignored”를 응답에 남깁니다. 검증: 새 테스트internal/mcp/argument_type_test.go4건(read-file 줄 범위 동등성, export-context 문자열 리스트, 해석 불가 값 보고, 디코딩 단위표) +gofmt -l,go vet ./...,go build -tags sqlite_fts5 ./...,go test -tags sqlite_fts5 ./...전체 통과,-race는 mcp·search 패키지에서 통과. - 보류 아이디어: (1)
clampResponse가 “예산에 맞춰 잘랐다”고 말하면서 실제로는 예산을 최대 ~330바이트 초과할 수 있음 (가치 2 / 위험 2 / S). (2)netclient.JoinAPIPath가 base URL이 다른 리소스 경로로 끝날 때 경로를 중복 연결 (가치 2 / 위험 3 / S). (3)netclient.resetHint의 RateLimit-Reset·Retry-After 조합에 대한 테스트 보강 (가치 2 / 위험 1 / S). (4)mcp.filterLibraries가 호출자 슬라이스를items[:0]로 제자리 변경 — 지금은 안전하지만 캐시된 슬라이스가 들어오면 ACL 오염 위험 (가치 2 / 위험 1 / S).
igame
- 선택: OIDC 로그인 후 복귀 경로의 open redirect 차단 (가치 5 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
safeReturnTo(internal/api/api.go)가 절대 URL만 걸러내서 scheme 없는 protocol-relative//host/path가 통과했고,/api/v1/auth/oidc/login?return_to=//evil/...링크를 따라간 사용자가 실제 SSO 로그인을 마친 직후 외부 사이트로 튕겨 나갔다(auth.go:335의http.Redirect). host/scheme/opaque/userinfo가 있으면 무조건/로 되돌리도록 고치고///host까지 막았으며, 통과·차단 양쪽을 확인하는 테스트를 api_test.go에 추가했다. 검증: gofmt 검사,go vet,go test,go test -race전체 통과,npm --prefix web test209개 통과. - 보류 아이디어: (1)
migrations패키지에 파일명 규약·번호 연속성·체크섬 안정성을 지키는 테스트 추가 — 지금은 사전식 정렬에 의존하는데10_x.sql같은 이름이 들어오면 순서가 깨진다. (2)internal/database는 커버리지 0% —Migrate의 체크섬 불일치 경로를 검증할 방법 마련. (3) 감사 로그 CSV 검색어의%/_가 ILIKE 와일드카드로 새어 들어가는 문제 이스케이프. (4)clockMinutes가+9:+5같은 부호 붙은 값을 시간으로 받아들이는 입력 검증 강화. (5)cmd/igame커버리지 13.7% — 기동/종료 경로 테스트 보강.
kanpic
- 선택: 엑셀 규칙과 어긋난 서식 함수(PROPER·FIXED·DOLLAR) 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: PROPER 가 빈칸·탭·붙임표·밑줄 뒤만 낱말의 처음으로 보아
PROPER("o'neil")→”O’neil”,PROPER("76budget")→”76budget” 을 내던 것을 엑셀 규칙대로 “글자가 아닌 것 뒤는 모두 낱말의 처음”(unicode.IsLetter)으로 고쳤고,formatNumber가 음수 자릿수를 0 으로 깎아FIXED(1234.567,-2)→”1,235” 를 내던 것을 소수점 왼쪽 반올림(“1,200”)으로 고쳤다(ROUND 와 답이 어긋나 있었다). 덤으로 ea6fd45 가 잘못 담은 18MBworkbook.test실행 파일을 지우고.gitignore에*.test를 넣었다. 검증:gofmt -l,go build ./...,go test ./...(전체 통과),cd web && npm test(480개 통과) 및npm run build,scripts/check-release-docs.sh,scripts/check-commit-identities.sh. 커밋 3개(03dda1d, e50238e, c4ae80d). - 보류 아이디어:
- CEILING/FLOOR 가
number*factor < 0이면 무조건 #NUM! 을 내는데, 엑셀·구글 시트는CEILING(-4.5,2)=-4,FLOOR(-4.5,2)=-6 을 낸다(MROUND 에만 맞는 규칙). 참조 구현으로 확인 후 고칠 것. internal/external/fetcher.go의 응답 캐시가 만료 항목을 지우지 않아 무한히 커진다. 만료 정리 + 상한 필요.- 같은 캐시가 실패도 담아, 관리자가 allowed_hosts 를 고쳐도 최대 5분간 “허용되지 않은 호스트” 가 남는다. 설정 변경 시 무효화 필요.
internal/external/fetcher.go의short()가 160바이트에서 자르며 UTF-8 글자를 쪼갠다(한글 오류 메시지가 깨진다).NUMBERVALUE미구현(#NAME?),VALUE("12:00")이 시각을 읽지 못한다.
- CEILING/FLOOR 가
- 릴리즈: v0.227.0 (2026-09-02)
moina
- 선택: 반복된
X-Forwarded-Forheader 줄을 하나의 chain으로 이어 붙이기 (가치 4 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
backend/internal/httpapi/network.go의forwardedAddresses가Header.Get으로 첫 번째X-Forwarded-For줄만 읽어, Proxy가 자기 hop을 별도 header 줄로 덧붙이는 구성에서는 Client가 미리 보낸 줄이 chain 전체를 대신했고clientIP가 위조 값으로 계산되어 감사 기록과 공유 요청 한도 bucket이 오염되었습니다. 같은 함수의Forwarded·X-Forwarded-Proto분기가 이미 쓰던Header.Values로 바꿔 받은 순서대로 이어 붙이도록 고치고, 위조 줄 + Proxy 줄 조합과 신뢰 hop 투명성을 검증하는 테스트 2개를 추가했으며(수정 전 코드에서 실패하는 것을 확인)docs/configuration.md에 이 동작을 한 문장 명시했습니다. 검증은make fmt,make check,go test -race ./...,go vet ./..., staticcheck 전부 통과(frontend는 변경 없어 실행 생략). - 보류 아이디어: Email 알림에 조용한 시간 미적용 — 문서상 Toast·Desktop 전용이 의도라 변경 보류(가치 2 / 위험 3 / M) ·
formatRelativeTime이 연도가 다른 Moin을 월·일만 표시해 모호함(가치 2 / 위험 2 / S) ·frontend/src/utils/format.ts단위 테스트 부재(가치 2 / 위험 1 / S) · Makefiletest가 CI와 달리-race미사용(가치 2 / 위험 1 / S) - 릴리즈: v0.1.16 (2026-09-02)
moyro
- 선택: 링크 프리뷰 SSRF 가드의 DNS 리바인딩 취약점 수정 및 links 패키지 첫 테스트 추가 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
server/internal/links의safeDialContext가 호스트를 조회해 모든 응답 주소를 검사한 뒤 원래 호스트명을net.Dialer에 그대로 넘겨 DNS를 두 번 해석했다. 리바인딩 응답이 검사 시점에는 공인 IP, 연결 시점에는 127.0.0.1을 반환하면 인증 사용자용/api/v4/link_preview_image프록시가 루프백·사설망으로 유도될 수 있었으므로, 검증한 주소로 직접 다이얼하도록dialTargets를 분리하고 CGNAT·192.0.0.0/24·멀티캐스트·예약 대역을 차단 목록에 추가했다. 테스트가 하나도 없던 패키지에 URL 추출, OpenGraph 파싱, 주소 정책, 주소 고정, 캐시 축출 테스트 12개를 추가했고go vet ./...,go test -race ./...(44 패키지 통과),scripts/check-source-sizes.sh로 검증했다. 웹 변경이 없어 webapp 빌드는 손대지 않았다. - 보류 아이디어: (1) 로드맵의 create-post 인가·멤버십 2회 쿼리를 단일 쿼리로 병합 — PostgreSQL 통합 테스트 환경이 필요해 보류. (2)
ratelimit.Limiter.Middleware가 JSON 본문을http.Error로 써서 Content-Type이 text/plain으로 나가는 문제. (3) 테스트가 전혀 없는invites/sidebar/userstatus/postacks패키지의 단위 테스트 보강. (4) 로드맵의 메시지 목록 가상화(로드된 모든 행이 DOM에 남음) — 작업량 L이라 단일 세션 범위 초과. - 릴리즈: v0.2.10 (2026-09-02)
muni
- 선택: 옛 한글 문서(.hwp)의 글자색·글꼴·크기 읽기 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
.hwp리더가 CHAR_SHAPE 에서 굵게·기울임·밑줄·취소선 네 스위치만 읽고 기준 크기·글자 색·글꼴 번호를 버려서, 같은 문서를.hwpx로 저장한 것과 달리 강조한 낱말이 전부 검은 본문으로 들어왔습니다. COLORREF(0x00BBGGRR, 파랑이 먼저)와 FACE_NAME 목록(한글 글꼴이 앞에 놓이므로 번호를 앞에서부터 셈)을 읽고, 1/100 pt 크기 환산은hangul.FontSize로 두 리더가 공유하게 했습니다. 검정·10pt 는 한글이 손대지 않은 글에도 적어 두므로 표시하지 않습니다. 검증은 형식 문서대로 바이트를 깐 새 픽스처 세 개(색·크기·글꼴, 기본값 미표시, 표가 없는 글꼴 번호)와go vet ./...,go test ./...전체 통과. 커밋 15c09b4. - 보류 아이디어:
.hwp/.hwpx위첨자·아래첨자 읽기·쓰기 (가치 3 / 위험 2 / S) — CHAR_SHAPE 속성 비트 15·16, HWPX<hh:supscript/>.hwpx글자 음영(형광펜) 양방향 (가치 3 / 위험 2 / S) — charPr 의 shadeColor,.hwp는 음영 색 오프셋 60.hwpx표 열 너비 읽기 (가치 3 / 위험 2 / M) — DOCX 는 이미 유지하는데 HWPX 는 버림- CI 에
go vet과gofmt -l추가 (가치 2 / 위험 1 / S) — 이번에도 gofmt 를 손으로 돌려야 했음
- 릴리즈: v0.22.0 (2026-09-02)
pii-masker
- 선택: 비동기
/v1/jobs업로드 사전 검증 (가치 4 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
POST /v1/jobs가 동기 경로(/v1/mask)와 달리 크기·MIME·페이지 수 검증을 건너뛰어, 초과/미지원 파일도 디스크에 저장하고 202를 반환한 뒤 백그라운드에서야 실패하던 문제를 고쳤습니다.service.CreateJob이 저장 전에validateAttachment/countPages를 실행하고 새service.InvalidInputError로 감싸 핸들러가 400invalid_request를 돌려주도록 했으며, 검증 실패 시 job 디렉터리가 생기지 않는지 확인하는 통합 테스트 3개(미지원 타입/크기 초과/PDF 페이지 한도)를 추가했습니다. 부수적으로-race에서 드러난 pdfcpuConfigPath전역 변수 경합을 요청마다 쓰지 않고 각 패키지init()에서 한 번만 설정하도록 분리했습니다. 검증은go vet ./...,go test ./...,go test -race ./...전부 통과, 그리고 수정 코드를 임시로 되돌려 새 테스트 3개가 실제로 실패하는 것까지 확인했습니다(커밋 2개). - 보류 아이디어:
/v1/history의limit상한 없음(과도한 값 요청 시 전체 목록 직렬화) / job 파일 보존·정리 정책 부재로 저장소 무한 증가 /CreateJob의go runJob동시 실행 개수 제한 없음 /internal/jobs,internal/config패키지 단위 테스트 전무 / 마스킹 정책 엣지케이스(빈 로컬파트 이메일, 구분자 없는 계좌번호 등) 테스트 보강 - 릴리즈: v1.0.4 (2026-09-02, 태그 사후 푸시)
ptium
- 선택: 워드 문단이 링크·추적삽입·콘텐츠컨트롤 안에 가진 글자를 읽기 (가치 5 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
internal/docs/prose.go의 워드 리더가w:p바로 아래의w:r>w:t만 읽어서,w:hyperlink·w:ins·w:sdt안에 든 글자가 말없이 사라졌다(“자세한 내용은 여기를 참고하십시오” → “자세한 내용은 를 참고하십시오”, 문단 전체가 래퍼 안이면 슬라이드에서 통째로 증발). 문단의 innerxml 을 토큰 순회해 순서대로t를 모으는wordtext.go를 추가하고 문단·표 칸 양쪽에 적용했으며,w:delText/w:instrText/수식(m:t)은 종전대로 제외했다. 재현 스크래치 테스트로 버그를 먼저 확인한 뒤wordlinks_test.go(6가지 문단 + 표 1가지)를 추가하고make test(go test -race, go vet, npm typecheck, npm build) 전부 통과. VERSION 1.69.19 로 스탬프하고 릴리스 노트 작성, 커밋 a634fe0. - 보류 아이디어:
- xlsx 의
t="str"(수식 캐시 문자열)·t="b"(불리언) 셀이 숫자 취급되어 서식 변환을 타는 문제 (가치 3 / 위험 2 / M) - 유럽식 Excel 이 저장하는
;구분 CSV 를 한 열로 읽는 문제 — 구분자 추정 (가치 2 / 위험 3 / S) docs.columnLetter가 ZZ(index 701) 를 넘으면 깨짐 — 현재 maximumColumns=5 라 도달 불가 (가치 1 / 위험 1 / S)- 워드 문단 안의
w:br/w:tab이 사라져 앞뒤 단어가 붙는 문제 (가치 2 / 위험 2 / S)
- xlsx 의
- 릴리즈: v1.69.19 (2026-09-02, 태그 사후 푸시)
releasedock
- 선택: 심플 모드에서 복제가 마지막 파일로 미뤄진 실행의 앱 배포 실행 차단 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
복제 실행 범위=ONCE+앱 배포 실행 범위=EACH조합에서 앞선 파일의 실행이 복제를 SKIPPED 로 건너뛴 뒤에도 앱 배포 명령을 실행해, 미러링되지 않은 이미지로 앱을 교체할 수 있었습니다(코드 주석이 명시한 불변식 위반).appDeployStageRuns헬퍼를 추가해 복제가 SKIPPED 면 앱 배포도 미루도록 하고, 단계 상태 문자열을 상수화했습니다. 순수 함수 단위 테스트(TestAppDeployWaitsForADeferredReplication)를 추가했고go vet, backend/runnergo test ./...,npm test -- --run,npm run build를 모두 통과했습니다. 관리 화면 도움말·docs/simple-mode.md 도 갱신하고 VERSION 을 0.5.1 로 올렸습니다(저장소 관례). - 보류 아이디어:
listSimpleRuns의actor쿼리 파라미터가 UUID 검증 없이actor_id=$1에 들어가 잘못된 값이면 400 대신 500 이 됩니다 (가치 2 / 위험 1 / S).readUploadBatch의 batchId/batchLast 파싱에 단위 테스트가 없습니다 (가치 2 / 위험 1 / S).- CI 와 Makefile 에
go vet(또는 golangci-lint) 단계가 없어 정적 검사가 수동입니다 (가치 3 / 위험 1 / S). simpleRunLogger의 로그 한도 도달 경계(정확히 remaining 만큼 잘릴 때의 안내 메시지) 동작이 테스트되지 않았습니다 (가치 2 / 위험 1 / S).web/dist/assets/vendor청크가 617KB 로 커서 폐쇄망 초기 로딩 최적화 여지가 있습니다 (가치 2 / 위험 3 / M).
- 릴리즈: v0.5.1 (2026-09-02)
relio
- 선택: 검색어의
%·_를 와일드카드가 아닌 글자로 다루기 (가치 4 / 위험 2 / 작업량 M) - 결과: 성공
- 요약: 고객·영업기회·리드·제품·담당자·협업자 검색과 관리자 Audit Log 검색이 사용자가 입력한 문자열을 그대로
LIKE '%'||lower($n)||'%'에 이어 붙여, Postgres 가%와_를 와일드카드로 읽었습니다. “50%” 로 검색하면 “50” 이 든 모든 고객이,_는 임의의 한 글자가,%하나는 테이블 전체가 나왔습니다.crm.SearchPattern이 Go 쪽에서 백슬래시·%·_를 이스케이프한 패턴을 만들고 7개 질의가 모두ESCAPE '\'를 선언하도록 바꿨습니다. 빈 질의는 빈 패턴을 그대로 돌려주므로 기존$n=''무필터 가드가 유지됩니다. 검증은 새 단위 테스트 3개(이스케이프 결과, 공백 질의,internal/전체를 훑어ESCAPE없는 LIKE/ILIKE 를 잡는 회귀 가드)와go test -race ./...,go vet ./...,webtypecheck·build,check-env-contract.sh,check-static-assets.sh전체 통과. 커밋 a49fd95. - 보류 아이디어:
- MCP 요청 본문이 1MB 를 넘으면 조용히 잘려 “Parse error” 가 되므로 413 으로 구분 (가치 3 / 위험 1 / S)
internal/api(OpenAPI 문서),internal/audit,internal/job은 테스트가 하나도 없음 (가치 3 / 위험 1 / M)internal/server는 파일 23개에 테스트 4개뿐 — 순수 헬퍼(projection, 필터 파싱)부터 보강 (가치 3 / 위험 1 / M)- CI 에
gofmt -l검사 추가 — 지금은 포맷 위반이 통과함 (가치 2 / 위험 1 / S)
- 릴리즈: v1.11.8 (2026-09-02)
umm
- 선택: 분량 제한이 남긴 “아무것도 열지 않는 부 제목” (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: v0.63.0의 부 나누기와 v0.64.0의 분량 맞추기를 같이 켜면, 덱 완성 후 2차
fit이 부 제목(Sectioned)을 사람의 슬라이드와 같은 점수표로 채점해 제목만 남고 그 부의 슬라이드가 전부 잘리는 결함이 있었습니다(재현: 3부 12장 → 8장 요청 시 빈 제목 2개, 사람 슬라이드는 8칸 중 5칸). 부 제목을 채점 대상에서 빼고 “그 부에 슬라이드가 남아 있을 때만 제목이 존재”하도록choose/openParts를 도입했고, 잘려 나간 뒤의 부 개수를 화면 문구(Storyline.Sections)에 다시 반영했습니다. 검증: 새 시험 3개 추가 후 구현을 옛 동작으로 되돌리면 3개 전부 실패함을 확인,go vet ./...·go test ./...(113개 파일) · gofmt · tsc · oxlint/Prettier · i18n 948키 · vitest 137개 ·scripts/check-version.sh통과. v0.65.1로 릴리스 커밋(7009570). - 보류 아이디어:
docs/README.md문서 허브가 v0.8.1 기준에 멈춰 있어 최신 릴리스 목록·기준 버전이 실제(v0.65.x)와 어긋남 (가치 2 / 위험 1 / S)usableSections가 서로 다른 부에 같은 제목이 오는 제안을 막지 않음 — 같은 이름의 부가 두 번 열림 (가치 2 / 위험 1 / S)PresentationModal이 부 개수는 말하지만 “몇 장이 부 제목인지”는 말하지 않음 — 분량 안에서 제목이 차지한 칸을 보여 주기 (가치 2 / 위험 2 / M)fit의openParts재계산이 채우기 루프마다 O(n) — 슬라이드 수가 큰 공간에서 O(n²), 지금은 무해하지만 상한 명시 필요 (가치 1 / 위험 1 / S)length.go파일 머리 주석이 부 제목을 다루지 않아, 이 파일이 무엇을 채점하고 무엇을 채점하지 않는지 한 곳에 적혀 있지 않음 (가치 2 / 위험 1 / S)
ReSSO
- 선택: JWKS Cache-Control이 writeJSON에 덮어써지던 문제 수정 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공 (커밋 35fd2d9)
- 요약:
jwks핸들러가 설정한Cache-Control: public, max-age=300을writeJSON이no-store로 무조건 덮어써서, 코드는 캐시 가능하다고 말하는데 응답은 아니라고 말하는 상태였다(직접 재현해 확인).writeJSON의no-store를 규칙이 아닌 기본값으로 바꾸고, JWKS의 max-age는 인스턴스 자신의 키 집합 캐시 수명인store.SigningKeyTTL(30초)에서 가져오도록 했다 — 기존 300초는 그 창의 10배라 회전 직후 최대 5분간 새kid검증이 실패할 수 있어 그대로 되살리지 않았다. 캐시 가능해진 응답의 CORS 헤더가 Origin에 의존하므로Vary: Origin을 허용된 Origin일 때만이 아니라 항상 붙이도록 했다. 검증: 새 테스트 2개(단위 + 연동)가 수정 전 코드에서 실제로 실패함을 확인했고,go test -race ./...전체 통과(연동 테스트 SKIP 0건),go vet,golangci-lint(0 issues),govulncheck(0),npm run lint,npm run test(22파일/104테스트, 디스크 파일 수와 일치),npm run build모두 통과. 빌드가 만든webui/dist/index.html해시 변경은 무관하므로 되돌렸다. - 보류 아이디어:
- Discovery에
authorization_response_iss_parameter_supported: true추가 — 서버는 이미 인가 응답에 RFC 9207iss를 넣는데 메타데이터로 알리지 않아 RP가 강제 검증을 켜지 못한다 (가치 3 / 위험 1 / S) - UserInfo POST에서 form-encoded
access_token파라미터 수용 (RFC 6750 §2.2). 현재는 Authorization 헤더만 읽는다 (가치 2 / 위험 1 / S) authorization엔드포인트가 기존 SSO Session 재사용 시 계정 상태를 다시 보지 않아, 토큰 교환에서야 거절될 코드를 발급하는 경로가 있는지 점검 (가치 2 / 위험 2 / M)- 잠긴(locked) 계정의 기존 SSO Session이 계속 새 인가 코드를 받는 동작을 의도된 것으로 문서화할지 검토 (가치 2 / 위험 2 / S)
login에서prompt=login재인증 시 기존 Session을 정리하지 않아 한 사용자에게 Session이 둘 남는 문제 (가치 2 / 위험 2 / M)
- Discovery에
- 릴리즈: v0.9.66 (2026-09-02)
visitflow
- 선택: 위조된 X-Forwarded-For로 접속 IP를 바꿀 수 있던 문제 수정 (가치 5 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
Routes()가 chi의middleware.RealIP를 무조건 사용해 모든 요청이X-Forwarded-For로RemoteAddr를 덮어쓸 수 있었고, 그 결과 로그인 잠금·공개 API 요청 한도를 헤더만 바꿔 우회할 수 있었으며 감사 로그·세션·동의 기록에도 위조 IP가 저장됐다. 새security.trusted_proxies설정(IP/CIDR/private키워드, 기본값 빈 문자열)에 등록된 주소에서 온 요청만 헤더를 신뢰하고 전달 체인을 peer 쪽부터 거꾸로 훑어 신뢰 프록시가 아닌 첫 hop을 채택하도록internal/app/clientip.go를 추가했으며, 해결된 주소를 request context에 실어 기존r.RemoteAddr사용처(감사/세션/동의 약 70곳)를clientIP(r)로 통일했다. 검증은go vet ./..., docker postgres:16-alpine을 띄운VISITFLOW_TEST_DSN전체 통합 테스트 3회 연속 통과(신규 단위 테스트 6개 + 위조 헤더로 요청 한도 우회를 막는 통합 테스트 1개 포함),npm ci && npm run build로 확인했다. 마이그레이션 0011, 관리자 설정 화면 필드, README·ADMIN_GUIDE 안내를 함께 추가했다. - 보류 아이디어: CSV/XLSX 가져오기 파서(
visitorInputsFromRows) 엣지케이스 단위 테스트 보강 /bestAcceptLanguage의q=0·q>1처리 정정 //metrics토큰 상수시간 비교와 요청 한도 적용 검토 / 설정 내보내기 파일을 되돌려 넣는 가져오기 경로와 스키마 검증 / 감사 로그 CSV 내보내기의 스트리밍·메모리 사용량 점검
weekly
- 선택: 주 시작 요일 변경 후 이미 제출한 팀원에게 작성 권고 메일이 가는 버그 수정 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공 — PR https://github.com/hkjang/weekly/pull/1 (예산 $3 소진으로 에이전트가 원장을 못 남겨 러너가 대신 기록)
- 요약: queueTeamReminders/reminderStillWanted 가 week_start 정확일치로 묻던 것을 weekCoveringDays(기간 겹침)로 통일. 회귀 테스트 2개 추가. 저장소 루트에 잘못 들어온 middleware.go 사본 파일 삭제. WEEKLY_TEST_POSTGRES_DSN 으로 실제 DB 테스트 통과 확인.
- 보류 아이디어: (기록 전 예산 소진)
- 릴리즈: v0.281.0 (2026-09-02)
Invenqor
- 선택: Query DSL의 시간 값을 DB에 넘기기 전에 시각으로 해석 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
querydsl은"now - 24h"상대 시간만time.Time으로 바꾸고 나머지 시간 값은 문자열 그대로 SQL 파라미터로 넘겼다. PostgreSQL에서는last_seen_at < "2026-13-45"가 statement를 실패시켜 HTTP 500(실제로 재현 확인)이 되고, SQLite fallback에서는"2026-01-31T09:00:00Z"가 컬럼에 저장된"2026-01-31 09:00:00 +0000 UTC"와 바이트 비교되어 같은 날짜의 모든 행이 경계 반대편으로 넘어갔다. 이제now,now - <duration>, 드라이버가 만들어내는 절대 시각 레이아웃만 받아time.Time으로 컴파일하고, 해석되지 않는 값은 값 자체를 인용한 400INVALID_QUERY로 거절한다./query/validate도 파싱만 하지 않고 컴파일까지 하도록 맞춰 실행 결과와 판정이 갈리지 않게 했다. 검증: 신규 querydsl 단위 테스트와/api/v1/query/execute·/validate통합 테스트(엔드포인트에 기존 테스트가 전무했음)를 추가하고 수정 전에 실패·수정 후 통과를 SQLite와 실제 PostgreSQL(scripts/test-postgres.sh) 양쪽에서 확인, 전체go test ./...(두 모드),go vet,go build,gofmt통과. - 보류 아이디어:
- Query DSL에 OR/괄호 지원 추가 (가치 3 / 위험 4 / L — 문법·평가기 변경 범위가 큼)
attributes.*경로의 숫자 비교가 텍스트 비교로 처리되는 문제 (가치 2 / 위험 3 / M)- 콘솔 쿼리 패널의 문법 참조 렌더링 테스트 보강 (가치 2 / 위험 1 / S)
/api/v1/external/query/*API key 경로의 한도·감사 로그 커버리지 (가치 3 / 위험 2 / M)
vibe-coders
- 선택: 모델 가격 prefix 매칭 비결정성 수정 (가치 5 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
audit.lookupPrice가 가격표 map을 range하며 첫 prefix 일치를 반환해, 내장 카탈로그에 서로의 prefix인 항목(gpt-4o vs gpt-4o-mini, claude-sonnet-4 vs claude-sonnet-4-5)이 많은 탓에 버전 접미사가 붙은 실제 모델 ID(gpt-4o-mini-2026-06-01)가 호출마다 최대 16.7배 다른 단가로 계산되던 버그를 고쳤다. 가장 긴(구체적인) prefix를 채택하도록 바꾸고, 같은 규칙을 손으로 복제해 동일 버그를 갖고 있던admin_explain.lookupModelPrice는 새로 export한audit.LookupPrice에 위임시켰다. 회귀 테스트를 추가해 옛 구현에서 실패(16250 vs 975)함을 확인했고, gofmt·go vet·go build·go test ./...·go test -race ./internal/audit ./internal/proxy·cmd/api-surface-audit모두 통과했다. - 보류 아이디어: redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / internal/config 패키지 테스트 부재 보강 /
EstimateTokens의[]rune(text)전체 복사를utf8.RuneCountInString으로 교체 / 가격표 키 정규화(소문자·trim)를 적재 시점에 일원화해 조회마다 재정규화 제거 / prefix 매칭에 경계 검사 추가로gpt-4가gpt-45에 잘못 매칭되는 것 방지
← 대시보드 · Atom 피드 · 원본 데이터 runs.jsonl