diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2adebc8..84b7966 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,15 +25,15 @@ jobs: with: persist-credentials: false - # The engine dependency: guard-core-jvm pinned at 9067b7b (full SHA for the + # The engine dependency: guard-core-jvm pinned at 09dfddf (full SHA for the # actions/checkout fetch), checked # out as the sibling directory ./guard-core-jvm and substituted for the # declared com.guardcore:guard-core coordinates by settings.gradle.kts. - - name: Checkout engine (guard-core-jvm @ 9067b7b) + - name: Checkout engine (guard-core-jvm @ 09dfddf) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: Guard-Core/guard-core-jvm - ref: 9067b7b247d73790cc93b4cb433a1e973265a473 + ref: 09dfddf77d1b25e7d79a03f8a37fe781ed30f236 path: guard-core-jvm persist-credentials: false diff --git a/.github/workflows/code-ql.yml b/.github/workflows/code-ql.yml index 5876c5e..8acef54 100644 --- a/.github/workflows/code-ql.yml +++ b/.github/workflows/code-ql.yml @@ -23,11 +23,11 @@ jobs: # The engine dependency: the composite includeBuild needs the pinned # guard-core-jvm checkout next to the repository (same as ci.yml). - - name: Checkout engine (guard-core-jvm @ 9067b7b) + - name: Checkout engine (guard-core-jvm @ 09dfddf) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: Guard-Core/guard-core-jvm - ref: 9067b7b247d73790cc93b4cb433a1e973265a473 + ref: 09dfddf77d1b25e7d79a03f8a37fe781ed30f236 path: guard-core-jvm persist-credentials: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65df299..b15faf8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,11 +72,11 @@ jobs: persist-credentials: false # Same engine checkout as ci.yml (the composite default for tests). - - name: Checkout engine (guard-core-jvm @ 9067b7b) + - name: Checkout engine (guard-core-jvm @ 09dfddf) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: Guard-Core/guard-core-jvm - ref: 9067b7b247d73790cc93b4cb433a1e973265a473 + ref: 09dfddf77d1b25e7d79a03f8a37fe781ed30f236 path: guard-core-jvm persist-credentials: false diff --git a/CHANGELOG.md b/CHANGELOG.md index e07fc6e..05f9386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Added + +- `guard.excluded-semantic-detection-headers` and `guard.detection-max-scan-seconds` bind to the engine's `excludedSemanticDetectionHeaders` and `detectionMaxScanSeconds`. The engine pin moves to guard-core-jvm 09dfddf, which carries both. + ### Changed - Telemetry agent support: an `AgentHandlerProtocol` bean (for example guard-agent-jvm's `GuardAgentTelemetry`) is now injected into `GuardMiddleware` by the auto-configuration, adopted into the engine composite on initialization, and stopped through the new `GuardMiddleware.shutdown()` when the context closes. Before this, `guard.enable-agent=true` always degraded with a "W7 artifact" message, because the middleware had no way to receive a handler. The degrade path now only applies when no handler bean is registered. The pinned guard-core-jvm engine ref moves to 9067b7b, whose agent telemetry wiring fix (Guard-Core/guard-core-jvm#48) the handler needs to receive anything. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4716f79..a01a8ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ side in the same directory) at the pinned ref: ```bash git clone https://github.com/Guard-Core/guard-core-jvm ../guard-core-jvm -git -C ../guard-core-jvm checkout 9067b7b +git -C ../guard-core-jvm checkout 09dfddf ./gradlew build # detekt + ktfmt check + tests (redis-backed tests skip when redis is down) ``` diff --git a/README.md b/README.md index bf16f3b..466d533 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ logs the same "eager init not honored" warning as fastapi-guard. The engine artifact `com.guard-core:guard-core` (groupId is the Central Portal namespace; Kotlin packages stay `com.guardcore.*`) is on Maven Central for consumers. This repository's own build pins the engine from source instead: CI and local builds check out `Guard-Core/guard-core-jvm` at the -pinned ref `9067b7b` as the sibling directory `./guard-core-jvm`, and `settings.gradle.kts` +pinned ref `09dfddf` as the sibling directory `./guard-core-jvm`, and `settings.gradle.kts` substitutes the declared coordinates `com.guard-core:guard-core:4.3.3` / `com.guard-core:guard-core-engine:4.3.3` with the included build's projects (the Gradle analog of the Rust path+version pattern). The declared dependency coordinates stay @@ -152,7 +152,7 @@ Local checkout: ```bash git clone https://github.com/Guard-Core/guard-core-jvm.git guard-core-jvm -git -C guard-core-jvm checkout 9067b7b +git -C guard-core-jvm checkout 09dfddf ./gradlew build ``` diff --git a/docs/examples.md b/docs/examples.md index 38529d9..8a6bee1 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -7,7 +7,7 @@ routes, live Redis-backed rate limiting, and per-route decorator configs. git clone https://github.com/Guard-Core/spring-boot-guard.git cd spring-boot-guard git clone https://github.com/Guard-Core/guard-core-jvm.git guard-core-jvm -git -C guard-core-jvm checkout 9067b7b +git -C guard-core-jvm checkout 09dfddf docker run -d -p 6379:6379 redis:7-alpine ./gradlew :examples:demo-boot:bootRun ``` diff --git a/docs/installation.md b/docs/installation.md index 2e34e0b..f71ac75 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -23,7 +23,7 @@ packages stay `com.guardcore.*`. ## Engine dependency (includeBuild + dependency substitution) Consumers resolve the engine artifact `com.guard-core:guard-core` from Maven Central. This repository's own build pins the engine from source instead: CI and -local builds check out `Guard-Core/guard-core-jvm` at the pinned ref `9067b7b` +local builds check out `Guard-Core/guard-core-jvm` at the pinned ref `09dfddf` as the sibling directory `./guard-core-jvm`, and `settings.gradle.kts` substitutes the declared coordinates `com.guard-core:guard-core:4.3.3` / `com.guard-core:guard-core-engine:4.3.3` with the included build's projects. @@ -32,7 +32,7 @@ The declared dependency coordinates stay `com.guard-core:guard-core:4.3.3` ```bash git clone https://github.com/Guard-Core/guard-core-jvm.git guard-core-jvm -git -C guard-core-jvm checkout 9067b7b +git -C guard-core-jvm checkout 09dfddf ./gradlew build ``` diff --git a/examples/demo-boot/README.md b/examples/demo-boot/README.md index e629036..5cd0586 100644 --- a/examples/demo-boot/README.md +++ b/examples/demo-boot/README.md @@ -21,12 +21,12 @@ engine's `SecurityConfig` field names). Prerequisites: -1. Clone Guard-Core/guard-core-jvm at the pinned ref `9067b7b` next to this repository +1. Clone Guard-Core/guard-core-jvm at the pinned ref `09dfddf` next to this repository (the engine builds from source via includeBuild until the artifacts publish): ```bash git clone https://github.com/Guard-Core/guard-core-jvm.git guard-core-jvm - git -C guard-core-jvm checkout 9067b7b + git -C guard-core-jvm checkout 09dfddf ``` 2. A Redis on `localhost:6379` (rate limiting), or set `guard.enable-redis: false` in diff --git a/examples/demo-boot/settings.gradle.kts b/examples/demo-boot/settings.gradle.kts index 26cb92d..5076dde 100644 --- a/examples/demo-boot/settings.gradle.kts +++ b/examples/demo-boot/settings.gradle.kts @@ -15,7 +15,7 @@ rootProject.name = "demo-boot" // The adapter and the engine are consumed from source until both publish: // the adapter repo sits two levels up and the engine at the pinned ref -// 9067b7b sits next to it (the same includeBuild convention as the adapter). +// 09dfddf sits next to it (the same includeBuild convention as the adapter). includeBuild("../../") { dependencySubstitution { substitute(module("com.guardcore:spring-boot-guard")).using(project(":")) @@ -35,7 +35,7 @@ val engineDir: File = run { ) roots.firstOrNull { it.resolve("settings.gradle.kts").isFile } ?: throw GradleException( - "Clone Guard-Core/guard-core-jvm at the pinned ref 9067b7b next to the " + + "Clone Guard-Core/guard-core-jvm at the pinned ref 09dfddf next to the " + "spring-boot-guard repository first (or point -PguardCoreJvmDir / " + "GUARD_CORE_JVM_DIR at an existing checkout). Searched: " + roots.joinToString(", ") { it.path }, diff --git a/settings.gradle.kts b/settings.gradle.kts index e25135a..3732ab6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { // GUARD_CORE_USE_COMPOSITE (environment variable): // unset / "1" / "true" -> composite ON (default). The engine is built from // source via includeBuild + dependency substitution: CI (and local builds) -// check out Guard-Core/guard-core-jvm at the PINNED REF 9067b7b as the +// check out Guard-Core/guard-core-jvm at the PINNED REF 09dfddf as the // sibling directory ./guard-core-jvm, and the block below substitutes the // declared coordinates com.guard-core:guard-core / // com.guard-core:guard-core-engine with the included build's projects. @@ -63,7 +63,7 @@ if (useComposite) { roots.firstOrNull { it.resolve("settings.gradle.kts").isFile } ?: throw GradleException( "The guard-core-jvm engine checkout was not found. Clone Guard-Core/guard-core-jvm " + - "at the pinned ref 9067b7b next to this repository (or point -PguardCoreJvmDir / " + + "at the pinned ref 09dfddf next to this repository (or point -PguardCoreJvmDir / " + "GUARD_CORE_JVM_DIR at an existing checkout). Searched: " + roots.joinToString(", ") { it.path }, ) diff --git a/src/main/kotlin/com/guardcore/spring/GuardProperties.kt b/src/main/kotlin/com/guardcore/spring/GuardProperties.kt index f6d6f07..ae38d88 100644 --- a/src/main/kotlin/com/guardcore/spring/GuardProperties.kt +++ b/src/main/kotlin/com/guardcore/spring/GuardProperties.kt @@ -293,6 +293,9 @@ public class GuardProperties { /** python_name: detection_max_scan_values. */ public var detectionMaxScanValues: Int? = null + /** python_name: detection_max_scan_seconds. */ + public var detectionMaxScanSeconds: Double? = null + /** python_name: detection_max_scan_chars. */ public var detectionMaxScanChars: Int? = null @@ -371,6 +374,9 @@ public class GuardProperties { /** python_name: excluded_detection_headers. */ public var excludedDetectionHeaders: Set? = null + /** python_name: excluded_semantic_detection_headers. */ + public var excludedSemanticDetectionHeaders: Set? = null + /** python_name: excluded_detection_params. */ public var excludedDetectionParams: Set? = null diff --git a/src/main/kotlin/com/guardcore/spring/GuardPropertiesMapper.kt b/src/main/kotlin/com/guardcore/spring/GuardPropertiesMapper.kt index fa16213..ac62f41 100644 --- a/src/main/kotlin/com/guardcore/spring/GuardPropertiesMapper.kt +++ b/src/main/kotlin/com/guardcore/spring/GuardPropertiesMapper.kt @@ -127,6 +127,7 @@ public fun GuardProperties.applyTo(builder: SecurityConfigBuilder) { detectionMaxBodyInspectBytes?.let { v -> builder.detectionMaxBodyInspectBytes = v } detectionMaxScanValues?.let { v -> builder.detectionMaxScanValues = v } detectionMaxScanChars?.let { v -> builder.detectionMaxScanChars = v } + detectionMaxScanSeconds?.let { v -> builder.detectionMaxScanSeconds = v } detectionMaxJsonDepth?.let { v -> builder.detectionMaxJsonDepth = v } detectionBinaryMinRunLength?.let { v -> builder.detectionBinaryMinRunLength = v } detectionPreserveAttackPatterns?.let { v -> builder.detectionPreserveAttackPatterns = v } @@ -152,6 +153,7 @@ public fun GuardProperties.applyTo(builder: SecurityConfigBuilder) { logfireServiceName?.let { v -> builder.logfireServiceName = v } enableEnrichment?.let { v -> builder.enableEnrichment = v } excludedDetectionHeaders?.let { v -> builder.excludedDetectionHeaders = v } + excludedSemanticDetectionHeaders?.let { v -> builder.excludedSemanticDetectionHeaders = v } excludedDetectionParams?.let { v -> builder.excludedDetectionParams = v } excludedDetectionBodyFields?.let { v -> builder.excludedDetectionBodyFields = v } detectionScanBody?.let { v -> builder.detectionScanBody = v } diff --git a/src/test/kotlin/com/guardcore/spring/GuardPropertiesBindingTest.kt b/src/test/kotlin/com/guardcore/spring/GuardPropertiesBindingTest.kt index 8460d6a..7eed336 100644 --- a/src/test/kotlin/com/guardcore/spring/GuardPropertiesBindingTest.kt +++ b/src/test/kotlin/com/guardcore/spring/GuardPropertiesBindingTest.kt @@ -93,6 +93,7 @@ public class GuardPropertiesBindingTest { "guard.detection-slow-pattern-threshold=0.2", "guard.detection-anomaly-emission-cooldown=30.0", "guard.detection-threat-score-threshold=0.9", + "guard.detection-max-scan-seconds=2.5", "guard.redis-socket-connect-timeout=1.25", "guard.redis-socket-timeout=1.75", "guard.agent-high-watermark-ratio=0.8", @@ -163,6 +164,7 @@ public class GuardPropertiesBindingTest { "guard.log-sensitive-params[0]=password", "guard.log-sensitive-body-fields[0]=ssn", "guard.excluded-detection-headers[0]=x-internal", + "guard.excluded-semantic-detection-headers[0]=x-semantic", "guard.excluded-detection-params[0]=token", "guard.excluded-detection-body-fields[0]=secret", "guard.enabled-detection-categories[0]=sqli", @@ -206,6 +208,7 @@ public class GuardPropertiesBindingTest { assertThat(c.logSensitiveParams).containsExactly("password") assertThat(c.logSensitiveBodyFields).containsExactly("ssn") assertThat(c.excludedDetectionHeaders).containsExactly("x-internal") + assertThat(c.excludedSemanticDetectionHeaders).containsExactly("x-semantic") assertThat(c.excludedDetectionParams).containsExactly("token") assertThat(c.excludedDetectionBodyFields).containsExactly("secret") assertThat(c.enabledDetectionCategories).containsExactly("sqli") @@ -254,6 +257,7 @@ public class GuardPropertiesBindingTest { assertThat(c.detectionSlowPatternThreshold).isEqualTo(0.2) assertThat(c.detectionAnomalyEmissionCooldown).isEqualTo(30.0) assertThat(c.detectionThreatScoreThreshold).isEqualTo(0.9) + assertThat(c.detectionMaxScanSeconds).isEqualTo(2.5) assertThat(c.redisSocketConnectTimeout).isEqualTo(1.25) assertThat(c.redisSocketTimeout).isEqualTo(1.75) assertThat(c.agentHighWatermarkRatio).isEqualTo(0.8)