From 4e8898ea1faa004e6efd9610a90c6fcf8ea7ffd1 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Sat, 23 May 2026 21:43:47 +0900 Subject: [PATCH] docs(readmes): replace hardcoded versions in root tables with Maven Central badges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual version sync across 14 demo rows × 2 languages = 28 spots that have to be hand-bumped on every starter release. The recent 5 stale-ref slips (#55) proved this surface drifts in practice — and was the second time it bit (after the post-SB4 sweep PR). Replaced the "Maven Central coordinates" column in all three tables (easy-paging SB4, easy-paging SB3 maintenance, ssrf-guard) with shields.io badges that read directly from Maven Central: [![Maven Central](https://img.shields.io/maven-central/v//?label=%3A)](https://central.sonatype.com/artifact//) For the SB3 maintenance demos, added &versionPrefix=0.4 so the badge pins to the 0.4.x line (the SB4 0.5.x release on Maven Central would otherwise overwrite the displayed version on the SB3 row). Same trick on the SB4 row with &versionPrefix=0.5 keeps the lines independent if the 0.4.x maintenance branch ever publishes a 0.4.1 patch. Trade-off captured for posterity: - Pro: zero manual sync; release publishes → README updates automatically. - Con: badge reflects "latest on Maven Central," not "what this demo pins in build.gradle.kts." There's a short mismatch window between a release going out and Dependabot landing the per-demo pin bump. Acceptable because (a) the window is short — dependabot fires within minutes; (b) per-demo READMEs still contain the actual pinned version in prose ("the only dep added is :X:Y:Z"), so the literal pin is still grep-able; (c) the table column is titled "Maven Central" not "What this demo uses," matching the badge semantics. Per-demo README prose was intentionally left as text — copy-paste Gradle snippets need real version strings, and the recent fix (#55) already aligned them with the build files. A CI mismatch check (Layer 1) is a follow-up if the prose drifts again. Excluded from this sweep: - Per-demo README "Files of interest" tables — kept as prose. - The `.github` profile open-source list — separate PR on the devslab-kr/.github repo, same badge pattern. --- README.ko.md | 34 +++++++++++++++++----------------- README.md | 34 +++++++++++++++++----------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/README.ko.md b/README.ko.md index b501395..8de7280 100644 --- a/README.ko.md +++ b/README.ko.md @@ -14,34 +14,34 @@ 최신 active 라인. Spring Boot 4 이상 사용 중이면 여기. -| 데모 | 보여주는 것 | Maven Central 좌표 | +| 데모 | 보여주는 것 | Maven Central | | --- | --- | --- | -| [`easy-paging-sb4-demo`](easy-paging-sb4-demo/) | `@AutoPaginate` 어노테이션 기반 offset 페이지네이션 (Spring Boot 4 + MyBatis + H2) | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-keyset-demo`](easy-paging-sb4-keyset-demo/) | `@KeysetPaginate` 커서(keyset) 페이지네이션 — composite `(time, id)` 키, 쓰기 중에도 안정, `OFFSET`/`COUNT(*)` 없음 | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-postgres-demo`](easy-paging-sb4-postgres-demo/) | 동일 스타터를 **실제 PostgreSQL**과 — `bootRun`은 Docker Compose, 테스트는 Testcontainers + `@ServiceConnection`, 로컬 DB 설치 불필요 | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-reactive-demo`](easy-paging-sb4-reactive-demo/) | Reactive 스택 — **WebFlux + R2DBC**, `R2dbcOffsetPagingSupport` 사용. MVC 데모와 동일한 JSON 봉투를 `Mono>`로 서빙 | [`kr.devslab:easy-paging-spring-boot-starter-reactive:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | +| [`easy-paging-sb4-demo`](easy-paging-sb4-demo/) | `@AutoPaginate` 어노테이션 기반 offset 페이지네이션 (Spring Boot 4 + MyBatis + H2) | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-keyset-demo`](easy-paging-sb4-keyset-demo/) | `@KeysetPaginate` 커서(keyset) 페이지네이션 — composite `(time, id)` 키, 쓰기 중에도 안정, `OFFSET`/`COUNT(*)` 없음 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-postgres-demo`](easy-paging-sb4-postgres-demo/) | 동일 스타터를 **실제 PostgreSQL**과 — `bootRun`은 Docker Compose, 테스트는 Testcontainers + `@ServiceConnection`, 로컬 DB 설치 불필요 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-reactive-demo`](easy-paging-sb4-reactive-demo/) | Reactive 스택 — **WebFlux + R2DBC**, `R2dbcOffsetPagingSupport` 사용. MVC 데모와 동일한 JSON 봉투를 `Mono>`로 서빙 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter-reactive?label=kr.devslab%3Aeasy-paging-spring-boot-starter-reactive&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | ### easy-paging — Spring Boot 3 maintenance (`0.4.x` 라인) Spring Boot 3.3–3.5 사용 중인 앱용. 스타터의 [`0.4.x` 브랜치](https://github.com/devslab-kr/easy-paging-spring-boot-starter/tree/0.4.x)가 SB3 보안 패치를 계속 받고, 이 데모들은 그 라인에 pin 됨. -| 데모 | 보여주는 것 | Maven Central 좌표 | +| 데모 | 보여주는 것 | Maven Central | | --- | --- | --- | -| [`easy-paging-demo`](easy-paging-demo/) | `@AutoPaginate` 어노테이션 기반 offset 페이지네이션 (Spring Boot 3 + MyBatis + H2) | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-keyset-demo`](easy-paging-keyset-demo/) | `@KeysetPaginate` 커서 페이지네이션 | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-postgres-demo`](easy-paging-postgres-demo/) | 실제 PostgreSQL 사용 | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-reactive-demo`](easy-paging-reactive-demo/) | Reactive 스택 — WebFlux + R2DBC | [`kr.devslab:easy-paging-spring-boot-starter-reactive:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | +| [`easy-paging-demo`](easy-paging-demo/) | `@AutoPaginate` 어노테이션 기반 offset 페이지네이션 (Spring Boot 3 + MyBatis + H2) | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-keyset-demo`](easy-paging-keyset-demo/) | `@KeysetPaginate` 커서 페이지네이션 | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-postgres-demo`](easy-paging-postgres-demo/) | 실제 PostgreSQL 사용 | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-reactive-demo`](easy-paging-reactive-demo/) | Reactive 스택 — WebFlux + R2DBC | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter-reactive?label=kr.devslab%3Aeasy-paging-spring-boot-starter-reactive&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | ### ssrf-guard -| 데모 | 보여주는 것 | Maven Central 좌표 | +| 데모 | 보여주는 것 | Maven Central | | --- | --- | --- | -| [`ssrf-guard-demo`](ssrf-guard-demo/) | SSRF(Server-Side Request Forgery) 방어를 3종 Spring HTTP 클라이언트(RestClient, RestTemplate, WebClient)에 동시 적용 — 모두 같은 `UrlPolicy`. 15가지 공격 매트릭스 엔드포인트, Micrometer 메트릭 포함 | [`kr.devslab:ssrf-guard:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) | -| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM 에이전트 SSRF 방어 (Spring AI).** 모든 Spring AI `ToolCallback`을 자동으로 wrap해서 LLM이 `fetch_url`을 호출하기 전에 URL 인자를 검증. 가짜 LLM 드라이버로 API 키 없이 오프라인 실행 가능 | [`kr.devslab:ssrf-guard-springai:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) | -| [`ssrf-guard-langchain4j-demo`](ssrf-guard-langchain4j-demo/) | ⭐ **LLM 에이전트 SSRF 방어 (LangChain4j).** 자바의 또 다른 메이저 LLM 프레임워크용 — 모든 `ToolExecutor` 빈을 wrap, executor 실행 전에 `ToolExecutionRequest.arguments()` JSON을 검증 | [`kr.devslab:ssrf-guard-langchain4j:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-langchain4j) | -| [`ssrf-guard-feign-demo`](ssrf-guard-feign-demo/) | Spring Cloud OpenFeign `RequestInterceptor` — `@FeignClient` 호출에 동일 `UrlPolicy` 적용. 화이트리스트 / 비화이트리스트 `@FeignClient` 2개로 차단 경로 시연 | [`kr.devslab:ssrf-guard-feign:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-feign) | -| [`ssrf-guard-jdkhttp-demo`](ssrf-guard-jdkhttp-demo/) | `java.net.http.HttpClient`(Java 11+) 래퍼 — 라이브러리 자체엔 Spring 의존성 없음. `main()`에서 3줄 wiring | [`kr.devslab:ssrf-guard-jdkhttp:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-jdkhttp) | -| [`ssrf-guard-okhttp-demo`](ssrf-guard-okhttp-demo/) | OkHttp `Interceptor` + `Dns` — Spring 필요 없음. `OkHttpClient.Builder`에 3줄 wiring | [`kr.devslab:ssrf-guard-okhttp:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-okhttp) | +| [`ssrf-guard-demo`](ssrf-guard-demo/) | SSRF(Server-Side Request Forgery) 방어를 3종 Spring HTTP 클라이언트(RestClient, RestTemplate, WebClient)에 동시 적용 — 모두 같은 `UrlPolicy`. 15가지 공격 매트릭스 엔드포인트, Micrometer 메트릭 포함 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard?label=kr.devslab%3Assrf-guard)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) | +| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM 에이전트 SSRF 방어 (Spring AI).** 모든 Spring AI `ToolCallback`을 자동으로 wrap해서 LLM이 `fetch_url`을 호출하기 전에 URL 인자를 검증. 가짜 LLM 드라이버로 API 키 없이 오프라인 실행 가능 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-springai?label=kr.devslab%3Assrf-guard-springai)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) | +| [`ssrf-guard-langchain4j-demo`](ssrf-guard-langchain4j-demo/) | ⭐ **LLM 에이전트 SSRF 방어 (LangChain4j).** 자바의 또 다른 메이저 LLM 프레임워크용 — 모든 `ToolExecutor` 빈을 wrap, executor 실행 전에 `ToolExecutionRequest.arguments()` JSON을 검증 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-langchain4j?label=kr.devslab%3Assrf-guard-langchain4j)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-langchain4j) | +| [`ssrf-guard-feign-demo`](ssrf-guard-feign-demo/) | Spring Cloud OpenFeign `RequestInterceptor` — `@FeignClient` 호출에 동일 `UrlPolicy` 적용. 화이트리스트 / 비화이트리스트 `@FeignClient` 2개로 차단 경로 시연 | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-feign?label=kr.devslab%3Assrf-guard-feign)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-feign) | +| [`ssrf-guard-jdkhttp-demo`](ssrf-guard-jdkhttp-demo/) | `java.net.http.HttpClient`(Java 11+) 래퍼 — 라이브러리 자체엔 Spring 의존성 없음. `main()`에서 3줄 wiring | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-jdkhttp?label=kr.devslab%3Assrf-guard-jdkhttp)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-jdkhttp) | +| [`ssrf-guard-okhttp-demo`](ssrf-guard-okhttp-demo/) | OkHttp `Interceptor` + `Dns` — Spring 필요 없음. `OkHttpClient.Builder`에 3줄 wiring | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-okhttp?label=kr.devslab%3Assrf-guard-okhttp)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-okhttp) | ## 컨벤션 diff --git a/README.md b/README.md index 092953f..a643eb5 100644 --- a/README.md +++ b/README.md @@ -14,34 +14,34 @@ Each subdirectory is an **independent** Spring Boot application with its own Gra Latest active line. Use these if your app is on Spring Boot 4+. -| Demo | Showcases | Maven Central coordinates | +| Demo | Showcases | Maven Central | | --- | --- | --- | -| [`easy-paging-sb4-demo`](easy-paging-sb4-demo/) | Annotation-driven offset pagination with `@AutoPaginate` (Spring Boot 4 + MyBatis + H2) | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-keyset-demo`](easy-paging-sb4-keyset-demo/) | Cursor (keyset) pagination with `@KeysetPaginate` — composite `(time, id)` key, stable under writes, no `OFFSET`/`COUNT(*)` | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-postgres-demo`](easy-paging-sb4-postgres-demo/) | Same starter against **real PostgreSQL** — Docker Compose for `bootRun`, Testcontainers + `@ServiceConnection` for tests, no local DB install | [`kr.devslab:easy-paging-spring-boot-starter:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-sb4-reactive-demo`](easy-paging-sb4-reactive-demo/) | Reactive stack — **WebFlux + R2DBC** via `R2dbcOffsetPagingSupport`. Same JSON envelope as the MVC demos, served as `Mono>` | [`kr.devslab:easy-paging-spring-boot-starter-reactive:0.5.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | +| [`easy-paging-sb4-demo`](easy-paging-sb4-demo/) | Annotation-driven offset pagination with `@AutoPaginate` (Spring Boot 4 + MyBatis + H2) | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-keyset-demo`](easy-paging-sb4-keyset-demo/) | Cursor (keyset) pagination with `@KeysetPaginate` — composite `(time, id)` key, stable under writes, no `OFFSET`/`COUNT(*)` | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-postgres-demo`](easy-paging-sb4-postgres-demo/) | Same starter against **real PostgreSQL** — Docker Compose for `bootRun`, Testcontainers + `@ServiceConnection` for tests, no local DB install | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-sb4-reactive-demo`](easy-paging-sb4-reactive-demo/) | Reactive stack — **WebFlux + R2DBC** via `R2dbcOffsetPagingSupport`. Same JSON envelope as the MVC demos, served as `Mono>` | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter-reactive?label=kr.devslab%3Aeasy-paging-spring-boot-starter-reactive&versionPrefix=0.5)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | ### easy-paging — Spring Boot 3 maintenance (`0.4.x` line) For apps still on Spring Boot 3.3–3.5. The starter's [`0.4.x` branch](https://github.com/devslab-kr/easy-paging-spring-boot-starter/tree/0.4.x) continues to receive SB3 security patches; these demos pin against that line. -| Demo | Showcases | Maven Central coordinates | +| Demo | Showcases | Maven Central | | --- | --- | --- | -| [`easy-paging-demo`](easy-paging-demo/) | Annotation-driven offset pagination with `@AutoPaginate` (Spring Boot 3 + MyBatis + H2) | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-keyset-demo`](easy-paging-keyset-demo/) | Cursor (keyset) pagination with `@KeysetPaginate` | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-postgres-demo`](easy-paging-postgres-demo/) | Same starter against real PostgreSQL | [`kr.devslab:easy-paging-spring-boot-starter:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | -| [`easy-paging-reactive-demo`](easy-paging-reactive-demo/) | Reactive stack — WebFlux + R2DBC | [`kr.devslab:easy-paging-spring-boot-starter-reactive:0.4.0`](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | +| [`easy-paging-demo`](easy-paging-demo/) | Annotation-driven offset pagination with `@AutoPaginate` (Spring Boot 3 + MyBatis + H2) | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-keyset-demo`](easy-paging-keyset-demo/) | Cursor (keyset) pagination with `@KeysetPaginate` | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-postgres-demo`](easy-paging-postgres-demo/) | Same starter against real PostgreSQL | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter?label=kr.devslab%3Aeasy-paging-spring-boot-starter&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter) | +| [`easy-paging-reactive-demo`](easy-paging-reactive-demo/) | Reactive stack — WebFlux + R2DBC | [![Maven Central 0.4.x](https://img.shields.io/maven-central/v/kr.devslab/easy-paging-spring-boot-starter-reactive?label=kr.devslab%3Aeasy-paging-spring-boot-starter-reactive&versionPrefix=0.4)](https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter-reactive) | ### ssrf-guard -| Demo | Showcases | Maven Central coordinates | +| Demo | Showcases | Maven Central | | --- | --- | --- | -| [`ssrf-guard-demo`](ssrf-guard-demo/) | SSRF (Server-Side Request Forgery) protection across three Spring HTTP clients (RestClient, RestTemplate, WebClient) — same `UrlPolicy` for all. 15-pattern attack matrix endpoint, Micrometer metrics. | [`kr.devslab:ssrf-guard:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) | -| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM agent SSRF defense (Spring AI).** Wraps every Spring AI `ToolCallback` so URL-shaped tool arguments are validated before the LLM-driven `fetch_url` runs. Fake-LLM driver makes the demo runnable offline (no API key). | [`kr.devslab:ssrf-guard-springai:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) | -| [`ssrf-guard-langchain4j-demo`](ssrf-guard-langchain4j-demo/) | ⭐ **LLM agent SSRF defense (LangChain4j).** Same story for the other major Java LLM framework — wraps every `ToolExecutor` bean and validates `ToolExecutionRequest.arguments()` JSON before the executor runs. | [`kr.devslab:ssrf-guard-langchain4j:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-langchain4j) | -| [`ssrf-guard-feign-demo`](ssrf-guard-feign-demo/) | Spring Cloud OpenFeign `RequestInterceptor` — same `UrlPolicy` applied to `@FeignClient` calls. Two `@FeignClient` interfaces (one whitelisted, one not) to show the block path. | [`kr.devslab:ssrf-guard-feign:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-feign) | -| [`ssrf-guard-jdkhttp-demo`](ssrf-guard-jdkhttp-demo/) | `java.net.http.HttpClient` (Java 11+) wrapper — no Spring required by the library. Three-line wiring in `main()`. | [`kr.devslab:ssrf-guard-jdkhttp:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-jdkhttp) | -| [`ssrf-guard-okhttp-demo`](ssrf-guard-okhttp-demo/) | OkHttp `Interceptor` + `Dns` integration — also no Spring needed. Three-line wiring on `OkHttpClient.Builder`. | [`kr.devslab:ssrf-guard-okhttp:3.1.0`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-okhttp) | +| [`ssrf-guard-demo`](ssrf-guard-demo/) | SSRF (Server-Side Request Forgery) protection across three Spring HTTP clients (RestClient, RestTemplate, WebClient) — same `UrlPolicy` for all. 15-pattern attack matrix endpoint, Micrometer metrics. | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard?label=kr.devslab%3Assrf-guard)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) | +| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM agent SSRF defense (Spring AI).** Wraps every Spring AI `ToolCallback` so URL-shaped tool arguments are validated before the LLM-driven `fetch_url` runs. Fake-LLM driver makes the demo runnable offline (no API key). | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-springai?label=kr.devslab%3Assrf-guard-springai)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) | +| [`ssrf-guard-langchain4j-demo`](ssrf-guard-langchain4j-demo/) | ⭐ **LLM agent SSRF defense (LangChain4j).** Same story for the other major Java LLM framework — wraps every `ToolExecutor` bean and validates `ToolExecutionRequest.arguments()` JSON before the executor runs. | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-langchain4j?label=kr.devslab%3Assrf-guard-langchain4j)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-langchain4j) | +| [`ssrf-guard-feign-demo`](ssrf-guard-feign-demo/) | Spring Cloud OpenFeign `RequestInterceptor` — same `UrlPolicy` applied to `@FeignClient` calls. Two `@FeignClient` interfaces (one whitelisted, one not) to show the block path. | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-feign?label=kr.devslab%3Assrf-guard-feign)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-feign) | +| [`ssrf-guard-jdkhttp-demo`](ssrf-guard-jdkhttp-demo/) | `java.net.http.HttpClient` (Java 11+) wrapper — no Spring required by the library. Three-line wiring in `main()`. | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-jdkhttp?label=kr.devslab%3Assrf-guard-jdkhttp)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-jdkhttp) | +| [`ssrf-guard-okhttp-demo`](ssrf-guard-okhttp-demo/) | OkHttp `Interceptor` + `Dns` integration — also no Spring needed. Three-line wiring on `OkHttpClient.Builder`. | [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/ssrf-guard-okhttp?label=kr.devslab%3Assrf-guard-okhttp)](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-okhttp) | ## Conventions