diff --git a/.github/workflows/security-release-gate.yml b/.github/workflows/security-release-gate.yml
index 051e4c9b7f..73c2dce1b9 100644
--- a/.github/workflows/security-release-gate.yml
+++ b/.github/workflows/security-release-gate.yml
@@ -59,6 +59,48 @@ jobs:
import xml.etree.ElementTree as ET
required = {
+ 'io.cattle.platform.iaas.api.auth.ai.catalog.AiCatalogLaunchLockOrderTest': {
+ 'nativeActionPrefetchAndConcurrentNativeGetCompleteWithApplicationBeforeRowLock',
+ 'onlyExactInternalActionOperandUsesSnapshotAndOrdinaryGetStillChecksSource',
+ 'snapshotRetainsOwnerProjectRoleKeyBindingAndDoesNotAuthorizeUnconfirmedApply',
+ },
+ 'io.cattle.platform.iaas.api.auth.ai.catalog.AiCatalogLaunchAccessTest': {
+ 'operandSnapshotRechecksOriginalKeyWithoutRetainingCredentialRowLock',
+ },
+ 'io.cattle.platform.iaas.api.auth.change.AiResourceChangeLockOrderTest': {
+ 'nativeActionReceiptPrefetchAndConcurrentGetDoNotInvertObservationWriteLocks',
+ 'exactSnapshotRetainsOwnerProjectKeyAndReceiptPermissionsWhileGetStillObservesAndApplyRequiresConfirmation',
+ },
+ 'io.cattle.platform.iaas.api.auth.upgrade.AiServiceUpgradePlanLockOrderTest': {
+ 'acceptedUpgradeNativeActionPrefetchAndConcurrentGetKeepApplicationBeforeRowLock',
+ 'runningUpgradeNativeActionPrefetchAndConcurrentGetKeepApplicationBeforeRowLock',
+ 'onlyExactNativeUpgradeActionMarkerUsesReadSnapshotAndOrdinaryGetStillObserves',
+ 'upgradeActionSnapshotRetainsOwnerProjectRoleVisibilityAndBoundKeyChecks',
+ 'upgradeActionReloadsSnapshotRoleAndKeyAuthorityAfterOperandLookupAndClaimsOnlyOnce',
+ 'concurrentNativeUpgradeAppliesShareOneDurableClaimAndDispatch',
+ },
+ 'io.cattle.platform.iaas.api.auth.ai.audit.AiAuditQueryServiceTest': {
+ 'realParserAndSchemaCannotEraseOriginalUnknownFieldsBeforeCanonicalError',
+ 'originalQueryParametersAreClosedButValidTypedQueriesKeepTheirCanonicalSuccess',
+ 'originalFieldCaptureIsOnlyTheAuditCollectionOwnerAndPrecedesTheRealSchemaValidator',
+ },
+ 'io.cattle.platform.iaas.api.auth.upgrade.AiServiceUpgradePlanServiceTest': {
+ 'realPreviewPipelineRejectsUnknownAndCoercedStrategyWithoutPlanOrDispatch',
+ 'originalPreviewGuardLeavesOrdinaryUpgradeAndPlanActionsUntouchedAndRunsBeforeNativeValidation',
+ 'omittedAndLegacyStrategiesKeepTheOriginalEffectiveDispatchDefaults',
+ 'newCustomAndDefaultPlansFenceLegacySnapshotBeforeClaim',
+ 'legacySnapshotWithoutStrategyOrFenceStillConfirmsAndAppliesDefaults',
+ 'strategyAndSnapshotFenceDriftCannotClaimOrDispatch',
+ 'reloadedFencedStrategyRemainsImmutableAndDispatchesOnlyOnce',
+ 'approvedStrategyIsDetachedReviewedAndDispatchedOnceWithoutActionOverrides',
+ 'strategyUsesNativeMinimaAndExactJsonIntegerBoundaryWithoutClamping',
+ 'invalidOptionalStrategyRejectsBeforePlanOrDispatchAndNativePreflightIsRequired',
+ },
+ 'io.cattle.platform.iaas.api.auth.upgrade.AiServiceUpgradeStrategyPreflightTest': {
+ 'nativeRetainIpStartFirstInvariantRejectsBeforeAnyPlanOrResourceMutation',
+ 'realStartFirstPortCapacityRejectsAtPreviewConfirmationAndBeforeClaim',
+ 'nativeVolumePreflightReceivesApprovedStartFirstAndNativeBatchNormalization',
+ },
'io.cattle.platform.iaas.api.auth.ai.catalog.AiCatalogLaunchPrivateWireTest': {
'nativeCryptoHttpRoundTripOpensExactlyOneGoBase64JsonLayer',
'nativeCryptoEnvelopeRejectsEveryPlanActorProjectAndOriginalKeyRebinding',
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 160bcc1134..f020b4c7d2 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -1,5 +1,51 @@
# Compatibility Contract
+## Engine 0.183.367 source candidate
+
+The existing image-upgrade plan accepts three optional strategy fields and
+returns a closed effective `upgradeStrategy`. Omitted fields and old persisted
+image-only plans keep `startFirst=false`, `batchSize=1`, `intervalMillis=2000`.
+Batch size retains the native minimum 1 and interval the native minimum 100ms.
+The JSON contract accepts exact integers through 9007199254740991; this is a
+client representation boundary, not a new native product maximum. The native
+preflight keeps its existing int-range batch normalization, while dispatch
+receives the approved Long values. Full/legacy keys, role/Key revision checks,
+port/volume rules and durable operation claims remain unchanged. No new mutation
+operation or global grant is introduced. Publication and runtime qualification
+remain pending; see the [candidate note](docs/releases/orchestration-engine-0.183.367.md).
+
+New plans bind the approved strategy into their private snapshot. An older
+Engine rejects these plans as stale before claiming an operation instead of
+applying its own defaults. Genuine legacy image-only plans remain compatible.
+After a downgrade, pending newer plans require a fresh preview and approval.
+
+Typed Audit queries now preserve a closed-field rejection through normal schema
+sanitization. The existing owner rejects unknown fields with422 and records its
+canonical ERROR outcome without starting the private query runner. Other API
+schema filtering and current actor/Key authorization remain unchanged.
+
+Catalog-launch, resource-change and upgrade action operands now use authorized,
+non-mutating read snapshots only for the framework's exact internal action marker.
+The native API write transaction previously retained a prefetch row lock after
+releasing its application lock, allowing a concurrent receipt GET to invert the
+next action's lock order. Ordinary GETs still refresh receipts; actions still
+reload under their application lock and repeat owner, role, Key, snapshot and
+durable-claim checks. No lock timeout, retry, database schema or grant changes.
+
+The lock-order tests declare managed Spring Context and AspectJ dependencies in
+test scope so they can load the shipped transaction advice and pointcut. Runtime
+dependency coordinates are unchanged.
+
+Frozen-package downloads retry transport failures only at the original signed
+snapshot URL, at most four attempts. Hash or size failures stop that origin
+immediately. Package selection, TLS verification and SHA256 checks are unchanged.
+
+Both Engine build recipes install Ubuntu's official PCRE2 security update
+`10.46-1ubuntu0.1` for the already installed `libpcre2-8-0` and `libpcre2-posix3`.
+The two archives are SHA256-pinned and their package, version, architecture and
+installed versions are checked. Other package locks and snapshot dates remain
+unchanged. See [USN-8974-1](https://ubuntu.com/security/notices/USN-8974-1).
+
## Engine 0.183.366 release
The [published release](https://github.com/PastureStack/orchestration-engine/releases/tag/v0.183.366)
diff --git a/Dockerfile b/Dockerfile
index 8e64834993..6c4b823b68 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,6 +31,8 @@ ADD --checksum=sha256:2c418a61d7165d0cde01d9cb05ae025cc41fc75ef6d3f095996bd045ba
ADD --checksum=sha256:8db913e1fa5a694ca6ba7b828bd18c5204e6853ac07fe7ed9c6e1f94e3444d8e https://security.ubuntu.com/ubuntu/pool/main/c/curl/libcurl3t64-gnutls_8.18.0-1ubuntu2.8_amd64.deb /tmp/libcurl3t64-gnutls.deb
ADD --checksum=sha256:7c0b37592f83a629cc22f0c98effa176da9edbd6d77b3f38184a9d0ddc5e0266 https://security.ubuntu.com/ubuntu/pool/main/c/curl/libcurl4t64_8.18.0-1ubuntu2.8_amd64.deb /tmp/libcurl4t64.deb
ADD --checksum=sha256:bedeef1cf3268f90338c411cdb0a45b224d0660fd2240f310f380316c8244606 https://security.ubuntu.com/ubuntu/pool/main/b/brotli/libbrotli1_1.2.0-3ubuntu0.1_amd64.deb /tmp/libbrotli1.deb
+ADD --checksum=sha256:44ebbeb6493eacffbc460ca71446959ba86a00b2e23515e4cfb1a5cc317d6672 https://security.ubuntu.com/ubuntu/pool/main/p/pcre2/libpcre2-8-0_10.46-1ubuntu0.1_amd64.deb /tmp/libpcre2-8-0.deb
+ADD --checksum=sha256:44605ea665673a359927d014f4a027c50fe6472af8bc956bd1b1c43ddc07a1cd https://security.ubuntu.com/ubuntu/pool/main/p/pcre2/libpcre2-posix3_10.46-1ubuntu0.1_amd64.deb /tmp/libpcre2-posix3.deb
ENV DEBIAN_FRONTEND=noninteractive
ARG TEMURIN_JDK25_URL="https://github.com/adoptium/temurin25-binaries/releases/download/jdk-25.0.5%2B8/OpenJDK25U-jdk_x64_linux_hotspot_25.0.5_8.tar.gz"
@@ -109,7 +111,17 @@ RUN set -eux; \
test "$(dpkg-deb -f /tmp/libbrotli1.deb Version)" = 1.2.0-3ubuntu0.1; \
test "$(dpkg-deb -f /tmp/libbrotli1.deb Architecture)" = amd64; \
dpkg -i /tmp/libbrotli1.deb; \
+ for package in libpcre2-8-0 libpcre2-posix3; do \
+ test "$(dpkg-query -W -f='${db:Status-Status}' "${package}")" = installed; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Package)" = "${package}"; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Version)" = 10.46-1ubuntu0.1; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Architecture)" = amd64; \
+ done; \
+ dpkg -i /tmp/libpcre2-8-0.deb /tmp/libpcre2-posix3.deb; \
sh /usr/local/bin/install-ubuntu-security-updates /licenses/ubuntu-security-packages.lock /etc/ssl/certs/ca-certificates.crt; \
+ for package in libpcre2-8-0 libpcre2-posix3; do \
+ test "$(dpkg-query -W -f='${Version}' "${package}")" = 10.46-1ubuntu0.1; \
+ done; \
for package in curl libcurl3t64-gnutls libcurl4t64; do \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_CURL_VERSION}"; \
done; \
diff --git a/Dockerfile.dapper b/Dockerfile.dapper
index 5b4265214e..601c8e3a32 100644
--- a/Dockerfile.dapper
+++ b/Dockerfile.dapper
@@ -32,6 +32,8 @@ ADD --checksum=sha256:2c418a61d7165d0cde01d9cb05ae025cc41fc75ef6d3f095996bd045ba
ADD --checksum=sha256:8db913e1fa5a694ca6ba7b828bd18c5204e6853ac07fe7ed9c6e1f94e3444d8e https://security.ubuntu.com/ubuntu/pool/main/c/curl/libcurl3t64-gnutls_8.18.0-1ubuntu2.8_amd64.deb /tmp/libcurl3t64-gnutls.deb
ADD --checksum=sha256:7c0b37592f83a629cc22f0c98effa176da9edbd6d77b3f38184a9d0ddc5e0266 https://security.ubuntu.com/ubuntu/pool/main/c/curl/libcurl4t64_8.18.0-1ubuntu2.8_amd64.deb /tmp/libcurl4t64.deb
ADD --checksum=sha256:bedeef1cf3268f90338c411cdb0a45b224d0660fd2240f310f380316c8244606 https://security.ubuntu.com/ubuntu/pool/main/b/brotli/libbrotli1_1.2.0-3ubuntu0.1_amd64.deb /tmp/libbrotli1.deb
+ADD --checksum=sha256:44ebbeb6493eacffbc460ca71446959ba86a00b2e23515e4cfb1a5cc317d6672 https://security.ubuntu.com/ubuntu/pool/main/p/pcre2/libpcre2-8-0_10.46-1ubuntu0.1_amd64.deb /tmp/libpcre2-8-0.deb
+ADD --checksum=sha256:44605ea665673a359927d014f4a027c50fe6472af8bc956bd1b1c43ddc07a1cd https://security.ubuntu.com/ubuntu/pool/main/p/pcre2/libpcre2-posix3_10.46-1ubuntu0.1_amd64.deb /tmp/libpcre2-posix3.deb
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
@@ -127,7 +129,17 @@ RUN set -eux; \
test "$(dpkg-deb -f /tmp/libbrotli1.deb Version)" = 1.2.0-3ubuntu0.1; \
test "$(dpkg-deb -f /tmp/libbrotli1.deb Architecture)" = amd64; \
dpkg -i /tmp/libbrotli1.deb; \
+ for package in libpcre2-8-0 libpcre2-posix3; do \
+ test "$(dpkg-query -W -f='${db:Status-Status}' "${package}")" = installed; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Package)" = "${package}"; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Version)" = 10.46-1ubuntu0.1; \
+ test "$(dpkg-deb -f "/tmp/${package}.deb" Architecture)" = amd64; \
+ done; \
+ dpkg -i /tmp/libpcre2-8-0.deb /tmp/libpcre2-posix3.deb; \
sh /usr/local/bin/install-ubuntu-security-updates /licenses/ubuntu-security-packages.lock /etc/ssl/certs/ca-certificates.crt; \
+ for package in libpcre2-8-0 libpcre2-posix3; do \
+ test "$(dpkg-query -W -f='${Version}' "${package}")" = 10.46-1ubuntu0.1; \
+ done; \
for package in curl libcurl3t64-gnutls libcurl4t64; do \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_CURL_VERSION}"; \
done; \
diff --git a/README.md b/README.md
index f0fa819c5b..39baa2e3f1 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,24 @@ Rancher 1.6 ecosystem. It is not affiliated with Rancher Labs or SUSE.
This fork of [`rancher/cattle`](https://github.com/rancher/cattle) preserves
upstream history, authorship, licenses, and dependency notices.
-## Current release: Engine 0.183.366
+## Development candidate: Engine 0.183.367
+
+Image-upgrade previews accept optional `startFirst`, `batchSize` and
+`intervalMillis`. The immutable plan exposes the three effective values in
+`upgradeStrategy`, with the existing defaults `false`, `1` and `2000`.
+Preview, human confirmation and apply repeat the existing native port/volume
+preflight using that approved strategy. Native role/Key checks, snapshot staleness
+and exactly-once dispatch remain in effect. See the
+[candidate contract](docs/releases/orchestration-engine-0.183.367.md).
+This source candidate has not been published or accepted in runtime QA.
+Catalog-launch, resource-change and upgrade action lookups use authorized read
+snapshots to preserve application/row lock order during concurrent receipt reads.
+
+Typed Audit queries retain their closed-input check before generic schema
+sanitization. Unknown caller fields are rejected with a canonical error outcome
+instead of being discarded into an accepted query.
+
+## Latest published release: Engine 0.183.366
[Engine 0.183.366](https://github.com/PastureStack/orchestration-engine/releases/tag/v0.183.366) is published with 12 original assets verified by
anonymous SHA256 readback. Use the
@@ -177,7 +194,7 @@ Before publishing an Engine artifact, run the complete package gate:
```sh
bash scripts/check-cattle-jdk25-full-package
-ENGINE_VERSION=0.183.366 bash scripts/build --release
+ENGINE_VERSION=0.183.367 bash scripts/build --release
bash scripts/check-release-artifact dist/artifacts/cattle.jar
```
@@ -214,9 +231,11 @@ the credential identity and policy revision; that same Key must apply them.
Using a browser's shared Cookie alone does not establish strict credential
isolation. Ordinary UI/API authorization remains unchanged.
-* `POST /v2-beta/projects/{projectId}/aiServiceUpgradePlans` with `serviceId` and
- `image` creates a five-minute actor/project-bound preview and, for Key callers,
- a credential/revision binding. It does not upgrade a service.
+* `POST /v2-beta/projects/{projectId}/aiServiceUpgradePlans` with `serviceId`,
+ `image` and optional `startFirst` / `batchSize` / `intervalMillis` creates a
+ five-minute actor/project-bound preview and, for Key callers, a credential/revision
+ binding. The effective strategy is immutable and shown for human review.
+ It does not upgrade a service.
* The confirmation UI separately calls `POST .../{planId}?action=confirm` with an
empty body. The same owner's human session confirms through the console;
API Keys cannot confirm, and a bound Key's current scope, expiry and revision
diff --git a/code/framework/api-pub-sub-jetty/pom.xml b/code/framework/api-pub-sub-jetty/pom.xml
index 3191ed3745..ed2e39df2d 100644
--- a/code/framework/api-pub-sub-jetty/pom.xml
+++ b/code/framework/api-pub-sub-jetty/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/api-pub-sub/pom.xml b/code/framework/api-pub-sub/pom.xml
index e5bfcd0085..afb9eaceea 100644
--- a/code/framework/api-pub-sub/pom.xml
+++ b/code/framework/api-pub-sub/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/api/pom.xml b/code/framework/api/pom.xml
index c1521b1a8b..3bdb69e3aa 100644
--- a/code/framework/api/pom.xml
+++ b/code/framework/api/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/archaius/pom.xml b/code/framework/archaius/pom.xml
index f46934b45f..80ceb00292 100644
--- a/code/framework/archaius/pom.xml
+++ b/code/framework/archaius/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-meta-parent
- 0.183.366
+ 0.183.367
../../meta-parent/pom.xml
diff --git a/code/framework/async/pom.xml b/code/framework/async/pom.xml
index 8eafe80a66..915b3a3b5b 100644
--- a/code/framework/async/pom.xml
+++ b/code/framework/async/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/auditing/pom.xml b/code/framework/auditing/pom.xml
index 3fdcb2c9a6..349031c9a4 100644
--- a/code/framework/auditing/pom.xml
+++ b/code/framework/auditing/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/db-loader/pom.xml b/code/framework/db-loader/pom.xml
index 936117cee1..c325ecf947 100644
--- a/code/framework/db-loader/pom.xml
+++ b/code/framework/db-loader/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/deferred/pom.xml b/code/framework/deferred/pom.xml
index 45d50246d1..4f8bb2fd92 100644
--- a/code/framework/deferred/pom.xml
+++ b/code/framework/deferred/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/encryption/pom.xml b/code/framework/encryption/pom.xml
index ac21aae936..9598f3b26b 100644
--- a/code/framework/encryption/pom.xml
+++ b/code/framework/encryption/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/engine/pom.xml b/code/framework/engine/pom.xml
index 0aba006241..de0ab2bead 100644
--- a/code/framework/engine/pom.xml
+++ b/code/framework/engine/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/eventing/pom.xml b/code/framework/eventing/pom.xml
index 0a2bb1c035..a69ca8de48 100644
--- a/code/framework/eventing/pom.xml
+++ b/code/framework/eventing/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/events/pom.xml b/code/framework/events/pom.xml
index c28c45845a..943495e100 100644
--- a/code/framework/events/pom.xml
+++ b/code/framework/events/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/extension-spring/pom.xml b/code/framework/extension-spring/pom.xml
index 9e59e72f3a..9a8bba6ecf 100644
--- a/code/framework/extension-spring/pom.xml
+++ b/code/framework/extension-spring/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/extension/pom.xml b/code/framework/extension/pom.xml
index 19a6b394f7..9dcf90b218 100644
--- a/code/framework/extension/pom.xml
+++ b/code/framework/extension/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/java-server/pom.xml b/code/framework/java-server/pom.xml
index cb7e0c7a4f..f777d20bb7 100644
--- a/code/framework/java-server/pom.xml
+++ b/code/framework/java-server/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/jmx/pom.xml b/code/framework/jmx/pom.xml
index bcba2408e4..a6137215c1 100644
--- a/code/framework/jmx/pom.xml
+++ b/code/framework/jmx/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/jooq/pom.xml b/code/framework/jooq/pom.xml
index 55619d0f1a..bac1e06f35 100644
--- a/code/framework/jooq/pom.xml
+++ b/code/framework/jooq/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/json/pom.xml b/code/framework/json/pom.xml
index c7eb19e829..859c1086cf 100644
--- a/code/framework/json/pom.xml
+++ b/code/framework/json/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/launcher/pom.xml b/code/framework/launcher/pom.xml
index 3d321de0fa..d1db55bac0 100644
--- a/code/framework/launcher/pom.xml
+++ b/code/framework/launcher/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/lock/pom.xml b/code/framework/lock/pom.xml
index fd9822fc88..4c24099f31 100644
--- a/code/framework/lock/pom.xml
+++ b/code/framework/lock/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/logback/pom.xml b/code/framework/logback/pom.xml
index ac27e218ab..685b0f318c 100644
--- a/code/framework/logback/pom.xml
+++ b/code/framework/logback/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-meta-parent
- 0.183.366
+ 0.183.367
../../meta-parent/pom.xml
diff --git a/code/framework/managed-context/pom.xml b/code/framework/managed-context/pom.xml
index e35b4d5036..eb409344fe 100644
--- a/code/framework/managed-context/pom.xml
+++ b/code/framework/managed-context/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/metrics/pom.xml b/code/framework/metrics/pom.xml
index 8af58d28f3..87cbff81bb 100644
--- a/code/framework/metrics/pom.xml
+++ b/code/framework/metrics/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/module/pom.xml b/code/framework/module/pom.xml
index 9242eb2bad..52542c681e 100644
--- a/code/framework/module/pom.xml
+++ b/code/framework/module/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/object/pom.xml b/code/framework/object/pom.xml
index bce652d977..458bd95d92 100644
--- a/code/framework/object/pom.xml
+++ b/code/framework/object/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/pool/pom.xml b/code/framework/pool/pom.xml
index caf6095d42..b1e5c57fa9 100644
--- a/code/framework/pool/pom.xml
+++ b/code/framework/pool/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/resource-monitor/pom.xml b/code/framework/resource-monitor/pom.xml
index 0fbe28ead9..5c040c1607 100644
--- a/code/framework/resource-monitor/pom.xml
+++ b/code/framework/resource-monitor/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/schema/pom.xml b/code/framework/schema/pom.xml
index b2cfe6c0b5..259986998b 100644
--- a/code/framework/schema/pom.xml
+++ b/code/framework/schema/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/server/pom.xml b/code/framework/server/pom.xml
index 8114bedd91..6b7d159562 100644
--- a/code/framework/server/pom.xml
+++ b/code/framework/server/pom.xml
@@ -4,7 +4,7 @@
cattle-meta-parent
io.cattle
- 0.183.366
+ 0.183.367
../../meta-parent/pom.xml
diff --git a/code/framework/spring/pom.xml b/code/framework/spring/pom.xml
index 9b193bf375..7eceeee309 100644
--- a/code/framework/spring/pom.xml
+++ b/code/framework/spring/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/system-task/pom.xml b/code/framework/system-task/pom.xml
index b57bdf467d..cbc0f4b280 100644
--- a/code/framework/system-task/pom.xml
+++ b/code/framework/system-task/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/token/pom.xml b/code/framework/token/pom.xml
index 64d42d2b7f..60a3ca76c5 100644
--- a/code/framework/token/pom.xml
+++ b/code/framework/token/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/framework/utils/pom.xml b/code/framework/utils/pom.xml
index e5159c176d..caed6301f5 100644
--- a/code/framework/utils/pom.xml
+++ b/code/framework/utils/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/agent-instance/pom.xml b/code/iaas/agent-instance/pom.xml
index d52dc7605b..e6b196ed1e 100644
--- a/code/iaas/agent-instance/pom.xml
+++ b/code/iaas/agent-instance/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/agent-server/pom.xml b/code/iaas/agent-server/pom.xml
index 28dbd1d760..928fdaf637 100644
--- a/code/iaas/agent-server/pom.xml
+++ b/code/iaas/agent-server/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/agent/pom.xml b/code/iaas/agent/pom.xml
index 00edfbbcdd..ec08f69f4c 100644
--- a/code/iaas/agent/pom.xml
+++ b/code/iaas/agent/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/allocator/pom.xml b/code/iaas/allocator/pom.xml
index ff4066e06b..b57761cde4 100644
--- a/code/iaas/allocator/pom.xml
+++ b/code/iaas/allocator/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/api-logic/pom.xml b/code/iaas/api-logic/pom.xml
index 2352f21955..80f543a390 100644
--- a/code/iaas/api-logic/pom.xml
+++ b/code/iaas/api-logic/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/archaius-management/pom.xml b/code/iaas/archaius-management/pom.xml
index 0de183a1e2..36d59accb1 100644
--- a/code/iaas/archaius-management/pom.xml
+++ b/code/iaas/archaius-management/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.366
+ 0.183.367
../../parent/pom.xml
diff --git a/code/iaas/auth-logic/pom.xml b/code/iaas/auth-logic/pom.xml
index 298472a2b1..d5942b2a12 100644
--- a/code/iaas/auth-logic/pom.xml
+++ b/code/iaas/auth-logic/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.366
+ 0.183.367
../../parent/pom.xml
@@ -18,6 +18,16 @@
spring-jdbc
test
+
+ org.springframework
+ spring-context
+ test
+
+
+ org.aspectj
+ aspectjweaver
+ test
+
io.cattle
cattle-docker-api
diff --git a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryInputHandler.java b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryInputHandler.java
new file mode 100644
index 0000000000..c26fae3677
--- /dev/null
+++ b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryInputHandler.java
@@ -0,0 +1,16 @@
+package io.cattle.platform.iaas.api.auth.ai.audit;
+
+import io.github.ibuildthecloud.gdapi.request.ApiRequest;
+import io.github.ibuildthecloud.gdapi.request.handler.AbstractResponseGenerator;
+
+/** Capture only the typed owner's closed-field violation before ordinary schema sanitization. */
+public class AiAuditQueryInputHandler extends AbstractResponseGenerator {
+ @Override
+ protected void generate(ApiRequest request) {
+ if (!"POST".equals(request.getMethod()) || request.getId() != null || request.getAction() != null
+ || request.getSchemaFactory() == null) return;
+ var schema = request.getSchemaFactory().getSchema(request.getType());
+ if (schema == null || !"aiAuditQuery".equals(schema.getId())) return;
+ AiAuditQueryService.captureOriginalFields(request);
+ }
+}
diff --git a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryService.java b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryService.java
index b68d8b5ee2..78240055e4 100644
--- a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryService.java
+++ b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/audit/AiAuditQueryService.java
@@ -19,6 +19,7 @@
/** The Engine observes the complete Go-owner result before durably recording its canonical outcome. */
public class AiAuditQueryService {
+ static final String ORIGINAL_FIELDS_REJECTED = AiAuditQueryService.class.getName() + ".originalFieldsRejected";
static final Set INPUTS = Set.of("keyId", "environmentId", "from", "to", "lastMinutes", "decision",
"outcome", "operation", "targetType", "requestId", "httpStatus", "limit", "offset", "eventId");
static final Set ROW_FIELDS = Set.of("type", "id", "created", "accountId", "authenticatedAsAccountId", "keyId",
@@ -36,6 +37,7 @@ public Map read(ApiRequest request) {
try {
actor = keys.actor(request);
if (actor == null || actor.getAuthenticatedAsAccountId() <= 0) throw error(403, "PermissionDenied");
+ if (Boolean.TRUE.equals(request.getAttribute(ORIGINAL_FIELDS_REJECTED))) throw error(422, "AuditQueryInputInvalid");
Map query = input(request.getRequestObject());
var servlet = request.getServletContext();
if (servlet == null || servlet.getRequest() == null) throw error(403, "PermissionDenied");
@@ -88,6 +90,16 @@ public Map read(ApiRequest request) {
}
}
+ static void captureOriginalFields(ApiRequest request) {
+ // Sticky across any repeated handler invocation. Retain no raw body,
+ // field values or caller-supplied field names in request attributes.
+ if (Boolean.TRUE.equals(request.getAttribute(ORIGINAL_FIELDS_REJECTED))) return;
+ Object value = request.getRequestObject();
+ boolean rejected = !(value instanceof Map, ?> fields)
+ || fields.keySet().stream().anyMatch(key -> !(key instanceof String) || !"type".equals(key) && !INPUTS.contains(key));
+ request.setAttribute(ORIGINAL_FIELDS_REJECTED, rejected);
+ }
+
static Map input(Object value) {
Map values = object(value); values.remove("type");
if (!INPUTS.containsAll(values.keySet())) throw error(422, "AuditQueryInputInvalid");
diff --git a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchAccess.java b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchAccess.java
index ac58641c22..0acf2f4d76 100644
--- a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchAccess.java
+++ b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchAccess.java
@@ -78,6 +78,15 @@ public ApiAuthenticator.CurrentAuthorization current(long actor, long project, A
/** Only persisted, already ownership-checked plans may supply the expected binding. */
public Authorization bound(Map plan, ApiRequest request) {
+ return bound(plan, request, true);
+ }
+
+ /** Operand prefetch must not retain a credential row lock in the outer API transaction either. */
+ Authorization snapshot(Map plan, ApiRequest request) {
+ return bound(plan, request, false);
+ }
+
+ private Authorization bound(Map plan, ApiRequest request, boolean lockCredential) {
long actor = positive(plan.get("actorId")), project = positive(plan.get("accountId"));
var current = current(actor, project, request);
if (plan.get("credentialId") == null) return new Authorization(current, null);
@@ -85,10 +94,11 @@ public Authorization bound(Map plan, ApiRequest request) {
if (!(plan.get("credentialRevision") instanceof Number expectedRevision) || expectedRevision.longValue() < 0
|| expectedRevision.doubleValue() != expectedRevision.longValue()) throw error(403, "OwnerPermissionDenied");
long revision = expectedRevision.longValue();
- // Current locking read, not an older ordinary SELECT from an API's RR
- // snapshot. Keep this credential stable until the surrounding write commits.
- Credential credential = create().selectFrom(io.cattle.platform.core.model.tables.CredentialTable.CREDENTIAL)
- .where(io.cattle.platform.core.model.tables.CredentialTable.CREDENTIAL.ID.eq(id)).forUpdate().fetchOne();
+ // Final get/action uses a current locking read and keeps the credential stable until commit.
+ // Operand snapshots are rechecked by action() before any claim, without retaining a row lock here.
+ var query = create().selectFrom(io.cattle.platform.core.model.tables.CredentialTable.CREDENTIAL)
+ .where(io.cattle.platform.core.model.tables.CredentialTable.CREDENTIAL.ID.eq(id));
+ Credential credential = lockCredential ? query.forUpdate().fetchOne() : query.fetchOne();
if (credential == null || credential.getRemoved() != null || !"active".equals(credential.getState())
|| !Set.of(CredentialConstants.KIND_API_KEY, CredentialConstants.KIND_API_KEY_RESTRICTED).contains(credential.getKind()))
throw error(403, "ApiKeyRevoked");
diff --git a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchPlanResourceManager.java b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchPlanResourceManager.java
index ac4599d58c..1d813393c4 100644
--- a/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchPlanResourceManager.java
+++ b/code/iaas/auth-logic/src/main/java/io/cattle/platform/iaas/api/auth/ai/catalog/AiCatalogLaunchPlanResourceManager.java
@@ -15,7 +15,12 @@ public class AiCatalogLaunchPlanResourceManager extends AbstractNoOpResourceMana
@Override public Class>[] getTypeClasses() { return new Class>[] { AiCatalogStackLaunchPlan.class }; }
@Override protected Object createInternal(String type, ApiRequest request) { return resource(service.preview(request)); }
@Override protected Object getByIdInternal(String type, String id, ListOptions options) {
- return resource(service.get(id, ApiContext.getContext().getApiRequest()));
+ ApiRequest request = ApiContext.getContext().getApiRequest();
+ // Native action validation/prefetch runs inside the outer API write transaction.
+ // Its operand lookup must not retain row locks before action() acquires the application lock.
+ boolean actionTarget = request != null && "POST".equals(request.getMethod()) && request.getAction() != null
+ && options != null && options.getMutationTarget() != null && options.getMutationTarget().matches(request, type, id);
+ return resource(actionTarget ? service.actionTarget(id, request) : service.get(id, request));
}
@Override protected Object listInternal(SchemaFactory factory, String type, Map