Skip to content

harden(security): 배포 전 보안·정확성 하드닝 (6커밋)#40

Merged
ArcSolver merged 6 commits into
mainfrom
fix/predeploy-hardening
Jun 4, 2026
Merged

harden(security): 배포 전 보안·정확성 하드닝 (6커밋)#40
ArcSolver merged 6 commits into
mainfrom
fix/predeploy-hardening

Conversation

@ArcSolver

@ArcSolver ArcSolver commented Jun 4, 2026

Copy link
Copy Markdown
Owner

무엇 / 왜

배포 전 보안·정확성 점검(7렌즈 워크플로우, 적대적 검증) 결과를 반영한 하드닝 6커밋. 기능 PR(#35·#36·#41·#38·#39) 머지 후 main 위로 리베이스해 하드닝만 담는다.

🔴 블로커 (해결)

  • feeds SSRFassert_public_url로 내부망/메타데이터/loopback 차단 + get_text(guard_ssrf, max_bytes).
  • .claude PyPI sdist 누출.gitignore·hatch sdist exclude·release 빌드후 가드.

🟠 곧 고칠 것 (해결)

mask_error_details + NetworkError URL 마스킹 · data.go.kr 6종 HTTPS · OAuth state 필수화(CSRF) · release 태그↔버전 게이트.

🟡 하드닝 (해결)

응답 크기 컷오프 · defusedxml(코어 arcsolve/xml.py, XXE/billion-laughs) · str(e.payload) 노출 제거(15종) · UpstreamError 절단 · retry 캡 · 손상 cred 복구 · 토큰 응답 검증 · REFRESH_TOKEN 마스킹 · seoul KST 타임존 · kakao/discord 빈입력 차단.

⏭️ 의도적 보류

토큰스토어 크로스프로세스 락(in-proc 원자적·cross-proc은 filelock 필요) · publish environment(PyPI publisher와 동시 변경 필요) · 의존성 상한 캡(fastmcp 3.x가 최신 httpx/pydantic 요구 → 캡 시 깨진 2.x 강등).

검증

891 passed(+34 보안 테스트) · ruff clean · sdist 민감파일 0건.

🤖 Generated with Claude Code

ArcSolver and others added 6 commits June 5, 2026 02:24
배포 전 보안 점검의 블로커#1 + 코어 DoS/정보노출 하드닝.

- http: assert_public_url(SSRF 가드) — getaddrinfo로 해석한 IP가 private/loopback/
  link-local/reserved/multicast/unspecified면 거부(메타데이터 169.254.169.254·localhost·내부망 차단).
- feeds_fetch: get_text(guard_ssrf=True, max_bytes=16MB) — 사용자 임의 URL의 SSRF 차단 +
  스트리밍 크기 컷오프(메모리 DoS 방지). 가드는 get_text 내부로 두어 단위테스트 무네트워크 유지.
- http: NetworkError가 _safe_url로 scheme://host만 노출(telegram /bot<token>/·serviceKey 누출 방지).
- http: Retry.max_delay(기본 30s) + _retry_delay 상한 — 거대 Retry-After 자기-DoS 차단.
- http: UpstreamError가 text payload를 2KB로 절단(dict는 보존).
- server: FastMCP(mask_error_details=True) — 미처리 예외 원문이 클라이언트로 누출되지 않게.

882 passed(+15: SSRF 차단/통과·토큰 마스킹·payload 절단·retry 캡·max_bytes 컷오프·feeds e2e) · ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
배포 전 점검의 OAuth 하드닝.

- oauth: state를 생성했으면(authorize_url_for_login 호출) exchange_code에 state 필수 —
  state를 떼고 code만 넘기는 token-fixation/CSRF 우회 차단(이전엔 둘 다 있을 때만 비교).
- oauth: 손상된 credentials.json을 .bak으로 비키고 빈 상태로 폴백(JSONDecodeError가 모든
  토큰 읽기를 막던 문제 제거).
- oauth: _post_token이 access_token 없는 응답을 KeyError 대신 명확한 RuntimeError로 거부.
- cli(auth): REFRESH_TOKEN을 기본 마스킹(앞4…뒤4), 전체 값은 --show-token opt-in 뒤로 —
  터미널 스크롤백·CI 로그 평문 잔존 방지.
- 참고: 인-프로세스 lost-update는 update()에 await가 없어 원자적이라 락 불필요(크로스프로세스는
  filelock 의존성 필요 → 범위 외).

885 passed(+3: state 필수/생략·토큰응답 검증·손상파일 복구) · ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
배포 전 점검의 블로커#2(.claude가 PyPI sdist로 누출) + 공급망 하드닝.

- .gitignore: `.claude/`·`*.bak`를 레포 .gitignore에 명시(전역 ignore 의존 제거 — hatchling
  sdist 선택이 레포 .gitignore를 따르므로 누출 차단).
- pyproject: [tool.hatch.build.targets.sdist] exclude로 .claude·.github·dist·docs/local·
  .env*·credentials.json·*.bak를 sdist에서 제외(이중 방어). 빌드 후 sdist에 민감파일 0건 검증.
- release.yml: (1) 태그 push 시 v<tag>와 __version__ 일치 게이트, (2) `rm -rf dist build` 후
  빌드, (3) sdist에 .claude/.env/.git/credential 포함 시 publish 실패 가드.
- stale dist/ 아티팩트(구 패키지명 arcsolve_mcp) 로컬 제거.
- 의존성 상한 캡은 두지 않음: fastmcp 3.x가 최신 httpx/pydantic를 요구해 캡이 fastmcp를 깨진
  2.x로 강등시킴(검증). 호환성은 uv.lock 고정으로 관리.

885 passed · ruff clean · sdist 민감파일 0건.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
배포 전 점검의 서비스 레벨 하드닝.

- transport: data.go.kr 6종(airkorea·egen·ev_charger·airport·parking·tago_transit)의
  BASE_URL을 http→https로 전환(apis.data.go.kr TLS 응답 확인). API 키가 쿼리에 실리므로
  평문 MITM 탈취 위험 제거. seoul_transit 2종은 상류가 HTTPS 미응답이라 http 유지 + README에
  키 노출 주의 명시.
- disclosure: 15개 서비스 _explain의 `detail or ' ' + str(e.payload)` 폴백 제거 — 상류
  비-JSON(HTML/XML) 원문이 도구 응답으로 새던 경로 차단(추출된 detail만 사용, status는 유지).
- validation: kakao text·discord content에 min_length=1 — 빈 본문이 상류 400을 만나기 전에 차단.
- correctness: seoul_subway_arrivals 경과시간을 KST-aware로 계산(naive 호스트 로컬타임 비교 →
  ZoneInfo("Asia/Seoul")). UTC+10~+14 호스트의 오표시 제거.

887 passed(+2: kakao/discord 빈문자열 거부) · ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
배포 전 점검의 파싱 하드닝(심층방어). 최신 expat은 XXE/billion-laughs를 기본 차단하나,
requires-python>=3.11이라 구형 expat 환경의 회귀를 대비.

- 코어 arcsolve/xml.py: safe_fromstring() — defusedxml로 파싱하고 악의적 구조(외부 엔티티·
  엔티티 확장)는 ET.ParseError로 정규화(기존 except ParseError 경로가 그대로 처리).
- 서비스 5종(arxiv·egen·feeds·ev_charger·pubmed)의 ET.fromstring(8개 호출)을 코어
  safe_fromstring으로 교체(서비스 폴더에 defusedxml 직접 의존 없이 코어 경유 — 규칙2).
- defusedxml>=0.7.1 의존성 추가(순수 파이썬, fastmcp 3.3.1 해석 유지 확인).

891 passed(+4: 정상 파싱·billion-laughs/XXE 거부·malformed) · ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ArcSolver ArcSolver force-pushed the fix/predeploy-hardening branch from 8ebacd0 to 5f92c1a Compare June 4, 2026 17:24
@ArcSolver ArcSolver changed the title harden(security): 배포 전 보안·정확성 하드닝 (deploy candidate) harden(security): 배포 전 보안·정확성 하드닝 (6커밋) Jun 4, 2026
@ArcSolver ArcSolver merged commit bdd6cb9 into main Jun 4, 2026
3 checks passed
@ArcSolver ArcSolver deleted the fix/predeploy-hardening branch June 4, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant