diff --git a/.github/workflows/security-release-gate.yml b/.github/workflows/security-release-gate.yml
index 36b31c0396..e7cffa5e62 100644
--- a/.github/workflows/security-release-gate.yml
+++ b/.github/workflows/security-release-gate.yml
@@ -313,6 +313,46 @@ jobs:
'versionRootRechecksEventProofOriginalOwnerSourceAndFrozenStackBeforeMetadata',
'versionRootRejectsUnknownPathsMethodsQueriesAndContradictoryRouteShapesWithSafe403',
},
+ 'io.cattle.platform.engine.process.ProcessAuthorizationProjectUpdateTest': {
+ 'capturedNativeProjectUpdateProofCannotBeForgedRetargetedOrGrowFields',
+ },
+ 'io.cattle.platform.iaas.api.auth.projects.NativeProjectQueuedOwnerTest': {
+ 'ordinaryNativeOwnerFullLegacyCustomAndLiveDenialsUseManagerCaptureWithoutAi',
+ },
+ 'io.cattle.platform.iaas.api.auth.change.AiProjectMetadataChangeTest': {
+ 'accountRootLockUsesTheActualAccountRowAndDetectsConcurrentMetadataChange',
+ 'customReadKeyDoesNotBorrowGlobalOwnerWriteAuthority',
+ 'durableAccountUpdateBindingUsesSelectedRootAndSeparateNativePrincipal',
+ 'metadataOwnerUsesNativeProjectOwnerAndGlobalSchemaWithoutGrantingObjects',
+ 'projectDescriptorIsMetadataOnlyAndKeepsNativeTextBounds',
+ 'selectedAccountIdentityAndScalarSnapshotNeverCopyConfigurationOrCredentials',
+ 'selectedProjectReadAndPreflightUseFreshGlobalNativeOwnerThenRestoreSelectedContext',
+ 'typedProjectProcessCannotFallbackWhenDurableBindingIsMissingOrMismatched',
+ 'unknownProjectExecutionKeepsOnlyItsOriginalDurableProcessWithoutReplayOrTerminalAuthority',
+ },
+ 'io.cattle.platform.iaas.api.auditing.AiServiceUpgradeAuditTest': {
+ 'bindingReasonDetailSurvivesJsonOutboxReplayAndCanonicalProjectionAsClientEvidence',
+ 'privateProjectCaptureKeepsSelectedAuditScopeForPrimaryAndQueueOutcomes',
+ },
+ 'io.cattle.platform.iaas.api.auth.ai.AiToolCallEventServiceTest': {
+ 'bindingDetailCapabilityKeepsLegacyConstructorAndDurableReadonlyAcksCompatible',
+ 'invalidReasonDetailsNeverReachDurableSink',
+ },
+ 'io.cattle.platform.iaas.api.auth.ai.view.AiProjectCollectionOwnerTest': {
+ 'adminNativeFullAndLegacySeeNonMembershipProjectsWithoutWideningMemberOrReadonly',
+ 'allIsPrivateToTypedProjectCollectionAndNeverChangesSingleGetOrOtherTypesOrAcceptsModelAll',
+ 'exactProjectCustomDenyAndReadonlyKeysRetainExactSetsThroughNativeCommonCollectionFilter',
+ },
+ 'io.cattle.platform.iaas.api.auth.change.AiLoadBalancerMetadataChangeTest': {
+ 'onlyLoadBalancerMetadataFieldsAndIntentAreAdmitted',
+ 'freshNativeStackAndKeyChecksProtectTheSparsePutAndPrivateParentRead',
+ 'scalarSnapshotsAndStackContextCasExcludeConfigurationAndRejectScopeOrMovement',
+ 'loadBalancerPlanKeepsHumanApprovalSnapshotCasRevocationAndOneClaim',
+ 'loadBalancerMetadataLocksActualServiceRowAndDetectsNativeScalarChanges',
+ 'queuedNativeServiceUpdateUsesExactLbKindUuidStackAndCapturedProcess',
+ 'queuedNativeOwnerRetainsFullLegacySchemaRevisionAndRevocationChecks',
+ 'ordinaryNativeV1V2FullAndLegacyCaptureNeverEnterTheAiMandatoryBranch',
+ },
}
suites = []
observed = {}
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index ae0dae62cc..ebeb85ab3d 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -1,5 +1,18 @@
# Compatibility Contract
+## Engine 0.183.372 candidate — not published
+
+Engine 0.183.372 candidate; not published or runtime qualified.
+
+Selected Project name/description updates use the native Project queued owner, fresh role/schema/Key limits, normal human approval, revision/CAS and the original durable claim. Visible nonselected Projects remain queryable but require normal context selection and a fresh reference before editing. Ordinary Full, legacy and custom native Project updates share the native owner; no generic HTTP tool or membership/network/auth change is added.
+
+Typed Project discovery uses the native all=true option and the common per-row Key collection filter. The optional fixed client-reported binding detail uses the existing audit start ACK capability; canonical evidence remains client_reported with NOT_EVALUATED/REPORTED, never a backend authorization denial. Six fixed role-field metadata entries permit only that optional input and read-only capability. HTTP tuple and native route/action grants remain unchanged.
+
+Load-balancer metadata adds only name and description through the existing native Service owner, fresh target/Stack reads, Key ceilings, human approval and one durable process claim. Ports, lbConfig, certificates, scaling, lifecycle and children are unchanged; the eight new source controls are not native acceptance.
+
+All 173 prior required identities are preserved and 26 new exact methods are registered (199 total). Source composition checks are separate from normal full App/Spring/schema/security CI, original artifact qualification and native business acceptance, which remain pending. Existing security findings remain disclosed; workflow SLSA is not supplied. Matching Web/Bridge and Server integration are separate; no whole-243 completion is claimed.
+
+
## Engine 0.183.371 published
Typed Host metadata visibility and receipt checks use the native Host model;
diff --git a/Dockerfile b/Dockerfile
index ff11d510ab..c46065d899 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -82,7 +82,7 @@ RUN set -eux; \
printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::http::Pipeline-Depth "0";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \
printf 'Types: deb\nURIs: https://snapshot.ubuntu.com/ubuntu/%s\nSuites: resolute-security\nComponents: main\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nSnapshot: no\n' \
"${UBUNTU_APT_SECURITY_SNAPSHOT}" > /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
set --; \
for package in libexpat1 libsqlite3-0 libssh2-1t64; do \
test "$(grep -c "^${package}=" /licenses/ubuntu-security-packages.lock)" -eq 1; \
@@ -100,7 +100,7 @@ RUN set -eux; \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_OPENSSL_VERSION}"; \
done; \
rm -f /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
set --; \
for package in libpython3.14-minimal libpython3.14-stdlib python3.14-minimal python3.14 python3.14-venv curl libcurl3t64-gnutls libcurl4t64 ca-certificates libbrotli1 libpcre2-8-0 libpcre2-posix3; do \
architecture=amd64; \
@@ -144,7 +144,7 @@ RUN set -eux; \
xz-utils="${UBUNTU_APT_XZ_UTILS_VERSION}"; \
printf 'Types: deb\nURIs: https://snapshot.ubuntu.com/ubuntu/%s\nSuites: resolute-security\nComponents: main\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nSnapshot: no\n' \
"${UBUNTU_APT_SECURITY_SNAPSHOT}" > /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
for package in libssl3t64 openssl openssl-provider-legacy; do \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_OPENSSL_VERSION}"; \
done; \
diff --git a/Dockerfile.dapper b/Dockerfile.dapper
index 79a49a440a..ddaaf8ed94 100644
--- a/Dockerfile.dapper
+++ b/Dockerfile.dapper
@@ -99,7 +99,7 @@ RUN set -eux; \
printf 'Acquire::Retries "5";\nAcquire::http::Timeout "30";\nAcquire::https::Timeout "30";\nAcquire::http::Pipeline-Depth "0";\nAcquire::https::CaInfo "/etc/ssl/certs/ca-certificates.crt";\nAcquire::https::Verify-Peer "true";\nAcquire::https::Verify-Host "true";\nAcquire::AllowInsecureRepositories "false";\nAPT::Get::AllowUnauthenticated "false";\n' > /etc/apt/apt.conf.d/80pasturestack-retries; \
printf 'Types: deb\nURIs: https://snapshot.ubuntu.com/ubuntu/%s\nSuites: resolute-security\nComponents: main\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nSnapshot: no\n' \
"${UBUNTU_APT_SECURITY_SNAPSHOT}" > /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
set --; \
for package in libexpat1 libsqlite3-0 libssh2-1t64; do \
test "$(grep -c "^${package}=" /licenses/ubuntu-security-packages.lock)" -eq 1; \
@@ -117,7 +117,7 @@ RUN set -eux; \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_OPENSSL_VERSION}"; \
done; \
rm -f /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
set --; \
for package in libpython3.14-minimal libpython3.14-stdlib python3.14-minimal python3.14 python3.14-venv curl libcurl3t64-gnutls libcurl4t64 ca-certificates libbrotli1 libpcre2-8-0 libpcre2-posix3; do \
architecture=amd64; \
@@ -161,7 +161,7 @@ RUN set -eux; \
xz-utils="${UBUNTU_APT_XZ_UTILS_VERSION}"; \
printf 'Types: deb\nURIs: https://snapshot.ubuntu.com/ubuntu/%s\nSuites: resolute-security\nComponents: main\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nSnapshot: no\n' \
"${UBUNTU_APT_SECURITY_SNAPSHOT}" > /etc/apt/sources.list.d/pasturestack-security.sources; \
- apt-get update; \
+ apt-get update --error-on=any; \
for package in libssl3t64 openssl openssl-provider-legacy; do \
test "$(dpkg-query -W -f='${Version}' "${package}")" = "${UBUNTU_APT_OPENSSL_VERSION}"; \
done; \
diff --git a/README.md b/README.md
index 9843009a6c..129ad2122e 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,10 @@ 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.
+## Next candidate: Engine 0.183.372
+
+Selected-Project and load-balancer name/description metadata, native queued-owner authorization and fixed client-reported audit detail are in [source and CI preparation](docs/releases/orchestration-engine-0.183.372.md). Publication and native acceptance are pending; use the published release below.
+
## Latest published release: Engine 0.183.371
[Engine 0.183.371](https://github.com/PastureStack/orchestration-engine/releases/tag/v0.183.371)
@@ -177,7 +181,7 @@ Before publishing an Engine artifact, run the complete package gate:
```sh
bash scripts/check-cattle-jdk25-full-package
-ENGINE_VERSION=0.183.371 bash scripts/build --release
+ENGINE_VERSION=0.183.372 bash scripts/build --release
bash scripts/check-release-artifact dist/artifacts/cattle.jar
```
diff --git a/code/framework/api-pub-sub-jetty/pom.xml b/code/framework/api-pub-sub-jetty/pom.xml
index 12c737806a..b2f5e23535 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.371
+ 0.183.372
../../parent/pom.xml
diff --git a/code/framework/api-pub-sub/pom.xml b/code/framework/api-pub-sub/pom.xml
index 7cd38434fd..b9f02dce7d 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.371
+ 0.183.372
../../parent/pom.xml
diff --git a/code/framework/api/pom.xml b/code/framework/api/pom.xml
index 2ad250b951..dcbf693d3b 100644
--- a/code/framework/api/pom.xml
+++ b/code/framework/api/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.371
+ 0.183.372
../../parent/pom.xml
diff --git a/code/framework/api/src/main/java/io/cattle/platform/api/auth/ClientToolAuditEvent.java b/code/framework/api/src/main/java/io/cattle/platform/api/auth/ClientToolAuditEvent.java
index e0c6133dd3..e2faffe9d7 100644
--- a/code/framework/api/src/main/java/io/cattle/platform/api/auth/ClientToolAuditEvent.java
+++ b/code/framework/api/src/main/java/io/cattle/platform/api/auth/ClientToolAuditEvent.java
@@ -4,7 +4,12 @@
/** Bounded client evidence, never proof of authorization or business completion. */
public record ClientToolAuditEvent(String requestId, String command, String phase,
- String status, String reason, String correlationId) {
+ String status, String reason, String correlationId, String reasonDetail) {
+ public static final String BRIDGE_PROJECT_BINDING_REQUIRED = "BRIDGE_PROJECT_BINDING_REQUIRED";
+ public ClientToolAuditEvent(String requestId, String command, String phase,
+ String status, String reason, String correlationId) {
+ this(requestId, command, phase, status, reason, correlationId, null);
+ }
public static final Set COMMANDS = Set.of("find_stacks", "find_services", "get_service",
"preview_service_upgrade", "apply_service_upgrade", "get_operation", "update_service_description",
"list_capabilities", "find_resources", "get_resource", "preview_resource_change", "apply_resource_change", "unknown");
@@ -19,6 +24,9 @@ public static boolean validId(String value) {
public boolean valid() {
return validId(requestId) && command != null && COMMANDS.contains(command)
&& status != null && STATUSES.contains(status) && reason != null && REASONS.contains(reason)
+ && (reasonDetail == null || BRIDGE_PROJECT_BINDING_REQUIRED.equals(reasonDetail)
+ && "preview_resource_change".equals(command) && "result".equals(phase)
+ && "needs_user".equals(status) && "NEEDS_USER".equals(reason))
&& (("start".equals(phase) && "pending".equals(status) && correlationId == null)
|| ("result".equals(phase) && !"pending".equals(status) && validId(correlationId)));
}
diff --git a/code/framework/archaius/pom.xml b/code/framework/archaius/pom.xml
index eb44a73e08..e4e6384699 100644
--- a/code/framework/archaius/pom.xml
+++ b/code/framework/archaius/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-meta-parent
- 0.183.371
+ 0.183.372
../../meta-parent/pom.xml
diff --git a/code/framework/async/pom.xml b/code/framework/async/pom.xml
index 115146baeb..3a79ef8a42 100644
--- a/code/framework/async/pom.xml
+++ b/code/framework/async/pom.xml
@@ -4,7 +4,7 @@
io.cattle
cattle-parent
- 0.183.371
+ 0.183.372
../../parent/pom.xml
diff --git a/code/framework/auditing/pom.xml b/code/framework/auditing/pom.xml
index aa658c0ca5..4f7a81376a 100644
--- a/code/framework/auditing/pom.xml
+++ b/code/framework/auditing/pom.xml
@@ -4,7 +4,7 @@
cattle-parent
io.cattle
- 0.183.371
+ 0.183.372
../../parent/pom.xml
diff --git a/code/framework/auditing/src/main/java/io/cattle/platform/iaas/api/auditing/AuditServiceImpl.java b/code/framework/auditing/src/main/java/io/cattle/platform/iaas/api/auditing/AuditServiceImpl.java
index 8baf734b46..3ec8691176 100644
--- a/code/framework/auditing/src/main/java/io/cattle/platform/iaas/api/auditing/AuditServiceImpl.java
+++ b/code/framework/auditing/src/main/java/io/cattle/platform/iaas/api/auditing/AuditServiceImpl.java
@@ -58,7 +58,7 @@ public class AuditServiceImpl implements AuditService, ApiKeyAuditSink, ApiReque
public static final Set API_KEY_AUDIT_FIELDS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
"eventId", "keyId", "decision", "outcome", "httpStatus", "responseCode", "requestId", "actor",
"targetType", "targetId", "operation", "policyRevision", "reason", "phase", "preview", "processId", "processName", "hostUuid", "failureCode",
- "clientReportedCommand", "clientReportedPhase", "clientReportedStatus", "clientReportedReason",
+ "clientReportedCommand", "clientReportedPhase", "clientReportedStatus", "clientReportedReason", "clientReportedReasonDetail",
"clientCorrelationId", "evidenceSource", "channel")));
private static final Set SENSITIVE_AUDIT_FIELDS = Set.of(
"secretvalue", "secretvalues", "password", "newsecret", "oldsecret", "adminaccountpassword", "serviceaccountpassword",
@@ -374,7 +374,8 @@ private void recordServiceDecision(ApiRequest request, Policy policy, String act
@Override
public void recordResourceChangeDecision(ApiRequest request, Policy policy, String action, String receiptId,
String resourceType, Long resourceId, boolean allowed, String reason) {
- if (!Set.of("stack", "service", "container", "externalService", "dnsService", "certificate", "host", "aiResourceChangePlan")
+ if (!Set.of("stack", "service", "container", "externalService", "dnsService", "certificate", "host", "project",
+ "loadBalancerService", "aiResourceChangePlan")
.contains(resourceType))
throw new IllegalArgumentException("Invalid resource-change audit target");
recordTypedResourceDecision(request, policy, action, receiptId, "aichange", resourceType, resourceId,
@@ -434,6 +435,7 @@ public void recordClientToolEvent(ApiRequest request, Policy policy, ClientToolA
data.put("evidenceSource", "client_reported"); data.put("channel", "webmcp-client-report");
data.put("clientReportedCommand", report.command()); data.put("clientReportedPhase", report.phase());
data.put("clientReportedStatus", report.status()); data.put("clientReportedReason", report.reason());
+ if (report.reasonDetail() != null) data.put("clientReportedReasonDetail", report.reasonDetail());
if (report.correlationId() != null) data.put("clientCorrelationId", report.correlationId());
Map event = new HashMap<>();
event.put("resourceType", "aiToolCallEvent"); event.put("resourceId", null);
diff --git a/code/framework/auditing/src/test/java/io/cattle/platform/iaas/api/auditing/AiServiceUpgradeAuditTest.java b/code/framework/auditing/src/test/java/io/cattle/platform/iaas/api/auditing/AiServiceUpgradeAuditTest.java
index fb457757dc..5eb756d3b8 100644
--- a/code/framework/auditing/src/test/java/io/cattle/platform/iaas/api/auditing/AiServiceUpgradeAuditTest.java
+++ b/code/framework/auditing/src/test/java/io/cattle/platform/iaas/api/auditing/AiServiceUpgradeAuditTest.java
@@ -25,6 +25,68 @@
public class AiServiceUpgradeAuditTest {
@Rule public TemporaryFolder temporary = new TemporaryFolder();
+ @Test public void privateProjectCaptureKeepsSelectedAuditScopeForPrimaryAndQueueOutcomes() throws Exception {
+ // Auth-logic's durableAccountUpdateBinding test owns private capture and the native owner.
+ // This module owns canonical projection of that public audit/queue contract.
+ List