자율 개선 일일 보고 — 2026-09-03
요약. 2026-09-03에 자율 개선 에이전트가 28개 프로젝트에서 96회차를 돌려 63건을 릴리즈하고 25건은 머지만 했으며 8건은 변경이 없었고 실패는 0건이다. 릴리즈: AgentHub v0.228.0, Clustara v0.9.264, Invenqor v0.2.20, ReSSO v0.9.67, Vendra v0.7.37, ai-admin v1.2.3, appstore v2.1.3, dataworks v0.9.37….
- 96회차
- 28프로젝트
- 62배포 준비 완료
- 1릴리즈 진행 중
- 25병합 완료
- 0검토 대기
- 0검증 실패
- 8변경 없음
- 0실행 오류
회차
| 시각 | 프로젝트 | 결과 |
|---|---|---|
| 00:17 | AgentHub | 배포 준비 완료 merged PR #3, released v0.228.0 |
| 00:29 | Clustara | 배포 준비 완료 merged PR #3, released v0.9.264 |
| 00:39 | Invenqor | 배포 준비 완료 merged PR #3, released v0.2.20 |
| 00:46 | Quantoss | 병합 완료 merged PR #1, release skipped |
| 01:04 | ReSSO | 배포 준비 완료 merged PR #4, released v0.9.67 |
| 01:22 | Vendra | 배포 준비 완료 merged PR #103, released v0.7.37 |
| 01:36 | ai-admin | 배포 준비 완료 merged PR #3, released v1.2.3 |
| 01:47 | aiportal-front-admin | 병합 완료 merged PR #3, release skipped |
| 01:55 | aiportal-java | 병합 완료 merged PR #3, release skipped |
| 02:07 | aiportal-py | 병합 완료 merged PR #3, release skipped |
| 02:16 | appstore | 배포 준비 완료 merged PR #3, released v2.1.3 |
| 02:29 | dataworks | 배포 준비 완료 merged PR #2, released v0.9.37 |
| 02:40 | git-ctx | 병합 완료 merged PR #15, release missing |
| 02:56 | igame | 배포 준비 완료 merged PR #2, released v0.7.2 |
| 03:06 | kanpic | 배포 준비 완료 merged PR #3, released v0.230.0 |
| 03:22 | moina | 배포 준비 완료 merged PR #3, released v0.1.18 |
| 03:44 | moyro | 배포 준비 완료 merged PR #4, released v0.2.13 |
| 04:00 | muni | 배포 준비 완료 merged PR #3, released v0.24.0 |
| 04:16 | pii-masker | 배포 준비 완료 merged PR #3, released v1.0.6 |
| 04:28 | ptium | 배포 준비 완료 merged PR #3, released v1.69.21 |
| 04:36 | releasedock | 배포 준비 완료 merged PR #3, released v0.5.3 |
| 04:47 | relio | 배포 준비 완료 merged PR #3, released v1.11.10 |
| 05:00 | umm | 배포 준비 완료 merged PR #133, released v0.66.1 |
| 05:19 | vibe-coders | 배포 준비 완료 merged PR #9, released v0.82.0 |
| 05:26 | visitflow | 병합 완료 merged PR #3, release missing |
| 06:06 | weekly | 배포 준비 완료 merged PR #4, released v0.284.0 |
| 06:18 | AgentHub | 배포 준비 완료 merged PR #4, released v0.229.0 |
| 06:29 | Clustara | 배포 준비 완료 merged PR #4, released v0.9.265 |
| 06:39 | Invenqor | 배포 준비 완료 merged PR #4, released v0.2.21 |
| 06:43 | Quantoss | 병합 완료 merged PR #2, release skipped |
| 07:05 | ReSSO | 배포 준비 완료 merged PR #5, released v0.9.68 |
| 07:21 | Vendra | 배포 준비 완료 merged PR #104, released v0.7.38 |
| 07:35 | aiportal-front-admin | 병합 완료 merged PR #4, release skipped |
| 07:46 | aiportal-java | 병합 완료 merged PR #4, release skipped |
| 07:56 | aiportal-java | 병합 완료 merged PR #5, release skipped |
| 08:05 | appstore | 배포 준비 완료 merged PR #4, released v2.1.4 |
| 08:16 | dataworks | 배포 준비 완료 merged PR #3, released v0.9.38 |
| 08:26 | dataworks | 배포 준비 완료 merged PR #4, released v0.9.39 |
| 08:37 | igame | 배포 준비 완료 merged PR #3, released v0.7.3 |
| 08:49 | kanpic | 배포 준비 완료 merged PR #4, released v0.231.0 |
| 09:09 | moina | 배포 준비 완료 merged PR #4, released v0.1.19 |
| 09:27 | moyro | 배포 준비 완료 merged PR #5, released v0.2.14 |
| 09:38 | muni | 배포 준비 완료 merged PR #4, released v0.25.0 |
| 09:46 | pii-masker | 배포 준비 완료 merged PR #4, released v1.0.7 |
| 09:57 | ptium | 배포 준비 완료 merged PR #4, released v1.69.22 |
| 10:05 | releasedock | 병합 완료 merged PR #4, release missing |
| 10:16 | relio | 배포 준비 완료 merged PR #4, released v1.11.11 |
| 10:31 | umm | 배포 준비 완료 merged PR #135, released v0.66.3 |
| 10:47 | visitflow | 병합 완료 merged PR #4, release missing |
| 11:28 | weekly | 배포 준비 완료 merged PR #5, released v0.285.0 |
| 11:40 | AgentHub | 배포 준비 완료 merged PR #5, released v0.230.0 |
| 12:04 | Clustara | 배포 준비 완료 merged PR #5, released v0.9.266 |
| 12:20 | Invenqor | 배포 준비 완료 merged PR #5, released v0.2.22 |
| 12:49 | ReSSO | 병합 완료 merged PR #6, release missing |
| 13:03 | Vendra | 배포 준비 완료 merged PR #105, released v0.7.39 |
| 13:20 | ai-admin | 배포 준비 완료 merged PR #4, released v1.2.4 |
| 13:35 | aiportal-front-admin | 병합 완료 merged PR #5, release skipped |
| 13:47 | aiportal-front | 병합 완료 merged PR #1, release skipped |
| 13:56 | aiportal-java | 병합 완료 merged PR #6, release skipped |
| 14:06 | aiportal-py | 병합 완료 merged PR #4, release skipped |
| 14:18 | dataworks | 배포 준비 완료 merged PR #5, released v0.9.40 |
| 14:30 | git-ctx | 배포 준비 완료 merged PR #16, released v0.77.3 |
| 14:47 | igame | 배포 준비 완료 merged PR #4, released v0.7.4 |
| 14:58 | jupiq | 배포 준비 완료 merged PR #1, released v1.3.0 |
| 15:10 | kanpic | 배포 준비 완료 merged PR #5, released v0.232.0 |
| 15:33 | kanpic | 배포 준비 완료 merged PR #6, released v0.233.0 |
| 15:48 | moina | 배포 준비 완료 merged PR #5, released v0.1.20 |
| 16:10 | moyro | 병합 완료 merged PR #6, release missing |
| 16:29 | muni | 배포 준비 완료 merged PR #5, released v0.26.0 |
| 16:38 | pii-masker | 배포 준비 완료 merged PR #5, released v1.0.8 |
| 16:46 | ptium | 배포 준비 완료 merged PR #5, released v1.69.23 |
| 17:03 | releasedock | 배포 준비 완료 merged PR #5, released v0.5.5 |
| 17:17 | relio | 배포 준비 완료 merged PR #5, released v1.11.12 |
| 17:31 | umm | 배포 준비 완료 merged PR #137, released v0.67.1 |
| 17:50 | vibe-coders | 병합 완료 merged PR #11, release missing |
| 18:08 | visitflow | 배포 준비 완료 merged PR #5, released v2.6.2 |
| 19:17 | weekly | 배포 준비 완료 merged PR #6, released v0.286.0 |
| 19:47 | AgentHub | 배포 준비 완료 merged PR #6, released v0.231.0 |
| 20:00 | Clustara | 배포 준비 완료 merged PR #6, released v0.9.267 |
| 20:22 | Invenqor | 배포 준비 완료 merged PR #6, released v0.2.23 |
| 20:39 | Quantoss | 병합 완료 merged PR #4, release skipped |
| 20:51 | ReSSO | 병합 완료 merged PR #7, release missing |
| 21:14 | Vendra | 배포 준비 완료 merged PR #106, released v0.7.40 |
| 21:43 | ai-admin | 릴리즈 진행 중 merged PR #5, released v1.2.5, ASSETS MISSING |
| 21:58 | aiportal-front-admin | 병합 완료 merged PR #6, release skipped |
| 22:04 | aiportal-front | 병합 완료 merged PR #2, release skipped |
| 22:15 | aiportal-java | 병합 완료 merged PR #7, release skipped |
| 22:23 | aiportal-py | 변경 없음 no change |
| 22:35 | dataworks | 변경 없음 no change |
| 22:44 | git-ctx | 변경 없음 no change |
| 22:53 | igame | 변경 없음 no change |
| 23:04 | jupiq | 변경 없음 no change |
| 23:21 | kanpic | 병합 완료 merged PR #7, release missing |
| 23:34 | moina | 변경 없음 no change |
| 23:43 | moyro | 변경 없음 no change |
| 23:50 | muni | 변경 없음 no change |
무엇을 왜 바꿨나 (원장 발췌)
AgentHub
- 선택: DLP에서 한 값이 두 등급으로 집계되고 잘못된 이름으로 마스킹되던 문제 수정 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공 — 커밋 493029f (auto/2026-09-03-0010)
- 요약: 검출기들이 각자 전체 페이로드를 독립적으로 매칭해 같은 값을 두 개가 가져갈 수 있었습니다. 계좌번호는 체크섬이 없고 자릿수 묶음만 보므로 카드번호(4111-1111-1111-1111)·사업자등록번호(220-81-62517)가 전부 계좌번호로도 잡혀 운영자가 읽는 “계좌번호 N건”이 부풀려졌고, 더 나쁘게는 계좌번호 검출기가 사업자등록번호보다 먼저 돌아서 회사의 등록번호가
[계좌번호 삭제됨]으로 나갔습니다 — 당사자가 읽는 유일한 자리에서 자기 데이터에 대해 틀린 말을 한 셈입니다. 검출기 목록 위 주석은 이미 “구체적인 것이 먼저 돌아 일반적인 것이 그 매치를 삼키지 않는다”고 적혀 있었지만 그렇게 하는 코드는 없었습니다.Scan이 각 검출기가 가져간 바이트 구간을 기록해 이미 claim된 후보를 건너뛰게 하고, 사업자등록번호를 계좌번호 앞으로 옮겼습니다(off인 등급은 아예 돌지 않으므로 claim도 하지 않아, 계좌번호만 켠 사이트는 그대로 탐지됩니다). 검증: 중복집계·마커 이름·공존 케이스 테스트 3개 추가, go vet ./…, go test -race ./cmd/… ./internal/…, web npm ci+lint+build,scripts/release-catalog-images.sh check-versions모두 통과. internal/dlp는 런타임 base 이미지에 들어가므로 BASE_VERSION 0.17.0으로 상향(5곳). - 보류 아이디어:
- Scan의 redaction이 위치가 아니라 strings.ReplaceAll로 치환해, 같은 문자열이 다른 맥락에 있으면 함께 지워짐 (3/3/M)
- korean.EndsInConsonant가 괄호·따옴표로 끝나는 값에서 조사를 잘못 고름 (2/2/S)
- captureHandler.WithGroup이 그룹 이름을 버려 서로 다른 그룹의 같은 키가 충돌 (2/1/S)
- runInfoCommand가 version 외 인자를 run()으로 흘려보내
agenthub --help가 DB 오류로 실패 (2/1/S)
- 릴리즈: v0.228.0 (2026-09-03)
Clustara
- 선택: 셀프서비스 카탈로그 검증 게이트의 구멍 4개 수정 +
internal/servicecatalog첫 단위 테스트 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
internal/servicecatalog(158줄, 단위 테스트 0개)의ValidateInput은 셀프서비스 요청 본문의values(전부 사용자 입력)가 운영자가 승인·apply 하는 manifest 로 바뀌는 사이의 유일한 게이트인데 4가지가 그냥 통과했다 — ①port미검증(그대로containerPort:에 렌더 → 0·-1·70000 이 검증·정책 미리보기·승인을 다 통과하고 apply 시점에만 거절) ② 태그 없는 이미지가Contains(":latest")가드 우회(k8s 는 무태그를 latest 로 해석), 게다가 호출부 운영 digest 게이트도Contains(image, ":")전제 때문에 콜론 없는 이미지는 digest 요구 자체를 건너뜀 ③ 같은 substring 검사가tomcat:latest-jdk21같은 고정 태그와 digest 로 고정된repo:latest@sha256:...을 오탐 거절 ④0·0Gi가 유효 수량(storage 0 은 PVC 검증기가 거절, memory limit 0 은 첫 할당에서 OOM). 참조를[registry[:port]/]repo[:tag][@digest]로 분해하는 방식으로 바꾸고 port 범위·0 초과 수량을 추가했으며, 호출부(admin_k8s_services.go)의 운영 digest 게이트에서 콜론 전제를 제거했다. 검증: 신규 테스트 7개를 고치기 전 코드에 되돌려 붙여 4개가 각 결함을 지목하며 실패함을 확인했고go build ./...·go vet ./...·go test ./...전부 통과(21 패키지). 저장소 관례대로 AppVersion·changelog·docs 버전 마커를 v0.9.264 로 올렸다(release gate 테스트가 강제). - 보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ②detectScanner가 못 맞히면scanner="unknown"저장 후 trivy 파서 실행 — 저장값과 실제 파서 불일치 (가치 2 / 위험 1 / S) ③ GrypeNegligible심각도가Unknown으로 접혀 Low 미만 등급 정보 소실 (가치 2 / 위험 2 / S) ④collectBenchmarkResults의 Section 귀속 — 바깥 노드 라벨이 먼저 이겨 kube-bench Section 이 “1.1” 대신 상위 control 설명으로 채워짐 (가치 2 / 위험 2 / S) ⑤ 카탈로그 caller 의num()헬퍼가 문자열"0"·비숫자를 조용히 기본값으로 되돌림 — 잘못된 입력이 기본값으로 성공 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.264 (2026-09-03)
- 릴리즈: v0.9.264 (2026-09-03)
Invenqor
- 선택: 속성 조회가 찾으려는 키를 소문자로 바꿔버리던 문제 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
querydsl.Parse가 컬럼을 대소문자 구분 없이 받으려고 필드명 전체를strings.ToLower로 접었다. 그런데attributes.뒤는 컬럼이 아니라 저장된 JSON 문서의 키이고 JSON 키는 대소문자를 구분한다. 자산 API·MCP 도구·타 시스템 임포트로 만든 자산은 호출자가 쓴 키를 그대로 갖고 있어 대문자가 흔한데,attributes.assetTag는'{assettag}'(PostgreSQL#>>)와'$.assettag'(SQLitejson_extract)로 컴파일되어 어느 문서에도 없는 경로를 물었다. 결과는 오류 없는 HTTP 200 빈 목록이었고/query/validate는 그 표현식을 이미 valid라고 답한 뒤였다. 이제attributes.접두사만 접고 키는 입력된 대소문자를 유지하며(컬럼명은 그대로 대소문자 무시), 콘솔이 렌더링하는 문법 참조에도 대소문자 구분을 명시했다. 검증: querydsl 단위 테스트 2개와/api/v1/query/execute·/validate통합 테스트 2개를 추가해 수정 전 실패·수정 후 통과를 확인하고,go test ./...를 SQLite fallback과 실제 PostgreSQL(scripts/test-postgres.sh) 양쪽에서 통과,go vet,go build,gofmt통과. - 보류 아이디어:
attributes.*경로의 숫자 비교가 텍스트 비교로 처리되는 문제 (가치 3 / 위험 3 / M)- 잘못된 scope로 키를 만들면 400 INVALID_SCOPES가 아니라 403 SCOPE_ESCALATION이 반환됨 (가치 2 / 위험 1 / S)
- 마지막 scope를 제거하면 scope가 하나도 없는 키가 남음(생성은 1개 이상을 요구) (가치 2 / 위험 2 / S)
/api/v1/external/query/*API key 경로의 한도·감사 로그 커버리지 (가치 3 / 위험 2 / M)
- 릴리즈: v0.2.20 (2026-09-03)
Quantoss
- 선택: 설정 검증(
config.Validate) 추가 — .env 오타를 시작 시 차단 (가치 5 / 위험 2 / 작업량 M) - 결과: 성공
- 요약: 실거래 봇인데
.env값에 대한 범위 검증이 전혀 없어, 오타 하나가 조용히 사고로 이어질 수 있었다(QUANTOSS_POLL_SECONDS=0→ 에이전트 티커 패닉,QUANTOSS_FLATTEN_AT=25:15→ 강제 청산이 영영 발동 안 함,QUANTOSS_DAILY_LOSS_LIMIT_PCT=-3→ 첫 거래부터 진입 차단, 수수료/거래세를 비율 아닌 %로 입력 → 손익 왜곡).Config.Validate()로 리스크%·비중·시각 순서(개장 ≤ 진입 시작 < 진입 종료 ≤ 강제 청산)·가격대·비용 단위·상관 임계값·시세 장애 임계값을 검사하고 문제를 모두 모아 한 번에 보고하도록 했고,ParseClock에 HH 0~23 / MM 0~59 범위 검사를 넣었다. 호출 지점은FromEnv()끝과optimize파라미터 자동 적용 직후(손상된 params.json 방어). 검증:gofmt -l(clean) ·go vet ./...·go test ./...전부 통과, 신규 테스트 5개(기본 설정 통과 / 위험값 33종 거부 / 다중 문제 동시 보고 / ParseClock 범위 / FromEnv 거부) 추가. 커밋 110975b. - 보류 아이디어:
- GitHub Actions CI 없음 —
go build/go vet/go test/gofmt -l워크플로 추가 (가치 4 / 위험 1 / S) internal/journal·internal/notify테스트 0건 — Summary/MaxDrawdown, Load 날짜 필터, Notifier httptest 테스트 (가치 3 / 위험 1 / S)- README “알려진 한계” 가 stale — WebSocket 미사용이라 적혀 있으나 이미 구현됨 (가치 2 / 위험 1 / S)
risk.RecordPartial은 DailyPnL 만 더하고 일일 손실 한도를 재평가하지 않음 — 절반 익절 손실이 한도를 넘겨도 halt 안 됨 (가치 3 / 위험 2 / S)market.FetchCandles종료 조건page[0].TS.Before(cutoff) || !page[0].TS.After(cutoff)가 중복 — 단순화 + 페이지네이션 테스트 (가치 2 / 위험 1 / S)
- GitHub Actions CI 없음 —
ReSSO
- 선택: userinfo가 Role을 못 읽었을 때 “Role 없음”으로 답하던 문제 수정 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공 (커밋 c5fb7b4)
- 요약:
roles스코프 뒤의 두 조회(RealmRolesForUser,ClientRolesForUser)가 에러를_로 버려서, 조회 실패가realm_access.roles: []+ 200으로 나갔다 — 서비스가 “이 계정은 아무 Role도 없다”고 단언하는 것이고, 관리자가 Role을 전부 회수한 경우와 글자 그대로 구별되지 않는다. 같은 스코프의 같은 두 호출이 토큰 발급(IssueUserTokens)에서는 이미 요청을 실패시키므로, userinfo만 못 읽은 Role을 에러가 아닌 claim으로 바꾸고 있었다. 이제 500server_error로 거절하고 어느 조회가 실패했는지 로그에 남긴다 — 여기의 다른 거절이 쓰는 401을 일부러 쓰지 않았다. 토큰은 멀쩡한데 invalid_token이라 하면 RP가 멀쩡한 자격증명을 버리고 사용자를 로그아웃시킨다./api/v1/me도 같은 에러를 버리고 있어 함께 고쳤다. 검증: 새 연동 테스트가 두 Role 테이블을 차례로 치우며(테스트마다 전용 스키마라 안전) 수정 전 코드에서 실제로 두 건 모두 실패함을 확인했다(answered 200 and map[realm_access:map[roles:[]]...]).make test전체 통과 —go test -race ./...전 패키지 ok, 연동 테스트 SKIP 0건(httpserver 76s / store 77s),go vet,golangci-lint(0 issues),govulncheck(0),npm run lint,npm run test(22파일/104테스트),npm run build. 빌드가 만든webui/dist/index.html변경은 되돌렸다. - 보류 아이디어:
- UserInfo POST에서 form-encoded
access_token파라미터 수용 (RFC 6750 §2.2). 현재는 Authorization 헤더만 읽는다 (가치 2 / 위험 1 / S) SessionByToken이locked_until을 보지 않는 것은 의도로 보인다 — 잠금은 무차별 대입 방어이고, 이를 세션 종료로 확장하면 공격자가 남의 비밀번호를 틀리는 것만으로 피해자를 계속 로그아웃시키는 DoS가 된다. 막지 말고 문서화하는 쪽으로 결론낼 것 (가치 2 / 위험 1 / S)authorization이id_token_hint·max_age를AuthorizationRequest에 저장하지 않아, 로그인 폼을 거친 뒤에는 hint가 지목한 계정과 다른 계정으로 로그인해도 코드가 나간다 (가치 2 / 위험 2 / M)oidcLogout이id_token_hint의 토큰 타입(Extra.Type)을 확인하지 않아 Access Token도 hint로 받아들인다 (가치 2 / 위험 1 / S)scripts/test-services.sh가 PostgreSQL 포트 충돌 시 컨테이너를 Created 상태로 남기고 다음 실행에서 인증 실패로만 드러난다 — 포트 점유를 감지해 알려주기 (가치 2 / 위험 1 / S)
- UserInfo POST에서 form-encoded
- 릴리즈: v0.9.67 (2026-09-03)
Vendra
- 선택: 요청 본문의 짧은 자유 텍스트(레코드 이름·제목·코드)를 길이 검사 없이 text 컬럼에 넣던 쓰기 경로 전부 수정 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공 (commit ba6e366)
- 요약: 날짜·숫자 스윕과 같은 기준(철자가 아니라 연산 — 요청 본문의 짧은 문자열이 레코드를 표시하는 text 컬럼에 도달)으로 훑었다. maxIdentifierLen은 공급업체 10개 필드, 리스크 유형, 문서 이름에만 걸려 있었고 나머지는 전부 무방비였다. text에는 길이가 없어 20,000자 계약 제목이 그대로 저장된 뒤 레코드를 열지도 않은 사람의 목록·드롭다운·내보내기·감사 로그를 전부 밀어냈고, 구매 원장의 품목명·분류는 Spend 차트의 그룹 키라 범례가 그 길이에 맞춰졌으며, 표시 이름은 그 계정이 남기는 모든 감사 줄과 결재 단계의 서명이었다. 특히 포털 자가등록은 createSupplier가 처음부터 검사해 온 suppliers.name 컬럼을 아무도 재지 않은 두 번째 문으로 썼는데, 그 문이 바로 외부인이 지나는 문이다. rows.go에 validDateFields·validNumberFields 옆에 validTextFields를 두어 거절이 고칠 입력란을 지목하게 했고, 호출자가 하나뿐이던 overlongField를 대체했다. 검증: docker postgres:16-alpine으로 CI와 동일한 세 DSN을 걸고
go test ./internal/... ./cmd/...전체 통과, gofmt·go vet 무결. 패키지를 파싱하는 TestEveryRequestLabelIsBounded(검사를 뺀 필드의 사유는 unboundedByDesign에 명시)와 17개 엔드포인트를 실제로 호출하는 TestEveryLabelOnAWriteIsBounded를 추가했고, 포털 프로필 가드를 되돌리면 둘 다 실패하는 것까지 확인했다. - 보류 아이디어:
- CI의 go job이
go test ./internal/...만 돌려./cmd/...를 빼놓음 — Makefile/README와 불일치 (가치 2 / 위험 1 / S) web프론트엔드 테스트 커버리지가 9개 파일뿐 — Sourcing/Objects/Admin 페이지에 테스트 없음 (가치 3 / 위험 1 / L)- 업무 객체의 data jsonb 블롭에는 어떤 검증도 없음 — 폼이 쓰는 키(품목·수량·단가)가 API로는 무제한 (가치 3 / 위험 2 / M)
Makefile의 VERSION(0.6.21)이 README의 릴리스 예시(0.7.26)와 어긋남 — 릴리스 문서 정합성 (가치 2 / 위험 1 / S)- 공급업체 수정(PATCH /suppliers/{id})은 tradingSince·annualSpend·businessNumber를 갱신하지 않음 — 편집 폼이 보내지 않으므로 의도로 보이나 API 전용 클라이언트에는 무응답 (가치 2 / 위험 2 / S)
- CI의 go job이
- 릴리즈: v0.7.37 (2026-09-03)
ai-admin
- 선택: AI 공급자
availableModels배열 검증·정규화 추가 (가치 3 / 위험 2 / 작업량 S) - 결과: 성공
- 요약:
providers.go의availableModels는 항목 수도 항목 길이도 전혀 검증하지 않아 2MB 본문 한도까지 임의 크기 JSON이ai_admin.ai_provider.available_modelsjsonb에 저장되었고, 저장된 값은/api/v1/models·/api/v1/ai/catalog·chat 허용 모델 검사가 호출될 때마다 언마샬+정렬되었다.chatCompletions가 240자를 넘는 모델 이름을 이미 거부하므로 그보다 긴 이름은 저장되어도 쓸 수 없는 값이었다.validateProviderRequest에 항목 수 200개·이름 240자 한도를 넣고,normalizeProviderRequest에서 읽는 쪽이 이미 쓰던uniqueStrings(트림·빈 항목 제거·중복 제거·정렬)를 저장 전에도 적용했다. 세 경로(생성, 수정, 승인 적용workflow.go:649)가 모두 이 두 함수를 거치므로 한곳 수정으로 전부 덮인다. 정규화 결과와 200개/240자 경계값 단위 테스트를 추가해 수정 전 코드에서 실패하는 것을 실제로 확인했고,go vet·go build ./...·go test -count=1 ./...·scripts/verify-version.sh·npm test(55개)·npm run build를 모두 통과시켰다. 저장소 관례에 따라 VERSION을 1.2.3으로 올리고 CHANGELOG·README·docs·web 버전 메타데이터를 함께 맞췄으며,docs/api.md에 새 한도를 명시했다(직전 릴리즈 커밋들과 동일하게internal/ui/dist는 재빌드하지 않음). - 보류 아이디어:
safeCSVCell이 OWASP가 함께 권고하는 tab(0x09)·CR(0x0D) 선행 문자를 중화하지 않아,\t=cmd|...같은 값이 기존 방어를 우회함(Gocsv.Writer는 tab만 있는 필드를 인용하지 않음) (가치 3 / 위험 1 / S)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)- 공급자 HTTP 통합 테스트가 없어
POST/PATCH /api/v1/ai/providers의 낙관적 잠금·검증 경로가 단위 테스트로만 검증됨 (가치 3 / 위험 1 / M)
- 릴리즈: v1.2.3 (2026-09-03, 태그 사후 푸시)
- 릴리즈: v1.2.3 (2026-09-03)
aiportal-front-admin
- 선택: 목록 화면의 stale response가 최신 결과를 덮어쓰는 race 방지 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: CatalogView·DirectoryView·ContentAccessView의 조회 함수들이 요청 순서를 추적하지 않아, PaginationBar 버튼·탭 전환·행 선택처럼
:disabled="loading"이 걸리지 않은 동선에서 요청이 겹치면 늦게 도착한 이전 응답이 최신 결과를 덮어썼다(다른 페이지 목록 표시, 취소한 상세 재출현, 지나간 오류 메시지 노출). 새shared/async.ts의createRequestGuard()로 ticket을 발급해 최신 요청의 결과만 상태에 반영하고, 목록을 다시 읽을 때는 진행 중인 상세 요청을invalidate()로 버리며, UUID 검증 실패 같은 조기 반환 경로에서도 loading을 정리하도록 했다. 검증은npm run verify(typecheck + vitest 35개 통과, 기존 30개 + guard 단위 테스트 5개로 응답 역순 도착 시나리오 포함)와npm run build(build → runtime-config 검증 → offline 검사 → integrity manifest 19개) 전체 통과. 커밋 bbab374. - 보류 아이디어:
unwrapPageEnvelope가 서버 pageInfo 누락 시 요청한 pageNum/pageSize를 잃고 1/10으로 되돌려 페이지 이동이 막히는 문제 (가치 3 / 위험 1 / S)ensureAdminSession(force=true)가 진행 중인 비강제 요청 promise를 그대로 반환하는 재진입 버그 (가치 3 / 위험 2 / S)monitoringParser.findRows재귀에 깊이 제한 추가 및 테스트 (가치 2 / 위험 1 / S)shared/format.ts단위 테스트 공백 보강 (가치 2 / 위험 1 / S)- 루트 앱의
crypto-jslocal tarball 의존성 제거로 clean install 복구 — 현재npm ci자체가 불가해 검증 비용 큼 (가치 4 / 위험 4 / M)
aiportal-java
- 선택: 페이징 파라미터 하한 미보정으로 인한 목록 조회 오류 수정 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
PageVo.pageNum기본값이 0 이라pageNum을 생략한 요청에서getOffset()이-pageSize를 반환해OFFSET을 쓰는 20여 개 매퍼 쿼리가 PostgreSQL “OFFSET must not be negative” 로 실패하고,PagingUtil.paginate()는 음수fromIndex로subListIndexOutOfBoundsException 을 던졌습니다(예:GET /app/chat/list,/toolsOCR·STT 목록). 이미QuestionLogServiceImpl,StorageBoxServiceImpl,ToolsServiceImpl,AppStatisticsServiceImpl4곳에 같은 보정이 개별 복사돼 있어 중앙화가 맞다고 판단했습니다.getOffset()과paginate()에서 pageNum/pageSize 를 1 이상으로 보정하고 long 연산으로 오버플로를 막았으며,ToolsMapper의 인라인((#{pageNum} - 1) * #{pageSize})2건을 다른 매퍼와 동일하게#{offset}로 통일했습니다.AthenaServiceImpl의getPageNum() != 0센티널 의미를 깨지 않으려고 필드 기본값과 getter 는 건드리지 않았습니다. 검증은PageVoTest(5) +PagingUtilTest(7) 추가 후sh gradlew check build실행으로 했고 47개 테스트 전부 통과했습니다. 커밋 44052f5. - 보류 아이디어:
ControllerLogAspect.logControllerCud가getAuthentication()null 일 때 NPE — @AfterReturning 이라 성공 응답이 500 으로 바뀜 (가치 3 / 위험 1 / 작업량 S)getDataMap이 null 반환 시dataMap.get("active")NPE — 명시적 예외 처리로 정리 (가치 3 / 위험 2 / 작업량 S)JwtAuthenticationFilter의 CORS 허용 Origin 30여 개 하드코딩을 설정(yaml)으로 외부화 (가치 3 / 위험 3 / 작업량 M)LIMIT #{pageSize}에 음수/0 pageSize 가 그대로 전달되는 경로 보정 (가치 2 / 위험 2 / 작업량 S) — setter 보정 시 pageSize 복사 경로 영향 확인 필요ValidUtil,ApiCallUtil단위 테스트 공백 보강 (가치 2 / 위험 1 / 작업량 S)
aiportal-py
- 선택: 외부 HTTP 호출 timeout 누락 일괄 수정 (감사 A-107) (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
requests는 timeout 이 없으면 무한 대기하는데, FastAPI 라우트와 LangGraph 노드가 이 호출을 동기로 하므로 상대 서비스가 멈추면 worker 가 그대로 묶인다. 설정 모듈에 의존하지 않는util/http_timeouts.py(DEFAULT 5/30s, LLM 5/300s, FILE 5/600s, 환경변수로 조정 가능)를 추가하고 timeout 이 없던 호출 53곳(api.py, service/, util/, pipeline/*, config/eval_class.py)에 용도별 값을 지정했다. AST 로 저장소 전체의requests/Session호출을 검사해 timeout 누락·timeout=None재발을 막는tests/unit/test_http_timeouts.py를 추가했고,python -m pytest450 passed(기존 352 → 450),python -m pyflakes .undefined name 0건 확인. docs 3종(CURRENT_STATE_AUDIT/TESTING/CONFIGURATION) 갱신. 커밋be6f163. - 보류 아이디어:
- A-104 Milvus filter 표현식 직접 조립 → 안전한 expression builder + 입력 검증 — 가치 4 / 위험 3 / M
- A-002 startup 무기한 대기(policy token) 에 timeout/backoff 추가 — 가치 4 / 위험 3 / M
collection_script/*의 import-time collection create/drop 부작용 제거 (감사 A-006) — 가치 4 / 위험 3 / M- A-206 추천 질문 캐시 무한 append → 원자적 교체·중복 제거 — 가치 3 / 위험 2 / S
- A-107 후속: 재시도·circuit breaker·공용 requests.Session 도입 — 가치 3 / 위험 3 / M
appstore
- 선택: maxOutputTokens 미설정 provider의
max_tokens: 0전송 버그 수정 (가치 4 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
store.validateAIProvider와httpapi.minimumPositive는maxOutputTokens = 0을 “AppStore 측 출력 제한 없음”으로 취급하는데,ai.Stream은 그 값을 그대로 upstream payload의"max_tokens"에 넣어 OpenAI 호환 서버가 거부하는max_tokens: 0을 보냈다(해당 provider로는 모든 AI 요청이 HTTP 400).input.MaxTokens > 0일 때만 필드를 포함하도록 고쳤고, 함께 미뤄 뒀던sanitizeProviderError의 512 byte 절단이 UTF-8 rune 경계를 깨뜨리던 문제도 마지막 불완전 rune을 잘라내도록 수정했다. 검증: payload를 캡처하는 httptest 기반 테이블 테스트와 rune 경계 테스트를 추가해 수정 전 코드에서 두 테스트가 모두 실패하는 것을 확인한 뒤go test -race(전체 패키지),go vet ./...,gofmt -l,go build ./cmd/server,check-env-contract.sh,check-docs.sh통과. Frontend는 변경하지 않아 npm 검사는 생략. 커밋 4450a58. - 보류 아이디어:
internal/httpapi커버리지 5.5% — DB 없이 테스트 가능한SPAHandler, middleware,DecodeJSON,parseID등에 단위 테스트 추가. 가치 3 / 위험 1 / Mai.consumeOpenAIStream이 SSE 다중data:줄을 이어 붙이지 않아 한 이벤트를 여러 줄로 쪼개 보내는 provider에서 JSON 디코드 실패. 가치 3 / 위험 2 / M- rate limiter의
Retry-After: 60고정값을 현재 분 윈도우 잔여 시간으로 계산. 가치 2 / 위험 1 / S httpapi.validHTTPURL강화(호스트의 공백·제어문자 거부, 포트 검증). 가치 2 / 위험 2 / Shttpapi.SPAHandler.fmtInt는strconv.Itoa재구현 — 표준 라이브러리로 교체. 가치 1 / 위험 1 / S
- 릴리즈: v2.1.3 (2026-09-03)
dataworks
- 선택: GitHub Actions CI 게이트 추가 +
go vet ./...잔여 4건 정리(그중 1건은 실제 응답 버그) (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약: 저장소에 워크플로가 하나도 없어 README와
cmd/api-surface-audit주석이 전제하는 검사들이 아무것도 강제되지 않았다..github/workflows/ci.yml에 Go 잡(build·vet·test·api-surface-audit)과 Web 잡(npm ci·lint·test·build)을 추가하고 README 에 백엔드 검증 명령을 문서화했다. vet 를 하드 게이트로 만들기 위해 잔여 4건을 정리했는데,admin_k8s_collect_slo.go의 json 태그 중복은 실제 버그였다:failView가store.K8sCollectRun과analyzer.CollectGap을 같은 깊이로 임베드해 두category태그가 충돌, encoding/json 이recent_failures[].category를 통째로 누락시켜 Collect Gap RCA 화면에 원인이 표시되지 않았다. 분류 필드를 평탄하게 명시하고 회귀 테스트를 추가했으며, 옛 구조로 되돌려 테스트가category=nil로 실제 실패하는 것까지 확인했다. 검증:go build ./...·go vet ./...(0건)·go test ./...전체 통과,go run ./cmd/api-surface-auditgap 0, web 에서npm ci && npm run lint && npm test && npm run build전부 통과(vitest 14건). - 보류 아이디어:
gofmt -l미정렬 64개 파일 일괄 포맷(현재 CI 게이트 제외) /parseWindow가 음수·과대 duration 을 그대로 받아 미래 시각 since 를 만드는 엣지케이스(30여 개 엔드포인트 영향) /internal/dataworks패키지 테스트 커버리지 보강 / Playwright e2e 를 서비스 컨테이너 기반 CI 잡으로 편입 /npm run build가 추적 파일web/dist/.gitkeep을 삭제하는 문제를 vite 설정으로 해결 - 릴리즈: v0.9.37 (2026-09-03)
git-ctx
- 선택: 권고(advisory) 판정용 버전 비교의 세 가지 오판 수정 — 빌드 메타데이터·프리릴리스 숫자 필드·상한 범위 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공 (commit 3431db0)
- 요약:
find-dependency-usage가 “수정 버전 기준 영향·안전·판정 불가”를 나눌 때 쓰는internal/manifest/version.go에 오판 세 건이 있었습니다. (1)v2.0.0+incompatible의 빌드 메타데이터를 프리릴리스로 읽어, 수정 버전에 이미 올라가 있는 Go 모듈 전부가 “영향”으로 보고됐습니다. (2) 프리릴리스 꼬리를 문자열로 비교해rc.9 > rc.10이 되어, rc.10에서 고쳐진 취약점을 rc.9 저장소가 “안전”으로 응답했습니다(허위 안전). (3)requests<3같은 상한 선언을 정확한 핀으로 읽어, 3.0.0에서 고쳐진 건에 대해 도달 불가능한 버전을 근거로 “안전”이라 답했습니다. 각각 메타데이터 선제거, semver 식별자 비교(숫자는 숫자로, 숫자<영숫자, 긴 꼬리 우선), 방향을 가진boundKind(exact/floor/below/atMost) 도입으로 고쳤고 상한은 수정 버전 이하일 때만 “영향”, 그 위로는 “판정 불가”로 둡니다. 검증: 새 테스트 3건(TestBuildMetadataDoesNotOrderARelease,TestPrereleaseFieldsCompareNumerically,TestCeilingResolvesDownwards) 추가 후gofmt -l,go vet ./...,go build -tags sqlite_fts5 ./...,go test -tags sqlite_fts5 ./...전체 통과, manifest·search 패키지는-race도 통과. - 보류 아이디어: (1)
clampResponse가 “예산에 맞춰 잘랐다”면서 실제로는 truncation notice 때문에 예산을 최대 ~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
- 선택: 검색어의 ILIKE 와일드카드(
%,_) 누출 차단 (가치 4 / 위험 2 / 작업량 S) - 결과: 성공
- 요약: 관리자 사용자/감사 로그 목록, 감사 CSV 내보내기, 게임 카탈로그 네 곳이 검색어를
'%'||$1||'%'로 그대로 이어 붙여,50%·user_id·user agent의 Windows 경로처럼%/_가 든 검색어가 와일드카드로 새어 들어가 필터가 적용된 것처럼 보이면서 전체 행을 돌려줬다(%하나면 전부 매치).searchPattern(internal/api/api.go)이 백슬래시와 두 와일드카드를 이스케이프해 패턴을 만들고 빈 검색어는 빈 문자열로 남겨 기존$1=''필터 건너뛰기를 유지하도록 고쳤으며, 카탈로그는 정확 일치인 태그 비교용으로 원문 검색어를 별도 파라미터로 유지했다. 검증: gofmt 검사,go vet,go test,go test -race전체 통과,npm --prefix web run lint과npm --prefix web test209개 통과. - 보류 아이디어: (1)
migrations패키지에 파일명 규약·번호 연속성 테스트 추가 — 사전식 정렬이라10_x.sql이 들어오면 순서가 깨진다. (2)internal/database커버리지 0% —Migrate의 체크섬 불일치 경로 검증. (3)clockMinutes가09:+5같은 부호 붙은 값을 받아들이는 입력 검증 강화. (4)cmd/igame커버리지 13.7% — 기동/종료 경로 테스트 보강. (5)listUsers만 검색어를 TrimSpace 하지 않아 다른 목록과 동작이 다른 점 정리. - 릴리즈: v0.7.2 (2026-09-03)
kanpic
- 선택: 외부 호출 캐시가 정책 검사를 가로막지 않고 상한을 지킨다 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
internal/external/fetcher.go의 답 캐시에 겹쳐 있던 세 가지를 함께 고쳤다 — (1) 허용 목록·https·주소 형식을 보는 정책 검사(CheckURL)를fetch에서one의 맨 앞으로 올려 캐시 바깥에 두어, 관리자가external.allowed_hosts를 고치면cache_seconds(기본 5분)를 기다리지 않고 곧바로 통하고 목록에서 뺀 호스트의 지난 답도 캐시에서 나오지 않는다, (2) 만료 항목을 지우는 자리가 없어 무한히 자라던 지도에store()를 두어 새 답이 들어올 때 만료분을 쓸고 512개 상한에서 가장 먼저 만료될 항목을 내보낸다, (3)short()가 160바이트에서 잘라 한글을 반 글자로 남기던 것을 160글자·글자 경계로 고쳤다. 검증: 새 테스트 3개(TestPolicyIsNotCachedInEitherDirection,TestCacheSweepsExpiredAndStaysBounded,TestShortKeepsKoreanLettersWhole)와gofmt -l,go vet,go build ./...,go test ./...(전체 통과),scripts/check-release-docs.sh,scripts/check-commit-identities.sh. 커밋 1개(1266384), 릴리즈 노트 v0.230.0 과 README VERSION 갱신. 웹 변경이 없어 npm 검사는 돌리지 않았다. - 보류 아이디어:
NUMBERVALUE미구현(#NAME?),VALUE("12:00")이 시각을 읽지 못한다.TRUNC이math.Pow/이진 실수로 자리를 잘라TRUNC(2.29,1)같은 값에서 어긋날 수 있다. ROUND·CEILING 이 쓰는decimalRound로 통일할 것.csvNumber가 IMPORTDATA 의"1,200"·"12%"·앞뒤 통화 기호를 글자로 남긴다. 시트가 읽는 수의 범위와 맞출지 검토할 것.- 외부 호출 캐시 키가 함수·주소만 담아,
external.max_kb를 올려도 캐시가 남아 있는 동안은 “크기를 넘습니다” 가 그대로다(정책 값도 키에 넣거나 크기 오류는 담지 않기). internal/external의 동시 접근에-race시험이 없다. 한 번의 재계산이 여러 주소를 동시에 부르는 길을 시험으로 고정할 것.
- 릴리즈: v0.230.0 (2026-09-03)
- 릴리즈: v0.230.0 (2026-09-03)
moina
- 선택: 작성 뒤 본문이 바뀐 Moin에 “수정됨” 표시 + 상대 시각에 정확한 시각 노출 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: API·프런트 타입 모두
updatedAt을 이미 실어 나르는데 어떤 화면도 표시하지 않아, 작성자가 공개 Moin을 다시 쓰면 이미 Echo·반응한 사람이 본문 변경을 알 수 없었습니다.utils/format.ts에moinEditedAt을 추가해updatedAt이createdAt보다 1초 이상 뒤일 때만 수정으로 보고, MoinCard에 정확한 수정 시각 tooltip이 붙은수정됨표시를 렌더링했으며 MoinCard·알림 목록의 상대 시각을<time dateTime title>으로 감싸 보류 목록에 있던 정확한 시각 부재도 함께 해결했습니다. 또한 승인 게시 SQL(workflow.go)이updated_at을 함께 옮겨 승인 정책을 켠 인스턴스에서는 모든 승인 Moin이 수정으로 보였을 문제를 고쳤고(상태 전이는published_at과 승인 요청이 이미 기록),api/openapi.yaml에updatedAt의미를 명시했습니다. 검증은 로컬 PostgreSQL 컨테이너에moina_ciDB를 만들어MOINA_TEST_POSTGRES_DSN으로 integration test 포함go test -race ./...전체 통과, 새 integration test가 수정 전 코드에서 실패하는 것 확인,make fmt·make check·go vet·staticcheck·npm run lint(0 error)·npm test(31파일 208개)·npm run build모두 통과. - 보류 아이디어: Email 알림에 조용한 시간 미적용 — 문서상 Toast·Desktop 전용이 의도라 변경 보류(가치 2 / 위험 3 / M) · Makefile
test가 CI와 달리-race미사용(가치 2 / 위험 1 / S) ·pagination()이limit=abc같은 잘못된 query를 조용히 기본값으로 바꿔 400을 주지 않고,offset이 100만을 넘으면 0으로 되돌려nextCursor추종 시 1페이지로 되돌아감(가치 2 / 위험 2 / S) · 조용한 시간·Digest가 사용자별 시간대가 아닌 인스턴스 기본 시간대만 사용(가치 3 / 위험 3 / L) - 릴리즈: v0.1.18 (2026-09-03)
moyro
- 선택: 커스텀 이모지 검색 누락·
emoji/namesN+1·삭제된 이름 재사용 불가 수정 (가치 4 / 위험 2 / 작업량 M) - 결과: 성공
- 요약:
writeEmojiSearch가 최신 200개만 가져와 Go에서strings.Contains로 걸러서, 커스텀 이모지가 한 페이지를 넘는 워크스페이스에서는 오래된 이모지가 자동완성·검색 양쪽에서 영구히 사라졌다. 매칭을 DB로 옮기고LIKE대신strpos를 써서 사용자가 입력한%·_가 와일드카드로 새지 않게 했으며, 요청당 최대 200회 순차 쿼리를 돌던POST /emoji/names는= ANY($1::text[])단일 쿼리로 바꾸고 요청 순서를 유지했다. 또 v0.1 베이스라인이emojis.name에 테이블 전역 UNIQUE를 걸어둔 탓에 소프트 삭제된 이모지 이름을 다시 등록하면 라이브 전용 충돌 검사는 통과하고 업로드까지 끝난 뒤 INSERT만 실패해 이름이 영구히 잠기고 매 시도마다 고아 파일이 남았으므로, 마이그레이션 000017로 제약을delete_at=0부분 유니크 인덱스로 교체했다. 테스트가 없던emojis패키지에 통합 테스트 5건과store에 마이그레이션 업그레이드 테스트 1건을 추가하고 CI PostgreSQL 잡 대상에./internal/emojis를 넣었다. 로컬 postgres:16 컨테이너를 띄워go vet ./...,MOYRO_TEST_POSTGRES_DSN설정 후go test -race -p 1 ./...(전 패키지 통과),scripts/check-source-sizes.sh로 검증했다. 웹 변경이 없어 webapp 빌드는 손대지 않았다. - 보류 아이디어: (1)
emojis.Create가 크기 초과 파일을 업로드한 뒤에야 거절해 고아 file_infos 행을 남기는 문제 — files.Service에 정리 경로가 필요. (2) 테스트가 전혀 없는invites/sidebar/userstatus/postacks패키지의 통합 테스트 보강. (3) 로드맵의 create-post 인가·멤버십 2회 쿼리를 단일 쿼리로 병합. (4) 로드맵의 메시지 목록 가상화 — 작업량 L이라 단일 세션 범위 초과. - 릴리즈: v0.2.13 (2026-09-03)
muni
- 선택: 한글 표(.hwp/.hwpx)의 열 너비 읽기와 .hwpx 쓰기 (가치 4 / 위험 2 / M)
- 결과: 성공
- 요약:
.docx리더는 예전부터colwidth를 지켰는데 한글 리더 둘은 버려서, 좁은 항목 칸과 넓은 설명 칸이 있는 표가 한글 파일로 들어오면 전부 같은 폭이 되었습니다..hwp는 셀 LIST_HEADER 의 주소(열·행·병합) 다음에 오는 HWPUNIT 크기에서,.hwpx는<hp:cellSz width>에서 읽고, 두 형식 모두 병합된 칸은 덮는 열들의 합만 말하므로 열 너비는 한 열만 덮는 칸에서 배웁니다. 픽셀 환산(인치당 96,hangul.PixelWidth)과 칸이 지키는 열 목록(hangul.ColumnWidths)은 hangul 패키지에 두어 두 리더가 공유하고,.hwpx쓰기는 6인치 본문 폭을 균등 분할하는 대신 비율대로 나누도록 바꿨습니다(그 김에 열 개수를 세로 병합으로 내려온 칸까지 세도록 고침). 검증은 새 테스트 넷 —.hwp픽스처 둘(너비 있는 표, 너비 없는 표는 아무것도 기록하지 않음), 한글이 쓰는 모양의.hwpx픽스처 하나, 쓰기 쪽 비율 검사 하나 — 과gofmt -l·go vet ./...·go test ./...전체 통과. 커밋 f971045. - 보류 아이디어:
.hwpx글자 음영(형광펜) 양방향 (가치 3 / 위험 2 / S) — charPr 의 shadeColor,.hwp는 음영 색 오프셋 60.hwp표 칸 음영·세로 정렬 읽기 (가치 3 / 위험 3 / M) — HWPX·DOCX 는 유지하는데.hwp만 버림 (셀 LIST_HEADER 의 borderFill ID → DocInfo 의 BORDER_FILL)- CI 에
go vet과gofmt -l추가 (가치 2 / 위험 1 / S) — 이번에도 gofmt 를 손으로 돌려야 했음 .hwp/.hwpx표 행 높이 읽기 (가치 2 / 위험 2 / S) — 너비와 같은 자리에 있는데 muni 편집기가 행 높이를 들지 않음
- 릴리즈: 없음(태그 푸시는 외부 절차)
- 릴리즈: v0.24.0 (2026-09-03)
pii-masker
- 선택: 업로드 요청 본문 크기 상한 적용 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
POST /v1/mask,POST /v1/jobs가r.Body에 아무 상한 없이ParseMultipartForm(MaxFileSizeBytes + 1MB)를 호출해, 클라이언트가 임의 크기(수 GB) 본문을 흘려보내면 서버가 51MB를 메모리에 버퍼링하고 나머지는 temp 파일로 흘린 뒤에야validateAttachment에서 거절하던 메모리·디스크 DoS 경로를 막았습니다.http.MaxBytesReader로 본문을MaxFileSizeBytes + 64KB(멀티파트 프레이밍 여유분)에서 자르고, 잘린 요청은 새payloadTooLargeError→413 payload_too_large로 응답하며,maxMemory를 8MB 고정으로 낮춰 큰 파트가 RAM에 이중으로 남지 않고 temp 파일로 넘어가게 했습니다. 상한 초과 시 413을 받는 통합 테스트 2개(/v1/mask,/v1/jobs+ job 미생성 확인)와 한도에 딱 맞는 업로드가 여전히 200으로 처리되는 회귀 테스트 1개를 추가했고,gofmt -l(무출력)·go vet ./...·go test -count=1 ./...·go test -race -count=1 ./...전부 통과,-count=5로 새 테스트 플래키 여부 확인, 수정 전 코드로 되돌려 새 테스트 2개가 실제로 실패(본문 512KB를 끝까지 읽고 400 반환)하는 것까지 확인했습니다. - 보류 아이디어:
/v1/history의limit상한 없음(과도한 값 요청 시 전체 목록 직렬화) / job 파일 보존·정리 정책 부재로 저장소 무한 증가 /CreateJob의go runJob동시 실행 개수 제한 없음 /internal/jobs,internal/config패키지 단위 테스트 전무 / 마스킹 결과가 원본과 동일할 때(예: 숫자 없는 주민등록번호 필드) 스팬이 비어 필드 전체를 덮는 과잉 마스킹 - 릴리즈: v1.0.6 (2026-09-03)
ptium
- 선택: 워드 본문이 콘텐츠 컨트롤(w:sdt) 안에 든 절을 통째로 잃는 문제 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
internal/docs/prose.go가w:body바로 아래의w:p/w:tbl만 훑어서, 서식 파일의 표지·자동 목차·양식처럼 콘텐츠 컨트롤로 감싼 덩어리가 문단이든 표든 한 줄도 읽히지 않았다. 스크래치 테스트로 “본문 전체가 w:sdt 하나인 문서 → 슬라이드 0장, 경고도 없음”을 먼저 재현한 뒤, 본문 블록을 훑을 때w:sdt/w:sdtContent/w:customXml을 innerxml 재파싱으로 여는blocksIn(12겹 제한)을 추가해 원래 자리에 그대로 이어 붙였다.w:sdtPr안의 이름·선택 항목은 문단이 아니므로 전과 같이 걸리지 않는다.wordsections_test.go(5가지 + 순서 1가지)를 추가하고make test(go test -race, go vet, tsc, vite build) 전부 통과. VERSION 1.69.21 스탬프(openapi·kubernetes·offline-deployment 포함)와 릴리스 노트 작성, 커밋 12ef96e. - 보류 아이디어:
- 표 안(
w:tc)의 문단이w:sdt로 감싸이면 그 칸이 비는 문제 — 이번 변경의 표 내부판 (가치 3 / 위험 2 / S) - CSV 에 이스케이프 안 된 따옴표가 하나만 있어도 파일 전체를 거부하는 문제 —
csv.Reader.LazyQuotes(가치 3 / 위험 2 / S) allNumeric이 통화 기호·괄호 음수(“₩1,200”, “(1,200)”)를 숫자로 보지 않아 2열 금액 시트가 차트 대신 표가 되는 문제 (가치 3 / 위험 2 / S)- xlsx 의
t="b"(불리언)이 TRUE/FALSE 가 아니라 1/0 으로 읽히는 문제 (가치 2 / 위험 1 / S)
- 표 안(
- 릴리즈: v1.69.21 (태그·푸시는 외부 스크립트)
- 릴리즈: v1.69.21 (2026-09-03)
releasedock
- 선택: 배포 후 단계 설정을 읽지 못한 실행이 SUCCESS 로 남는 문제 수정 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
executeSimpleRun은 긴 배포 중의 설정 변경을 반영하려고 명령이 끝난 뒤loadSimpleSettings를 호출하는데, 이 읽기가 실패하면 복제·앱 배포를 통째로 건너뛴 채 실행이 SUCCESS 로 기록됐습니다. 두 단계가 켜져 있었는지 여부 자체가 그 설정에 들어 있으므로, 미러링되지 않은 이미지와 교체되지 않은 앱이 초록색으로 보이는 것은 단계 순서가 막으려던 바로 그 상태입니다. 순수 함수outcomeWithoutStageSettings를 추가해 명령이 성공한 경우에만 FAILED 로 뒤집고(이미 실패한 실행은 자기 사유 유지) 로그에[post-deploy]줄을 남기도록 했습니다. 단위 테스트 1건을 추가했고 backend/runnergo vet·go test ./...,npm test -- --run(72건),npm run build를 모두 통과했습니다. docs/simple-mode.md 에 절을 추가하고 VERSION 을 0.5.3 으로 올렸습니다(저장소 관례). - 보류 아이디어:
- CI 와 Makefile 에
go vet(또는 golangci-lint) 단계가 없어 정적 검사가 수동입니다 (가치 3 / 위험 1 / S). simpleRunLogger는 로그 한도 도달 후system()출력까지 버려서exit=... status=...마지막 줄과 복제/앱 배포·설정 오류 안내가 사라집니다 (가치 3 / 위험 2 / S).readUploadBatch의 batchId/batchLast 파싱에 단위 테스트가 없습니다 (가치 2 / 위험 1 / S).web/dist/assets/vendor청크가 617KB 로 커서 폐쇄망 초기 로딩 최적화 여지가 있습니다 (가치 2 / 위험 3 / M).- 실행 상세 화면에서
건너뜀/없음단계 상태 표기가 사용자에게 구분되는지 UI 문구 점검 (가치 2 / 위험 1 / S).
- CI 와 Makefile 에
- 릴리즈: v0.5.3 (2026-09-03)
- 릴리즈: v0.5.3 (2026-09-03)
relio
- 선택: intelligence 단건 조회를 상위 200건 선형 탐색이 아닌 SQL id 조건으로 (가치 5 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
GetSignal/GetRisk/GetInsight/GetRecommendation이List*(Limit: 200)을 부른 뒤 Go 에서 ID 를 훑었습니다. 목록은 심각도·점수 순 정렬이라 레코드가 200건을 넘는 계정에서는 그 뒤의 항목이 조회뿐 아니라IgnoreSignal·AcceptRisk·AcceptRecommendation·DismissRecommendation(모두 쓰기 전에 단건 조회를 함) 까지 “not found” 로 실패했습니다 — 분석이 가장 많이 쌓이는 바쁜 계정에서 정확히 그 조언들을 처리할 수 없었습니다. 네 필터에ID를 추가하고 각 질의에($n='' OR x.id::text=$n)선택 조건을 넣어 목록과 단건이 같은 문장(같은 Data Scope 조인)을 쓰게 했고, 단건은Limit: 1로 받습니다. 테스트를 위해 각 목록 질의를(sql, args)를 돌려주는 순수 빌더로 분리했습니다. 검증은 새 테스트 3개(단건 질의의 id 플레이스홀더와 인자 대응, 빈/공백 id 는 무필터, 모든$n이 인자와 1:1 인지 검사하는 회귀 가드)와 id 조건을 지우면 실제로 실패하는지 확인, 그리고go test -race ./...,go vet ./...,gofmt -l, web typecheck·build,check-env-contract.sh,check-static-assets.sh전체 통과. 커밋 cee08a9. - 보류 아이디어:
internal/server/today.go:87의time.Now().Truncate(24*time.Hour)는 UTC 자정이라 KST 배포에서 하루 지난 연체 건이 HIGH 대신 WARNING 으로 분류됨 (가치 4 / 위험 2 / S)internal/approval/service.go:214의CONTAINS는 양쪽이 숫자처럼 보이면 숫자 분기 default 로 빠져==로 평가됨 → 승인 정책이 조용히 무시되고 결재가 우회됨 (가치 4 / 위험 2 / M)internal/intelligence/engine.go:333계약 만료D-N라벨이 정수 절삭 탓에 하루 짧고, D-90 창이 실제로는 D-91 까지 걸림 (가치 3 / 위험 2 / S)- 네 intelligence 필터의
Cursor필드는 어떤 질의도 쓰지 않는 죽은 코드 — 실제 커서 페이징을 붙이거나 제거 (가치 3 / 위험 1 / M) - MCP 요청 본문이 1MB 를 넘으면 조용히 잘려 “Parse error” 가 되므로 413 으로 구분 (가치 3 / 위험 1 / S)
- 릴리즈: v1.11.10 (2026-09-03)
umm
- 선택: 연결되어 있는데 연결을 기다리는 오프라인 변경 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: 오프라인 큐를 다시 비우는 유일한 자동 신호가 브라우저
online이벤트인데, 연결과 무관하게 flush가 멈추는 두 경우 — 409idempotency-in-progress(다른 요청이 같은 변경을 쓰는 중)와navigator.onLine이 참인 채로 던진 요청 — 에는 그 이벤트가 오지 않아, 변경이1개 변경이 연결을 기다리는 중배너를 띄운 채 사람이지금 동기화를 누를 때까지 영원히 앉아 있었습니다(기존 시험 이름은 “retries an in-progress reservation”이었지만 재시도를 아무도 하지 않았음). 각각 2초·5초 타이머를 걸었고, 그 타이머가 넓히는 2차 결함 — 버전 충돌은 재전송해도 같은 409이고 매번umm:offline-conflict가 다시 발생해 CanvasPage가setMergeDraft로 사람이 타이핑하던 병합칸을 덮어씀 — 을 막기 위해 충돌을 이미 보여 준 변경은 재전송하지 않고 결정(discardOfflineMutation)까지 붙잡아 두게 했습니다. 검증: 새 vitest 3개 추가 후api.ts를 옛 동작으로 되돌리면 3개 전부 실패함을 확인,go vet ./...·go test ./...· tsc · oxlint/Prettier · i18n 948키 · vitest 140개 ·scripts/check-version.sh통과. v0.66.1로 릴리스 커밋(a1280ef). - 보류 아이디어:
OfflineStatus배너가 온라인인데도 “연결을 기다리는 중”이라고 말함 — 충돌 대기/재시도 대기와 진짜 오프라인을 구분해 문장을 나누기 (가치 3 / 위험 1 / S)README.md상단 릴리스 소개가 v0.44.0, 배지가 v0.22.0,docs/README.md문서 허브가 v0.8.1 — 셋 다 실제(v0.66.1)와 어긋남 (가치 2 / 위험 1 / S)WriteSource는 제목이 빈 슬라이드를 건너뛰는데SlideSources는 그 자리를 세어 출처 매핑이 밀림(현재는selectThoughts덕에 도달 불가이나 두 곳의 규칙이 다름) (가치 3 / 위험 2 / S)usableSections가 서로 다른 부에 같은 제목이 오는 제안을 막지 않음 — 같은 이름의 부가 두 번 열림 (가치 2 / 위험 1 / S)PresentationModal이 분량 안에서 부 제목이 차지한 칸 수를 말하지 않음 (가치 2 / 위험 2 / M)
- 릴리즈: v0.66.1 (2026-09-03)
- 릴리즈: v0.66.1 (2026-09-03)
vibe-coders
- 선택:
PROXY_API_KEYS필드 트림 및 빈 시크릿 검증 (가치 4 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
config.parseProxyKeys가 CSV 항목 전체만 TrimSpace하고:분리 후 개별 필드는 트림하지 않아,PROXY_API_KEYS="dev: dev-proxy-key: alice"처럼 자연스럽게 띄어쓴 설정이" dev-proxy-key"의 해시로 저장되던 버그를 고쳤다. 인증 경로의bearerToken은 토큰을 트림해서 넘기므로 이 키는 어떤 요청으로도 절대 매칭되지 않는 무성 인증 실패였다. 아울러 시크릿이 빈 항목(dev:,dev::alice:team)이sha256("")을 active 키 해시로 등록해 게이트웨이를 키 필수 모드로 뒤집던 것도 건너뛰도록 했다. 테스트가 전무하던internal/config에 첫 테스트 파일을 추가해 문서화된name:key:owner:team형식과 두 회귀를 덮었고, 수정 전 코드에서 실패함을 확인했다. gofmt·go vet·go build·go test ./...·go test -race ./internal/config모두 통과. - 보류 아이디어: redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 /
EstimateTokens의[]rune(text)전체 복사를utf8.RuneCountInString으로 교체 / 가격표 키 정규화(소문자·trim)를 적재 시점에 일원화해 조회마다 재정규화 제거 /databaseConfig의 DSN 우선순위·durationEnv/floatMapEnv등 나머지 config 헬퍼 테스트 보강 /parseProxyKeys에서 중복 키가 같은 ID로 서로를 덮어쓰는 문제 경고 처리 - 릴리즈: v0.82.0 (2026-09-03)
visitflow
- 선택: 감사 로그 CSV 내보내기가 화면의 행위자·기간 필터를 무시하는 문제 수정 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: 관리자 → 감사 로그 화면은 이벤트 접두어·행위자·시작·종료로 필터링하지만
exportAuditLogsCSV는action만 읽었고 프런트의exportQuery도action만 실어 보내, 특정 담당자나 기간으로 좁혀 내려받아도 최근 10,000행 전체가 담기는 잘못된 근거 자료가 만들어졌다.internal/app/admin.go에auditLogFilters와parseAuditLogFilters를 두어 목록과 내보내기가 같은 파라미터를 같은 규칙으로 파싱하게 하고, 내보내기 쿼리에 행위자·기간 조건과 목록과 동일한a.id DESC정렬을 적용했으며,audit.export감사 기록에 실제 적용된 범위를 남기고AdminPage.tsx의 다운로드 링크가buildQuery()를 재사용하도록 했다. 검증은go vet ./...,npm ci && npm run build, docker postgres:16-alpine을 띄운VISITFLOW_TEST_DSN전체 테스트 통과이며, 신규 통합 테스트 1개(행위자·from·to 필터 각각)와 단위 테스트 2개를 추가한 뒤 수정을 임시로 되돌려 통합 테스트가 실제로 실패하는 것까지 확인했다. API_AND_MCP 문서에 한 줄 안내를 덧붙였다. - 보류 아이디어:
bestAcceptLanguage의q=0·q>1·잘못된q값 처리 정정 / CSV·XLSX 가져오기 파서(visitorInputsFromRows) 엣지케이스 단위 테스트 보강 //metrics토큰 상수시간 비교와 요청 한도 적용 검토 / 설정 내보내기 JSON을 되돌려 넣는 가져오기 경로와 스키마 검증 / 방문 이력 CSV의 50,000행 상한 초과 시 사용자에게 잘렸음을 알리는 표시
weekly
- 선택: 주 격자를 옮긴 다음 주에 지난주 자동 복제가 조용히 건너뛰던 버그 수정 (가치 3 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
runAutomaticCloneForUser는 이번 주 충돌은 이미 날짜 겹침으로 묻고 있었는데 원본만week-7정확일치로 찾아, 주 시작 요일을 바꾼 다음 주에는 작성자가 이어 쓰던 전환 주 보고서(옛 격자 날짜)를 못 찾고 그 주를 processed 로 표시한 뒤 초안을 만들지 않았습니다. 원본 조회를weekCoveringDays겹침 +week_start DESC LIMIT 1로 바꿔 두 반쪽의 규칙을 통일했고, 회귀 테스트 1개(currentweekgrid_test.go, guards: runAutomaticCloneForUser/weekCoveringDays)를 더했습니다. 전환 주 자체에는 겹치는 보고서가 있으므로 여전히 복제하지 않는다는 것도 같은 테스트가 확인합니다. 관리자 안내서의 “정확히 한 주 전 보고서” 문장과 자동화 표를 제품에 맞게 고치고 HTML·PDF 를 다시 만들었습니다. 검증: 수정 전 코드에서 새 테스트가 실패하는 것을 확인 → 실제 DB(WEEKLY_TEST_POSTGRES_DSN)로go test ./...전체 통과,go vet, guard-check 279개 도달, version·openapi·modal-close 검사 통과, frontend lint·build·test(121개) 통과. mutation-check 는 새 가드에서 잔존 변이 3건(287·304·330행)인데 모두 이번에 바꾸지 않은 “쓸 것이 없는” 분기(설정 꺼짐/이미 처리/커밋 생략)이고, 바꾼 조회 경로의 변이 3건은 모두 잡혔습니다. backup-check 는 로컬에 psql 이 없어 건너뛰었습니다(CI 에서 실행). - 보류 아이디어:
buildMailMessage의 From 표시이름이 순수 ASCII면 인코딩되지 않아,·<가 든 이름이 From 헤더를 깨뜨립니다 (가치 2 / 위험 1 / 작업량 S)- 전환 주에는
previousWeekPlan이week_start < target로 물어 작성자가 지금 쓰고 있는 그 보고서를 “지난주 계획” 으로 되돌려 줍니다 (가치 3 / 위험 2 / 작업량 S) meeting.go의 지난주 조회(weekBefore)도 같은 전환 주 정확일치 문제를 갖는지 확인 (가치 2 / 위험 2 / 작업량 S)outlookForDueDate의 AT_RISK 문구가 low==high 일 때 최근 속도를 빼고 전체 평균만 말합니다 (가치 1 / 위험 1 / 작업량 S)
- 릴리즈: v0.284.0 (2026-09-03)
aiportal-front
- 선택: 공통 유틸 Vitest 단위 테스트 도입 및 파일 확장자 판별 오류 수정 (가치 5 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약: 자동화 테스트가 전무했던 저장소에 Vitest + jsdom 환경(
vitest.config.js,npm test/npm run test:watch)을 추가하고common,page,chatFile,favToggle유틸에 대한 단위 테스트 61건을 작성했다. 테스트 작성 중 발견한 실제 버그 2건(isPreviewablXlsx가['xlsx','xlsx']로 중복 비교해.xls를 인식하지 못하던 문제, 파일 판별 함수들이file이 null 일 때file.file_name접근으로 예외를 던지던 문제)을 고치고 회귀 테스트로 고정했다. 검증은npm test(61건 통과)와npm run build:dev(빌드 성공)로 수행했다. README 와docs/09-테스트가이드-총론.md에 실행 방법을 문서화했다. - 보류 아이디어:
- markdown.js 의 DOMPurify 설정/onclick 파싱 경로 XSS 하드닝 검토 (가치 4 / 위험 3 / M)
- ESLint + Prettier 도입 (탭·스페이스 혼재로 초기 diff 노이즈가 커서 보류) (가치 3 / 위험 2 / M)
parseUtcToKstDate가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (운영 표시값 영향 커서 보류) (가치 3 / 위험 4 / S)useFileAccept의 mode 값 불일치(modeKey는 ‘ocr’, 제외 확장자 분기는 ‘OCR’) 수정 (가치 3 / 위험 3 / S)docs/07-개선사항-권장사항.md의 스토어 패턴 통일(useServiceStore → Pinia defineStore) (가치 3 / 위험 4 / L)
jupiq
- 선택: OpenAPI 문서와 등록 경로 계약 검증 테스트 추가 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약:
register*함수 시그니처를*http.ServeMux에서 최소router인터페이스로 바꿔 테스트가 등록 경로를 수집할 수 있게 하고,internal/api/openapi_contract_test.go에서 openapi.yaml의 paths와 양방향(경로→문서, 문서→경로)으로 대조하도록 했다. probe·별칭·항상 405인 승인 쓰기 경로는 이유를 적은 예외 목록으로 관리하며, 이 검증으로 드러난 누락GET /auth/oidc/callback을 문서에 추가했다. 임시로 가짜 경로를 등록해 테스트가 실제로 드리프트를 잡는지 확인했고,go vet ./...,go test -race ./...,scripts/check-version.sh,scripts/check-screenshots.mjs,npm run lint,npm test(16파일 49개) 모두 통과했다. - 보류 아이디어:
internal/api/helpers.go의 사용되지 않는parseTimeQuery제거(boundedTimeRange로 대체됨) /internal/collector커버리지 8.5% 보강 /serveSPA의 해시 자산에 장기 Cache-Control 부여 / 로그인 리미터succeeded가 ip 키를 정리하지 않는 동작에 대한 테스트·문서화 - 릴리즈: v1.3.0 (2026-09-03)
← 대시보드 · Atom 피드 · 원본 데이터 runs.jsonl