Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ Spring Boot 3.3–3.5 사용 중인 앱용. 스타터의 [`0.4.x` 브랜치](htt

| 데모 | 보여주는 것 | 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.0.1`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) |
| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM 에이전트 SSRF 방어.** 모든 Spring AI `ToolCallback`을 자동으로 wrap해서 LLM이 `fetch_url`을 호출하기 전에 URL 인자를 검증. 가짜 LLM 드라이버로 API 키 없이 오프라인 실행 가능 | [`kr.devslab:ssrf-guard-springai:3.0.1`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) |
| [`ssrf-guard-feign-demo`](ssrf-guard-feign-demo/) | Spring Cloud OpenFeign `RequestInterceptor` — `@FeignClient` 호출에 동일 `UrlPolicy` 적용. 화이트리스트 / 비화이트리스트 `@FeignClient` 2개로 차단 경로 시연 | [`kr.devslab:ssrf-guard-feign:3.0.1`](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.0.1`](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.0.1`](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 메트릭 포함 | [`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) |

## 컨벤션

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ For apps still on Spring Boot 3.3–3.5. The starter's [`0.4.x` branch](https://

| Demo | Showcases | Maven Central coordinates |
| --- | --- | --- |
| [`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.0.1`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard) |
| [`ssrf-guard-springai-demo`](ssrf-guard-springai-demo/) | ⭐ **LLM agent SSRF defense.** 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.0.1`](https://central.sonatype.com/artifact/kr.devslab/ssrf-guard-springai) |
| [`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.0.1`](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.0.1`](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.0.1`](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. | [`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) |

## Conventions

Expand Down
8 changes: 4 additions & 4 deletions ssrf-guard-demo/README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

하나의 Spring Boot 앱에 **3종 Spring HTTP 클라이언트**가 모두 동일 `UrlPolicy`를 통해 wiring됨:

- `RestClient` (Spring 6.1+) — 메타 아티팩트 `kr.devslab:ssrf-guard:3.0.1`
- `RestTemplate` — `kr.devslab:ssrf-guard-resttemplate:3.0.1`
- `WebClient` (WebFlux) — `kr.devslab:ssrf-guard-webclient:3.0.1`
- `RestClient` (Spring 6.1+) — 메타 아티팩트 `kr.devslab:ssrf-guard:3.1.0`
- `RestTemplate` — `kr.devslab:ssrf-guard-resttemplate:3.1.0`
- `WebClient` (WebFlux) — `kr.devslab:ssrf-guard-webclient:3.1.0`

추가로 `/attacks` 엔드포인트는 가드가 차단하는 모든 SSRF 우회 패턴 목록을 각 모듈별 curl 예제와 함께 제공합니다.

Expand Down Expand Up @@ -124,7 +124,7 @@ curl -s http://localhost:8080/actuator/prometheus | grep ssrf_guard

| 파일 | 왜 |
| --- | --- |
| `build.gradle.kts` | 표준 스타터 외 의존성은 `kr.devslab:ssrf-guard:3.0.1`, `:ssrf-guard-resttemplate:3.0.1`, `:ssrf-guard-webclient:3.0.1` 셋뿐 — 별도 configuration 클래스 불필요 |
| `build.gradle.kts` | 표준 스타터 외 의존성은 `kr.devslab:ssrf-guard:3.1.0`, `:ssrf-guard-resttemplate:3.0.1`, `:ssrf-guard-webclient:3.0.1` 셋뿐 — 별도 configuration 클래스 불필요 |
| `application.yml` | 모든 `ssrf.guard.*` 옵션이 한 곳에 주석과 함께 |
| `web/FetchController.java` | RestClient 전체 — 3줄 setup, 가드는 보이지 않게 실행 |
| `web/FetchResttemplateController.java` | RestTemplate 동일 — 레거시 코드 마이그레이션 불필요 |
Expand Down
8 changes: 4 additions & 4 deletions ssrf-guard-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Runnable example for [`ssrf-guard`](https://github.com/devslab-kr/ssrf-guard)

One Spring Boot app shows **all three Spring HTTP clients** wired through the same `UrlPolicy`:

- `RestClient` (Spring 6.1+) via the meta `kr.devslab:ssrf-guard:3.0.1` artifact
- `RestTemplate` via `kr.devslab:ssrf-guard-resttemplate:3.0.1`
- `WebClient` (WebFlux) via `kr.devslab:ssrf-guard-webclient:3.0.1`
- `RestClient` (Spring 6.1+) via the meta `kr.devslab:ssrf-guard:3.1.0` artifact
- `RestTemplate` via `kr.devslab:ssrf-guard-resttemplate:3.1.0`
- `WebClient` (WebFlux) via `kr.devslab:ssrf-guard-webclient:3.1.0`

Plus a `/attacks` endpoint that lists every SSRF bypass pattern the guard catches, with copy-paste curls for each.

Expand Down Expand Up @@ -125,7 +125,7 @@ You'll see counters per `reason` tag (`blocked_host`, `blocked_ip_literal`, `blo

| File | Why |
| --- | --- |
| `build.gradle.kts` | The only dependencies beyond the standard starters are `kr.devslab:ssrf-guard:3.0.1`, `kr.devslab:ssrf-guard-resttemplate:3.0.1`, `kr.devslab:ssrf-guard-webclient:3.0.1` — no manual configuration class needed |
| `build.gradle.kts` | The only dependencies beyond the standard starters are `kr.devslab:ssrf-guard:3.1.0`, `kr.devslab:ssrf-guard-resttemplate:3.1.0`, `kr.devslab:ssrf-guard-webclient:3.1.0` — no manual configuration class needed |
| `application.yml` | Every `ssrf.guard.*` knob in one place with comments |
| `web/FetchController.java` | The whole RestClient story — three lines of setup, the guard runs invisibly |
| `web/FetchResttemplateController.java` | Same shape for RestTemplate — no migration needed for legacy code |
Expand Down
6 changes: 3 additions & 3 deletions ssrf-guard-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ dependencies {
// The meta `ssrf-guard` artifact transitively pulls in `-core`, `-httpclient5`,
// and `-restclient`. The `-resttemplate` and `-webclient` modules are
// additive and reuse the same UrlPolicy / SsrfGuardMetrics beans.
implementation("kr.devslab:ssrf-guard:3.0.1")
implementation("kr.devslab:ssrf-guard-resttemplate:3.0.1")
implementation("kr.devslab:ssrf-guard-webclient:3.0.1")
implementation("kr.devslab:ssrf-guard:3.1.0")
implementation("kr.devslab:ssrf-guard-resttemplate:3.1.0")
implementation("kr.devslab:ssrf-guard-webclient:3.1.0")

// Micrometer Prometheus registry — turns SSRF Guard's counters into
// /actuator/prometheus output so you can curl the metrics in the demo.
Expand Down
4 changes: 2 additions & 2 deletions ssrf-guard-feign-demo/README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ curl http://localhost:8080/feign/evil | jq

| 파일 | 왜 |
| --- | --- |
| `build.gradle.kts` | `kr.devslab:ssrf-guard-feign:3.0.1` + `spring-cloud-starter-openfeign` |
| `build.gradle.kts` | `kr.devslab:ssrf-guard-feign:3.1.0` + `spring-cloud-starter-openfeign` |
| `HttpBinClient.java` / `EvilClient.java` | 평범한 `@FeignClient` 인터페이스 2개 — 가드 코드 없음 |
| `FeignDemoController.java` | `SsrfGuardException` catch (Feign이 한 단계 wrap — 컨트롤러가 unwrap) |
| `application.yml` | `ssrf.guard.exact-hosts: [httpbin.org]` — 그 한 줄이 화이트리스트 |

Feign 인터셉터는 자동 등록됨 — `ssrf-guard-feign-3.0.1`이 Spring 자동설정으로 `feign.RequestInterceptor` 빈을 publish하고, Spring Cloud OpenFeign이 모든 `@FeignClient`에 적용.
Feign 인터셉터는 자동 등록됨 — `ssrf-guard-feign-3.1.0`이 Spring 자동설정으로 `feign.RequestInterceptor` 빈을 publish하고, Spring Cloud OpenFeign이 모든 `@FeignClient`에 적용.

## 빌드 검증

Expand Down
4 changes: 2 additions & 2 deletions ssrf-guard-feign-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ curl http://localhost:8080/feign/evil | jq

| File | Why |
| --- | --- |
| `build.gradle.kts` | `kr.devslab:ssrf-guard-feign:3.0.1` + `spring-cloud-starter-openfeign` |
| `build.gradle.kts` | `kr.devslab:ssrf-guard-feign:3.1.0` + `spring-cloud-starter-openfeign` |
| `HttpBinClient.java` / `EvilClient.java` | Two normal `@FeignClient` interfaces — no guard code |
| `FeignDemoController.java` | Catches `SsrfGuardException` (wrapped one level deep by Feign — the controller unwraps) |
| `application.yml` | `ssrf.guard.exact-hosts: [httpbin.org]` — that one line is the whitelist |

The Feign interceptor registers itself automatically — `ssrf-guard-feign-3.0.1` provides a Spring autoconfig that publishes a `feign.RequestInterceptor` bean, which Spring Cloud OpenFeign then applies to every `@FeignClient`.
The Feign interceptor registers itself automatically — `ssrf-guard-feign-3.1.0` provides a Spring autoconfig that publishes a `feign.RequestInterceptor` bean, which Spring Cloud OpenFeign then applies to every `@FeignClient`.

## Verify the build

Expand Down
2 changes: 1 addition & 1 deletion ssrf-guard-feign-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-openfeign")

// The library this demo showcases. Pulls in ssrf-guard-core transitively.
implementation("kr.devslab:ssrf-guard-feign:3.0.1")
implementation("kr.devslab:ssrf-guard-feign:3.1.0")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand Down
2 changes: 1 addition & 1 deletion ssrf-guard-jdkhttp-demo/README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ curl 'http://localhost:8080/fetch?url=https://evil.com/' | jq

| 파일 | 왜 |
| --- | --- |
| `build.gradle.kts` | 의존성 하나: `kr.devslab:ssrf-guard-jdkhttp:3.0.1` |
| `build.gradle.kts` | 의존성 하나: `kr.devslab:ssrf-guard-jdkhttp:3.1.0` |
| `SsrfGuardJdkHttpDemoApplication.java` | 전체 스토리: `HostPolicy` → `UrlPolicy` → `HttpClient` wrap |
| `JdkHttpDemoController.java` | 평범한 `client.send(req, ...)` — 호출부에서 wrap은 보이지 않음 |

Expand Down
2 changes: 1 addition & 1 deletion ssrf-guard-jdkhttp-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ curl 'http://localhost:8080/fetch?url=https://evil.com/' | jq

| File | Why |
| --- | --- |
| `build.gradle.kts` | One dep: `kr.devslab:ssrf-guard-jdkhttp:3.0.1` |
| `build.gradle.kts` | One dep: `kr.devslab:ssrf-guard-jdkhttp:3.1.0` |
| `SsrfGuardJdkHttpDemoApplication.java` | The whole story: build `HostPolicy` → `UrlPolicy` → wrap `HttpClient` |
| `JdkHttpDemoController.java` | Calls `client.send(req, ...)` like any other HttpClient — the wrap is invisible at the call site |

Expand Down
2 changes: 1 addition & 1 deletion ssrf-guard-jdkhttp-demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies {
// dependency itself; the Spring Boot framing is just the demo's UX.
implementation("org.springframework.boot:spring-boot-starter-web")

implementation("kr.devslab:ssrf-guard-jdkhttp:3.0.1")
implementation("kr.devslab:ssrf-guard-jdkhttp:3.1.0")
// ssrf-guard-core's @ConfigurationProperties pulls in spring-boot
// (transitively from -jdkhttp's API), so we get the SsrfGuardProperties
// binding for free.
Expand Down
Loading