diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 2163f6779f..aa52ebb217 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -1,5 +1,18 @@
# Compatibility Contract
+
+
+## Server v1.6.554 prepared, not published
+
+The candidate integrates Web Console `1.6.196` and Engine `v0.183.355`.
+The build verifies the browser's enabled capability ceiling and off initial
+mode against the exact archive. Backend activation remains explicit.
+Seven typed tools share current RBAC and per-Key policy checks; service-upgrade
+plans require separate authenticated human approval. No general action proxy
+or approval tool is exposed. See the [candidate note](docs/releases/server-1.6.554.md)
+and [AI collaboration guide](docs/ai-collaboration.md).
+Final publication identities and 8080 acceptance will be recorded after release.
+
## Server v1.6.553 published
diff --git a/README.md b/README.md
index a4be1d96b6..1e657f9528 100644
--- a/README.md
+++ b/README.md
@@ -11,21 +11,18 @@ upstream history, authorship, licenses, and notices.
-## Current release
+## Current source candidate
-[Server v1.6.553](https://github.com/PastureStack/server/releases/tag/v1.6.553)
-is published. It applies the accepted Google Brotli fix for CVE-2026-111497 through the qualified
-Ubuntu backport bundle. Only already-installed packages are updated; package sets,
-providers, library interfaces and configuration are preserved.
+Server `v1.6.554` is a prepared source candidate, not a published image.
+It integrates Web Console `1.6.196` and Engine `v0.183.355` for optional,
+scoped AI collaboration. New pages start with tools off. Upgrade execution
+requires an authenticated owner's separate confirmation; ordinary API Keys
+retain their existing permissions. See the [candidate note](docs/releases/server-1.6.554.md).
-Web Console `1.6.195` and its original runtime SBOM, Engine `v0.183.354`,
-Host API `0.38.8` and Temurin `25.0.5+8` retain their verified inputs.
-Node Agent `v1.2.34` uses compatibility base `v0.3.8`. Published Catalog
-`v0.3.18` records the qualified component images and their publication evidence.
-
-See its [release note](docs/releases/server-1.6.553.md) and the
-[compatibility contract](COMPATIBILITY.md#server-v16553-brotli)
-for producer identities, immutable image digests, SBOMs and remaining findings.
+The previous published release is
+[Server v1.6.553](https://github.com/PastureStack/server/releases/tag/v1.6.553),
+with its [release note](docs/releases/server-1.6.553.md) and
+[immutable identities](COMPATIBILITY.md#server-v16553-brotli).
Personal API Keys remain available without an accessible environment. See the [Host guide](docs/hosts/README.md) before enrolling a host.
@@ -49,16 +46,27 @@ and a compatible Linux Node Agent lifecycle. Open a Key's audit from its action
menu. See the [API Key guide](docs/api-keys.md) for policy and audit contracts.
Maintained CLI `0.6.20` propagates rejected requests as nonzero errors. Legacy `0.6.14` can silently exit zero; see [client compatibility](COMPATIBILITY.md#maintained-cli-error-reporting) and check HTTP status explicitly for automation.
+## Optional AI collaboration
+
+The Web Console provides seven bounded service tools, an explicit per-page
+on/off control and human review of upgrade plans. An optional stdio bridge lets
+compatible clients use a dedicated scoped API Key. Both reuse current account
+permissions; a Key cannot approve its own upgrade. See the
+[AI collaboration guide](docs/ai-collaboration.md) for activation, supported
+operations, audit records and rollback.
+
## Quick start
-Public GHCR downloads do not require a registry login. Keep all three named volumes.
+These examples target the candidate; run them only after its official release.
+This candidate is not yet available from GHCR. Public downloads do not require
+a registry login. Keep all three named volumes.
```sh
docker run -d --name pasturestack-server --restart unless-stopped -p 8080:8080 \
-v pasturestack-cattle:/var/lib/cattle \
-v pasturestack-mysql:/var/lib/mysql \
-v pasturestack-mysqllog:/var/log/mysql \
- ghcr.io/pasturestack/server:v1.6.553
+ ghcr.io/pasturestack/server:v1.6.554
```
For HTTPS termination at a reverse proxy, set the exact public origin:
@@ -66,7 +74,7 @@ For HTTPS termination at a reverse proxy, set the exact public origin:
```yaml
services:
pasturestack-server:
- image: ghcr.io/pasturestack/server:v1.6.553
+ image: ghcr.io/pasturestack/server:v1.6.554
restart: unless-stopped
ports:
- "8080:8080"
diff --git a/docs/README.md b/docs/README.md
index 290f33bdc9..1970e0d873 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -7,6 +7,9 @@ runtime gates have passed.
## Release notes
+- [Server v1.6.554 candidate](releases/server-1.6.554.md) — optional seven-tool
+ AI collaboration with scoped Keys, human approval and current authority checks.
+
- [Server v1.6.553](releases/server-1.6.553.md) — published qualified Brotli
security backports, with exact artifact identities and scoped QA evidence
recorded separately from component publication.
diff --git a/docs/ai-collaboration.md b/docs/ai-collaboration.md
new file mode 100644
index 0000000000..56fb5e69b3
--- /dev/null
+++ b/docs/ai-collaboration.md
@@ -0,0 +1,74 @@
+# Optional AI collaboration
+
+Server v1.6.554 combines Web Console 1.6.196 and Engine 0.183.355. The supported
+tools are `find_stacks`, `find_services`, `get_service`,
+`update_service_description`, `preview_service_upgrade`,
+`apply_service_upgrade` and `get_operation`. Other platform operations remain
+available through the normal UI/API, not through a generic AI proxy.
+
+## Activation
+
+Append the following properties to the deployment's existing `JAVA_OPTS`,
+preserving its other arguments, and recreate only the Server container with
+the same volumes, network, restart policy and security settings:
+
+```text
+-Dapi.ai.service.upgrade.enabled=true -Dapi.ai.bridge.enabled=true
+```
+
+The first property enables the upgrade-plan API. The second permits dedicated
+AI bridge Keys. Both default to false. Disabling the bridge does not remove
+ordinary account/API Key permissions or alter an ordinary service API.
+
+The supplied Web Console supports the enabled build profile but starts each
+new page with native browser tools off. Use the header's AI collaboration panel
+to select read-only or enabled mode. Native browser support requires a secure
+context and feature detection; unsupported browsers can still review plans and
+use the optional stdio client. Do not disable browser security or install a
+polyfill to force native support.
+
+For custom Web Console builds, `WEBMCP_MODE` is the build capability ceiling,
+not the initial user setting. An off build does not register browser tools.
+The release archive records the ceiling and initial mode in `webmcp-build.json`.
+
+## Scoped clients and approval
+
+Create an **AI collaboration bridge** Key in the existing Key editor. Start
+from full access or deny-by-default and choose permitted resources and
+operations by name. Its maximum authority is always the owner's current role
+and membership. To bootstrap a scoped client, grant read on the selected
+environment resource as well as the intended stack/service operations; an
+environment-resource rule does not grant all its child stacks.
+
+Install the standalone bridge source archive and configure the client as
+described in the [bridge guide](https://github.com/PastureStack/web-console/blob/main/bridge/README.md).
+Keep the credential file local to that client. The bridge accepts HTTPS, with
+an HTTP exception only for loopback development connections. It does not take
+browser cookies, general-purpose Keys, arbitrary URLs or an approval command.
+
+The client finds a named service and previews the target image. The Key owner
+reviews the target and changes in the normal authenticated UI, then explicitly
+approves. The client applies that plan and reads the same operation until
+`verifiedResult=true`. Approval expires; changed targets, policy revisions,
+revocation or lost permissions require a new valid decision. An uncertain
+response is not permission to submit another upgrade.
+
+Description updates use the captured expected description and preserve the
+other service settings. A conflict requires reading the current value again.
+Do not automatically retry an uncertain write.
+
+## Audit and disabling
+
+Per-Key audit distinguishes the server's authorization decision, the HTTP
+result and the later operation result. Client-reported tool events are labelled
+separately; an ALLOW decision or accepted request is not proof of completion.
+Credentials, OTPs and description contents are not tool audit payloads.
+
+To stop native tools, select off in the panel. Before disabling the backend or
+downgrading, let accepted operations reach a terminal state, cancel unused
+plans, and revoke the dedicated bridge Keys. Stop local bridge processes and
+remove their private credential files when no longer needed. Retain the normal
+database/volume backup and previous Server image. Follow the
+[API Key rollback guide](upgrades/api-key-policy-rollback.md); do not assume an
+older Engine understands a newer Key policy or restore an older database while
+clients continue sending requests.
diff --git a/docs/performance/README.md b/docs/performance/README.md
index 3ab13b789d..3af4c153af 100644
--- a/docs/performance/README.md
+++ b/docs/performance/README.md
@@ -8,8 +8,9 @@ The following example is appropriate as a starting point for a dedicated
16-vCPU host with about 32 GiB of RAM. It is not a universal performance
profile; measure the real workload before increasing it further.
-The example uses the published numeric Server `v1.6.553` image.
-See the [release note](../releases/server-1.6.553.md) and the
+The example targets the Server `v1.6.554` candidate; run it only after its official release and image have been published and read back.
+The candidate is not yet available from GHCR.
+See the [release note](../releases/server-1.6.554.md) and the
[current release](../../README.md#current-release) for component versions and
release details. Preserve the existing runtime overrides and all three named
volumes during an upgrade or restart.
@@ -20,7 +21,7 @@ evidence is not a performance benchmark; measure the actual workload.
```yaml
services:
pasturestack-server:
- image: ghcr.io/pasturestack/server:v1.6.553
+ image: ghcr.io/pasturestack/server:v1.6.554
restart: unless-stopped
ports:
- "8080:8080"
diff --git a/docs/releases/server-1.6.554.md b/docs/releases/server-1.6.554.md
new file mode 100644
index 0000000000..aa01e5e8b3
--- /dev/null
+++ b/docs/releases/server-1.6.554.md
@@ -0,0 +1,61 @@
+# Server v1.6.554 candidate
+
+Prepared source only; no Server image or release has been published.
+The release workflow records the final image digest and source
+revision only after its required checks pass.
+
+## Verified component inputs
+
+- Web Console `1.6.196`: producer `4e3e4d51db003ef7b456166084d425bfb06e1518`,
+ archive SHA256 `4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa`.
+- Engine `v0.183.355`: producer `9ea138d1db1718cf65157c9564c379e62bafe9a0`,
+ `cattle.jar` SHA256 `a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f`.
+
+This release integrates Web Console `1.6.196` and Engine `v0.183.355` for optional
+AI collaboration. Engine uses official Guava `33.7.2-jre` for
+`CVE-2026-102554`; assembly verifies the exact nested artifact and rejects older
+or additional Guava jars. Other component versions and the existing security backport
+baseline are retained. The build recipe pins component commits, archive hashes
+and the Web Console runtime SBOM; the bridge has a separate source archive and
+SBOM because its Node.js process is not part of the browser bundle.
+
+Seven typed tools provide stack/service discovery, a bounded service read,
+conditional description updates, and service-upgrade preview, apply and status.
+New browser pages start with tools off. The owner enables them explicitly;
+upgrade plans require a separate authenticated human confirmation. The stdio
+bridge uses a dedicated scoped Key, pins its policy revision, and cannot approve
+its own plans. Current role, scope, expiry and revocation checks remain active.
+
+Upgrade completion includes replacement-instance readiness and removal of the
+old instance's ports, network attachments and mounts. Named/shared volume data
+is preserved. Normal live storage transitions use their existing authorization;
+the stricter retired-instance path does not add a general delete grant.
+
+## Bounded pre-release acceptance
+
+The isolated candidate completed two consecutive scoped-Key image upgrades and
+return to its original image, a weak-model upgrade through human confirmation,
+and an ordinary GENERAL/FULL Key upgrade as a control. Launch settings and
+mounted data were retained; retired instances and their live ports were gone.
+Server restart and browser refresh retained service state and data. Native
+tool registration, explicit off/read-only/enabled modes, role/scope rejection,
+conditional-description conflicts and per-Key audit were checked separately.
+
+The new collaboration panel passed 78 locale/theme/width cases, including the
+automatic theme's day/night timer transitions. This is scoped acceptance of the
+new tools and affected UI, not completion of every existing frontend feature or
+the entire planned permission matrix. Rejected pre-confirmation requests are
+expected negative tests, not successful upgrades.
+
+## Activation, upgrade and rollback
+
+Follow the [AI collaboration guide](../ai-collaboration.md) for the opt-in backend
+properties, dedicated Key and client setup. Keep the existing Compose
+environment, HTTPS origin, ports, AppArmor, restart policy and all three named
+volumes. Do not use a runtime bundle override or `docker compose down -v`.
+
+Before disabling the backend or downgrading, let accepted operations reach a
+terminal state, cancel unused plans and revoke the dedicated bridge Keys. Keep
+the preceding image and matching database/volume backup. The existing
+[API Key rollback requirements](../upgrades/api-key-policy-rollback.md) still
+apply; an old Engine must not receive newer policy-bearing Keys unnoticed.
diff --git a/scripts/check-server-api-explorer-patch.sh b/scripts/check-server-api-explorer-patch.sh
index 53368a9229..d9e16077a1 100755
--- a/scripts/check-server-api-explorer-patch.sh
+++ b/scripts/check-server-api-explorer-patch.sh
@@ -533,9 +533,10 @@ require_marker "$release_dockerfile" \
SERVER_INCREMENTAL_RELEASE_BASE_MISSING
for web_sbom_release_marker in \
'COPY --chmod=0755 artifacts/verify-web-console-sbom.py /usr/local/bin/verify-web-console-sbom' \
- 'ARG WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT=web-console-1.6.195-runtime.cdx.json' \
+ 'ARG WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT=web-console-1.6.196-runtime.cdx.json' \
'"${WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT}" "${WEB_CONSOLE_RUNTIME_SBOM_SHA256}" "${web_sbom}";' \
- '--lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}";' \
+ '--lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}"' \
+ '--webmcp-ceiling enabled;' \
'install -m 0644 "${web_sbom}" /out/runtime-licenses/web-console/runtime.cdx.json;' \
'echo "${WEB_CONSOLE_RUNTIME_SBOM_SHA256} /usr/share/licenses/pasturestack/web-console/runtime.cdx.json" | sha256sum -c -;' \
'ENV PASTURESTACK_WEB_CONSOLE_RUNTIME_SBOM_SHA256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256}'; do
@@ -570,7 +571,7 @@ python3 scripts/test-web-console-sbom.py
python3 server/security/test_web_console_sbom_scan.py
require_marker "$release_dockerfile" \
- 'ARG SERVER_RELEASE_TAG=v1.6.553' \
+ 'ARG SERVER_RELEASE_TAG=v1.6.554' \
SERVER_INCREMENTAL_RELEASE_DEFAULT_MISSING
require_marker "$release_dockerfile" \
'org.opencontainers.image.version="${SERVER_RELEASE_TAG}"' \
@@ -628,22 +629,22 @@ require_marker "$publish_workflow" \
'bash source/server/artifacts/verify-host-api-package.sh' \
SERVER_HOST_API_PRODUCER_RELEASE_CHECK_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_RELEASE_TAG=1.6.195' \
+ 'ARG WEB_CONSOLE_RELEASE_TAG=1.6.196' \
SERVER_INCREMENTAL_WEB_CONSOLE_VERSION_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.195.tar.gz' \
+ 'ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.196.tar.gz' \
SERVER_INCREMENTAL_WEB_CONSOLE_ARTIFACT_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_ARTIFACT_SHA256=26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3' \
+ 'ARG WEB_CONSOLE_ARTIFACT_SHA256=4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa' \
SERVER_INCREMENTAL_WEB_CONSOLE_HASH_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_COMMIT=f905bc12daae18d98a6ac65942625562fff2a424' \
+ 'ARG WEB_CONSOLE_COMMIT=4e3e4d51db003ef7b456166084d425bfb06e1518' \
SERVER_INCREMENTAL_WEB_CONSOLE_COMMIT_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_LOCK_SHA256=ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22' \
+ 'ARG WEB_CONSOLE_LOCK_SHA256=4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6' \
SERVER_INCREMENTAL_WEB_CONSOLE_LOCK_HASH_MISSING
require_marker "$release_dockerfile" \
- 'ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a' \
+ 'ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb' \
SERVER_INCREMENTAL_WEB_CONSOLE_SBOM_HASH_MISSING
require_marker "$release_dockerfile" \
"grep -aF 'hostsPage.permissionDenied'" \
@@ -652,22 +653,22 @@ require_marker "$release_dockerfile" \
'"hostsPage.permissionDenied":"您沒有權限在此環境中新增主機。"' \
SERVER_INCREMENTAL_WEB_CONSOLE_ZH_TW_PERMISSION_MESSAGE_MISSING
require_marker "$build_script" \
- 'web_console_commit=${WEB_CONSOLE_COMMIT:-f905bc12daae18d98a6ac65942625562fff2a424}' \
+ 'web_console_commit=${WEB_CONSOLE_COMMIT:-4e3e4d51db003ef7b456166084d425bfb06e1518}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_COMMIT_MISSING
require_marker "$build_script" \
- 'web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.195}' \
+ 'web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.196}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_VERSION_MISSING
require_marker "$build_script" \
- 'web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.195.tar.gz}' \
+ 'web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.196.tar.gz}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_ARTIFACT_MISSING
require_marker "$build_script" \
- 'web_console_artifact_sha256=${WEB_CONSOLE_ARTIFACT_SHA256:-26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3}' \
+ 'web_console_artifact_sha256=${WEB_CONSOLE_ARTIFACT_SHA256:-4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_HASH_MISSING
require_marker "$build_script" \
- 'web_console_lock_sha256=${WEB_CONSOLE_LOCK_SHA256:-ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22}' \
+ 'web_console_lock_sha256=${WEB_CONSOLE_LOCK_SHA256:-4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_LOCK_HASH_MISSING
require_marker "$build_script" \
- 'web_console_runtime_sbom_sha256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256:-ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a}' \
+ 'web_console_runtime_sbom_sha256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256:-44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb}' \
SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_SBOM_HASH_MISSING
require_marker "$release_dockerfile" \
'ARG CATALOG_SERVICE_VERSION=0.20.15' \
@@ -1138,21 +1139,21 @@ require_marker "$build_script" \
'--file server/Dockerfile.web-compose-release' \
SERVER_INCREMENTAL_RELEASE_BUILD_PATH_MISSING
require_marker "$build_script" \
- 'image=${IMAGE:-pasturestack-validation/server:v1.6.553}' \
+ 'image=${IMAGE:-pasturestack-validation/server:v1.6.554}' \
SERVER_INCREMENTAL_RELEASE_BUILD_VERSION_MISSING
require_marker "$build_script" \
- 'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.553}' \
+ 'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.554}' \
SERVER_INCREMENTAL_RELEASE_BUILD_DEFAULT_MISSING
require_marker "$build_script" \
'CATTLE_RANCHER_SERVER_VERSION="${server_release_tag}"' \
SERVER_INCREMENTAL_RELEASE_BUILD_RUNTIME_VERSION_MISSING
for release_engine_marker in \
- 'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.354' \
+ 'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.355' \
'ARG ORCHESTRATION_ENGINE_ARTIFACT=cattle.jar' \
- 'ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa' \
- 'ARG ORCHESTRATION_ENGINE_COMMIT=dc5a8d7bc44131f609f72a6504483b692d47ce16' \
+ 'ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f' \
+ 'ARG ORCHESTRATION_ENGINE_COMMIT=9ea138d1db1718cf65157c9564c379e62bafe9a0' \
'COPY --from=release_artifacts /out/orchestration-engine.jar /tmp/orchestration-engine.jar' \
- 'ARG ORCHESTRATION_ENGINE_VERSION=0.183.354' \
+ 'ARG ORCHESTRATION_ENGINE_VERSION=0.183.355' \
'sh /usr/share/cattle/verify-engine-archive.sh "${archive}" "${ORCHESTRATION_ENGINE_VERSION}" "${ORCHESTRATION_ENGINE_ARTIFACT_SHA256}"' \
'cattle-resources-${ORCHESTRATION_ENGINE_VERSION}.jar' \
'cattle-app-config-${ORCHESTRATION_ENGINE_VERSION}.jar' \
@@ -1171,10 +1172,10 @@ for release_engine_marker in \
SERVER_INCREMENTAL_ENGINE_REPLACEMENT_MISSING
done
for release_engine_build_marker in \
- 'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.354}' \
+ 'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.355}' \
'orchestration_engine_artifact=${ORCHESTRATION_ENGINE_ARTIFACT:-cattle.jar}' \
- 'orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa}' \
- 'orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-dc5a8d7bc44131f609f72a6504483b692d47ce16}' \
+ 'orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f}' \
+ 'orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-9ea138d1db1718cf65157c9564c379e62bafe9a0}' \
'CATTLE_CATTLE_VERSION="${orchestration_engine_release_tag}"' \
'cattle-resources-${ORCHESTRATION_ENGINE_VERSION}.jar' \
'< "${server_dir}/artifacts/verify-engine-archive.sh"'; do
@@ -1185,6 +1186,8 @@ for engine_archive_marker in \
'grep -Fx "Implementation-Version: $expected_version"' \
'WEB-INF/lib/freemarker-2.3.35.jar' \
'WEB-INF/lib/hazelcast-5.7.6.jar' \
+ 'WEB-INF/lib/guava-33.7.2-jre.jar' \
+ 'b530942257fb935f8b2cfaa5f8eb5bd59c555fd8e8d01b8ce98912e077ea606c' \
'825b5fc9526a10549bcb580f2d6c03bf1f5667625661722c0406ea6f796e83f2' \
'Rejected old Freemarker dependency' \
'Rejected old Hazelcast dependency' \
@@ -1691,10 +1694,10 @@ fi
if grep -Fxq '## Current source candidate' README.md; then
for candidate_readme_marker in \
'' \
- 'Server `v1.6.553` is a prepared source candidate, not a published image.' \
+ 'Server `v1.6.554` is a prepared source candidate, not a published image.' \
'run them only after its official release' \
'This candidate is not yet available' \
- 'ghcr.io/pasturestack/server:v1.6.553'; do
+ 'ghcr.io/pasturestack/server:v1.6.554'; do
require_marker README.md "$candidate_readme_marker" \
SERVER_CURRENT_CANDIDATE_README_MISSING
done
@@ -2300,7 +2303,7 @@ done
jq -e '
["CVE-2026-107928","CVE-2026-107929","CVE-2026-86409","CVE-2026-86410","CVE-2026-86412","CVE-2026-86414","CVE-2026-87862","CVE-2026-87863","CVE-2026-87864","CVE-2026-87865","CVE-2026-92386","CVE-2026-92387","CVE-2026-92388","CVE-2026-92389","CVE-2026-92390","CVE-2026-92391","CVE-2026-92392","CVE-2026-94266"] as $curl_fixed_cves
| .["@context"] == "https://openvex.dev/ns/v0.2.0"
- and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.553"
+ and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.554"
and (.statements | length) == 56
and ([.statements[].vulnerability.name] | length == (unique | length))
and ([.statements[] | select(.status == "fixed") | .vulnerability.name] | sort)
@@ -2338,7 +2341,7 @@ jq -r '
| @tsv
' "$runtime_vendor_pending" | LC_ALL=C sort -u >"$vendor_pending_fixture"
bash "$vendor_pending_validator" "$runtime_vendor_pending" \
- "$vendor_pending_fixture" v1.6.553 >/dev/null
+ "$vendor_pending_fixture" v1.6.554 >/dev/null
bash scripts/test-vendor-pending-findings.sh >/dev/null
rm -f "$vendor_pending_fixture"
trap - EXIT
@@ -2427,4 +2430,4 @@ for release_readback_contract in \
fi
done
-printf 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.553 base=v1.6.460 engine=0.183.354 web_console=1.6.195 catalog_service=0.20.15 webhook_automation_service=0.10.5 authentication_service=0.4.46 curl=8.18.0-1ubuntu2.8 freemarker=2.3.35 artifact_scan=required vendor_pending=exact-set role_matrix=qa-required locale_layout=qa-required\n'
+printf 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.554 base=v1.6.460 engine=0.183.355 web_console=1.6.196 catalog_service=0.20.15 webhook_automation_service=0.10.5 authentication_service=0.4.46 curl=8.18.0-1ubuntu2.8 freemarker=2.3.35 artifact_scan=required vendor_pending=exact-set role_matrix=qa-required locale_layout=qa-required\n'
diff --git a/scripts/test-current-server-packaging-contract.py b/scripts/test-current-server-packaging-contract.py
index 9832f94fde..a9bc727908 100644
--- a/scripts/test-current-server-packaging-contract.py
+++ b/scripts/test-current-server-packaging-contract.py
@@ -34,16 +34,16 @@
PUBLISHED_508 = 'Server `v1.6.508` 已正式發布,封裝 Web Console `1.6.171`。'
STALE_508_CANDIDATE = 'Server `v1.6.508` candidate packages Web Console `1.6.171`'
COMPATIBILITY_CODE = 'SERVER_CREATE_RESPONSE_ORDER_COMPATIBILITY_MISSING'
-WEB_SHA = '26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3'
-WEB_SOURCE = 'f905bc12daae18d98a6ac65942625562fff2a424'
-WEB_SBOM_SHA = 'ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a'
-WEB_LOCK_SHA = 'ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22'
+WEB_SHA = '4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa'
+WEB_SOURCE = '4e3e4d51db003ef7b456166084d425bfb06e1518'
+WEB_SBOM_SHA = '44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb'
+WEB_LOCK_SHA = '4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6'
CATALOG_COMMIT = '00eec89e5fdeb9728c1c41babccad8b7dfc8b297'
-ENGINE_SHA = 'f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa'
-ENGINE_SOURCE = 'dc5a8d7bc44131f609f72a6504483b692d47ce16'
+ENGINE_SHA = 'a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f'
+ENGINE_SOURCE = '9ea138d1db1718cf65157c9564c379e62bafe9a0'
OLD_COORDINATES = {
- 'v1.6.553': 'v1.6.521',
- '1.6.195': '1.6.181',
+ 'v1.6.554': 'v1.6.521',
+ '1.6.196': '1.6.181',
WEB_SHA: '9199681f2c4311f3fbe8727fcf0f6fa136c9bafe9fbe9d13878c55df442a7a03',
WEB_SOURCE: '7f2bdad0c90a3241693c2ddd4475202ababba550',
CATALOG_COMMIT: '7670ffd81d5f0b5570197fb03c7e55b46da45bf3',
@@ -60,29 +60,29 @@
('LICENSE_SHA256', 'license_sha256', '0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594'),
)
LEGACY_ENGINE_COORDINATES = {
- '0.183.354': '0.183.337',
+ '0.183.355': '0.183.337',
ENGINE_SHA: '7e45189f2b57097efc7929c04ddf7767aebbe027d1b9f90023aa6711522c5c80',
ENGINE_SOURCE: 'ba041fe312dfe97fc1e5144af576ddcf1b573d8e',
}
EXPECTED = {
COMPATIBILITY_CODE: (COMPATIBILITY, PUBLISHED_508),
- 'SERVER_INCREMENTAL_RELEASE_DEFAULT_MISSING': (DOCKER, 'ARG SERVER_RELEASE_TAG=v1.6.553'),
+ 'SERVER_INCREMENTAL_RELEASE_DEFAULT_MISSING': (DOCKER, 'ARG SERVER_RELEASE_TAG=v1.6.554'),
'SERVER_INCREMENTAL_RELEASE_VERSION_MISSING': (DOCKER, 'org.opencontainers.image.version="${SERVER_RELEASE_TAG}"'),
'SERVER_INCREMENTAL_RELEASE_RUNTIME_VERSION_MISSING': (DOCKER, 'ENV CATTLE_RANCHER_SERVER_VERSION=${SERVER_RELEASE_TAG}'),
- 'SERVER_INCREMENTAL_WEB_CONSOLE_VERSION_MISSING': (DOCKER, 'ARG WEB_CONSOLE_RELEASE_TAG=1.6.195'),
- 'SERVER_INCREMENTAL_WEB_CONSOLE_ARTIFACT_MISSING': (DOCKER, 'ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.195.tar.gz'),
+ 'SERVER_INCREMENTAL_WEB_CONSOLE_VERSION_MISSING': (DOCKER, 'ARG WEB_CONSOLE_RELEASE_TAG=1.6.196'),
+ 'SERVER_INCREMENTAL_WEB_CONSOLE_ARTIFACT_MISSING': (DOCKER, 'ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.196.tar.gz'),
'SERVER_INCREMENTAL_WEB_CONSOLE_HASH_MISSING': (DOCKER, 'ARG WEB_CONSOLE_ARTIFACT_SHA256=' + WEB_SHA),
'SERVER_INCREMENTAL_WEB_CONSOLE_COMMIT_MISSING': (DOCKER, 'ARG WEB_CONSOLE_COMMIT=' + WEB_SOURCE),
'SERVER_INCREMENTAL_WEB_CONSOLE_LOCK_HASH_MISSING': (DOCKER, 'ARG WEB_CONSOLE_LOCK_SHA256=' + WEB_LOCK_SHA),
'SERVER_INCREMENTAL_WEB_CONSOLE_SBOM_HASH_MISSING': (DOCKER, 'ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=' + WEB_SBOM_SHA),
'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_COMMIT_MISSING': (BUILD, 'web_console_commit=${WEB_CONSOLE_COMMIT:-' + WEB_SOURCE + '}'),
- 'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_VERSION_MISSING': (BUILD, 'web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.195}'),
- 'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_ARTIFACT_MISSING': (BUILD, 'web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.195.tar.gz}'),
+ 'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_VERSION_MISSING': (BUILD, 'web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.196}'),
+ 'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_ARTIFACT_MISSING': (BUILD, 'web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.196.tar.gz}'),
'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_HASH_MISSING': (BUILD, 'web_console_artifact_sha256=${WEB_CONSOLE_ARTIFACT_SHA256:-' + WEB_SHA + '}'),
'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_LOCK_HASH_MISSING': (BUILD, 'web_console_lock_sha256=${WEB_CONSOLE_LOCK_SHA256:-' + WEB_LOCK_SHA + '}'),
'SERVER_INCREMENTAL_WEB_CONSOLE_BUILD_SBOM_HASH_MISSING': (BUILD, 'web_console_runtime_sbom_sha256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256:-' + WEB_SBOM_SHA + '}'),
- 'SERVER_INCREMENTAL_RELEASE_BUILD_VERSION_MISSING': (BUILD, 'image=${IMAGE:-pasturestack-validation/server:v1.6.553}'),
- 'SERVER_INCREMENTAL_RELEASE_BUILD_DEFAULT_MISSING': (BUILD, 'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.553}'),
+ 'SERVER_INCREMENTAL_RELEASE_BUILD_VERSION_MISSING': (BUILD, 'image=${IMAGE:-pasturestack-validation/server:v1.6.554}'),
+ 'SERVER_INCREMENTAL_RELEASE_BUILD_DEFAULT_MISSING': (BUILD, 'server_release_tag=${SERVER_RELEASE_TAG:-v1.6.554}'),
'SERVER_INCREMENTAL_RELEASE_BUILD_RUNTIME_VERSION_MISSING': (BUILD, 'CATTLE_RANCHER_SERVER_VERSION="${server_release_tag}"'),
'SERVER_WEB_CONSOLE_RUNTIME_VERSION_GATE_MISSING': (BUILD, 'test "$(cat "${web_root}/VERSION.txt")" = "${WEB_CONSOLE_RELEASE_TAG}"'),
'SERVER_VOLUME_NATIVE_READ_ONLY_BUILD_GATE_MISSING': (DOCKER, 'grep -F \'"volume.isNative" : "r"\' >/dev/null'),
@@ -103,18 +103,18 @@
CREATE_RESPONSE_CODES = ['SERVER_WEB_CREATE_IDENTITY_BUILD_GATE_MISSING','SERVER_WEB_CANONICAL_RECORD_BUILD_GATE_MISSING','SERVER_WEB_CREATE_IDENTITY_RUNTIME_GATE_MISSING','SERVER_WEB_CANONICAL_RECORD_RUNTIME_GATE_MISSING']
ENGINE_EXPECTED = {
'SERVER_INCREMENTAL_ENGINE_REPLACEMENT_MISSING': (DOCKER, 'release_engine_marker', (
- 'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.354',
+ 'ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.355',
'ARG ORCHESTRATION_ENGINE_ARTIFACT=cattle.jar',
'ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=' + ENGINE_SHA,
'ARG ORCHESTRATION_ENGINE_COMMIT=' + ENGINE_SOURCE,
- 'ARG ORCHESTRATION_ENGINE_VERSION=0.183.354',
+ 'ARG ORCHESTRATION_ENGINE_VERSION=0.183.355',
'sh /usr/share/cattle/verify-engine-archive.sh "${archive}" "${ORCHESTRATION_ENGINE_VERSION}" "${ORCHESTRATION_ENGINE_ARTIFACT_SHA256}"',
'cattle-resources-${ORCHESTRATION_ENGINE_VERSION}.jar',
'cattle-app-config-${ORCHESTRATION_ENGINE_VERSION}.jar',
'ENV CATTLE_CATTLE_VERSION=${ORCHESTRATION_ENGINE_RELEASE_TAG}',
)),
'SERVER_INCREMENTAL_ENGINE_BUILD_COORDINATE_MISSING': (BUILD, 'release_engine_build_marker', (
- 'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.354}',
+ 'orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.355}',
'orchestration_engine_artifact=${ORCHESTRATION_ENGINE_ARTIFACT:-cattle.jar}',
'orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-' + ENGINE_SHA + '}',
'orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-' + ENGINE_SOURCE + '}',
@@ -126,6 +126,8 @@
'grep -Fx "Implementation-Version: $expected_version"',
'WEB-INF/lib/freemarker-2.3.35.jar',
'WEB-INF/lib/hazelcast-5.7.6.jar',
+ 'WEB-INF/lib/guava-33.7.2-jre.jar',
+ 'b530942257fb935f8b2cfaa5f8eb5bd59c555fd8e8d01b8ce98912e077ea606c',
'825b5fc9526a10549bcb580f2d6c03bf1f5667625661722c0406ea6f796e83f2',
'Rejected old Freemarker dependency',
'Rejected old Hazelcast dependency',
@@ -606,15 +608,15 @@ def verify(files, gate=GATE):
pipeline = r'(?:^|[;\n])\s*' + re.escape(marker) + r'(?=\s*(?:;|\n|$))'
if len(re.findall(pipeline, source)) != 1:
raise AssertionError('CURRENT_CATALOG_HASH_BYPASS')
- if 'and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.553"' not in gate:
+ if 'and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.554"' not in gate:
raise AssertionError('CURRENT_VEX_GATE_PIN_MISMATCH')
- if '"$vendor_pending_fixture" v1.6.553 >/dev/null' not in gate:
+ if '"$vendor_pending_fixture" v1.6.554 >/dev/null' not in gate:
raise AssertionError('CURRENT_VENDOR_GATE_PIN_MISMATCH')
- if json.loads(files[VEX]).get('@id') != 'https://github.com/PastureStack/server/security/openvex/v1.6.553':
+ if json.loads(files[VEX]).get('@id') != 'https://github.com/PastureStack/server/security/openvex/v1.6.554':
raise AssertionError('CURRENT_VEX_RELEASE_MISMATCH')
- if json.loads(files[VENDOR]).get('release') != 'v1.6.553':
+ if json.loads(files[VENDOR]).get('release') != 'v1.6.554':
raise AssertionError('CURRENT_VENDOR_RELEASE_MISMATCH')
- if 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.553 base=v1.6.460 engine=0.183.354 web_console=1.6.195 catalog_service=0.20.15 ' not in gate:
+ if 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.554 base=v1.6.460 engine=0.183.355 web_console=1.6.196 catalog_service=0.20.15 ' not in gate:
raise AssertionError('CURRENT_SUMMARY_MISMATCH')
verify_incremental_contract(files, gate)
verify_load_balancer_contract(files, gate)
@@ -629,11 +631,11 @@ def previous_gate(gate=GATE):
if stale(marker) != marker:
replacements["'" + marker + "' "] = "'" + stale(marker) + "' "
replacements.update({
- 'and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.553"':
+ 'and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.554"':
'and .["@id"] == "https://github.com/PastureStack/server/security/openvex/v1.6.521"',
- '"$vendor_pending_fixture" v1.6.553 >/dev/null':
+ '"$vendor_pending_fixture" v1.6.554 >/dev/null':
'"$vendor_pending_fixture" v1.6.521 >/dev/null',
- 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.553 base=v1.6.460 engine=0.183.354 web_console=1.6.195 catalog_service=0.20.15 ':
+ 'SERVER_API_EXPLORER_PATCH_OK release=v1.6.554 base=v1.6.460 engine=0.183.355 web_console=1.6.196 catalog_service=0.20.15 ':
'SERVER_API_EXPLORER_PATCH_OK release=v1.6.521 base=v1.6.460 engine=0.183.340 web_console=1.6.181 catalog_service=0.20.12 ',
})
for marker, stale_marker in replacements.items():
@@ -1178,7 +1180,7 @@ def test_candidate_coordinates_and_pending_rejection_use_actual_pin_predicates(s
self.assertLess(FILES[DOCKER].index(artifact_guard), FILES[DOCKER].index('fetch-server-component "${COMPONENT_ARTIFACT_MODE}"'))
self.assertIn('"${WEB_CONSOLE_ARTIFACT}" "${WEB_CONSOLE_ARTIFACT_SHA256}" "${web_archive}";', FILES[DOCKER])
- def test_web195_archive_sbom_lock_and_source_are_bound_in_both_stages(self):
+ def test_web196_archive_sbom_lock_source_and_profile_are_bound_in_both_stages(self):
fields = {
'COMMIT': WEB_SOURCE, 'ARTIFACT_SHA256': WEB_SHA,
'RUNTIME_SBOM_SHA256': WEB_SBOM_SHA, 'LOCK_SHA256': WEB_LOCK_SHA,
@@ -1191,7 +1193,8 @@ def test_web195_archive_sbom_lock_and_source_are_bound_in_both_stages(self):
self.assertIn(variable + '=${WEB_CONSOLE_' + suffix + ':-' + value + '}', FILES[BUILD])
self.assertIn('--build-arg "WEB_CONSOLE_' + suffix + '=${' + variable + '}"', FILES[BUILD])
for marker in (
- '--lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}";',
+ '--lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}"',
+ '--webmcp-ceiling enabled;',
'install -m 0644 "${web_sbom}" /out/runtime-licenses/web-console/runtime.cdx.json;',
'echo "${WEB_CONSOLE_RUNTIME_SBOM_SHA256} /usr/share/licenses/pasturestack/web-console/runtime.cdx.json" | sha256sum -c -;',
):
@@ -1296,7 +1299,7 @@ def test_each_old_engine_coordinate_rejected(self):
self.assertGreater(checked, 0)
def test_historical_engine340_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.340',
+ old = {'0.183.355': '0.183.340',
ENGINE_SHA: '5e5a1b908b43bf3824fc3bb2108bd661cd5199ce9bcfd187e0b18b5608a38cea',
ENGINE_SOURCE: 'ff0a41e7676211043eb4f755a306a9e2a19abee1'}
checked = 0
@@ -1317,13 +1320,13 @@ def test_historical_engine340_cannot_be_packaged(self):
def test_engine352_actual_war_producer_and_previous351_350_boundary(self):
candidate = (REPO / 'server/build-component-candidate.sh').read_text(encoding='utf-8')
- self.assertIn('export SERVER_RELEASE_TAG=${SERVER_RELEASE_TAG:-v1.6.553}', candidate)
- self.assertIn('export ORCHESTRATION_ENGINE_RELEASE_TAG=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.354}', candidate)
+ self.assertIn('export SERVER_RELEASE_TAG=${SERVER_RELEASE_TAG:-v1.6.554}', candidate)
+ self.assertIn('export ORCHESTRATION_ENGINE_RELEASE_TAG=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.355}', candidate)
for old in (
- {'0.183.354': '0.183.351',
+ {'0.183.355': '0.183.351',
ENGINE_SHA: 'efd0e826364c551f782b281f081d53cadb48faa7d5c37c2f101e6f9c7d7f9ea6',
ENGINE_SOURCE: '1befd83278618f9a5b7cdaf96522004dd89918ba'},
- {'0.183.354': '0.183.350',
+ {'0.183.355': '0.183.350',
ENGINE_SHA: '637ddaa42cd93c019b4d0813d7053b71dc42797a7f3ac2aa4b10fcc7e304cb31',
ENGINE_SOURCE: '9c884710e5ea3d4136d80a870d9de9485674f3c3'},
{ENGINE_SOURCE: '109f2623f00ca3a3a9079414a0f0f8bd23cd1eb2'},
@@ -1346,7 +1349,7 @@ def test_engine352_actual_war_producer_and_previous351_350_boundary(self):
def test_immediately_previous_engine341_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.341',
+ old = {'0.183.355': '0.183.341',
ENGINE_SHA: '3ede1ad28b7a7e0754c4c953bd486162f7ffe0b2f76ce384ec485632af6b8e6d',
ENGINE_SOURCE: 'b245cf778479445ded0786d3898b03fea153212e'}
checked = 0
@@ -1367,7 +1370,7 @@ def test_immediately_previous_engine341_cannot_be_packaged(self):
def test_immediately_previous_engine342_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.342',
+ old = {'0.183.355': '0.183.342',
ENGINE_SHA: '3533f247392795329c92516a7966510aafbe81a52e3f922e5ec657a2071363b5',
ENGINE_SOURCE: 'ddae4bb8e12806f70816f5782cd999eccd0f2ce6'}
checked = 0
@@ -1388,7 +1391,7 @@ def test_immediately_previous_engine342_cannot_be_packaged(self):
def test_immediately_previous_engine343_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.343',
+ old = {'0.183.355': '0.183.343',
ENGINE_SHA: 'b29c0057f96dbae38bfe2c663c63bbe4c8b5c755b3090f39414aa51f9813025f',
ENGINE_SOURCE: '5aa9d8978c75a7fe63330453c399a92716d88568'}
checked = 0
@@ -1409,7 +1412,7 @@ def test_immediately_previous_engine343_cannot_be_packaged(self):
def test_immediately_previous_engine344_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.344',
+ old = {'0.183.355': '0.183.344',
ENGINE_SHA: 'ffafa4700156b74ec11bcae1b830738e49943f1a3b8b0565895ed6d7726eebf1',
ENGINE_SOURCE: '72c42e6f2000301fe03ffd2442fb9ee89aee120c'}
checked = 0
@@ -1429,7 +1432,7 @@ def test_immediately_previous_engine344_cannot_be_packaged(self):
self.assertGreater(checked, 0)
def test_immediately_previous_engine345_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.345',
+ old = {'0.183.355': '0.183.345',
ENGINE_SHA: '780263c4a884527fa80a28b998ad4bda88b9ad6002a687f9f074602ecad46e62',
ENGINE_SOURCE: 'd27d4ac73fa05ae43b9ae0c32875780e8a3765b1'}
checked = 0
@@ -1449,7 +1452,7 @@ def test_immediately_previous_engine345_cannot_be_packaged(self):
self.assertGreater(checked, 0)
def test_immediately_previous_engine346_cannot_be_packaged(self):
- old = {'0.183.354': '0.183.346',
+ old = {'0.183.355': '0.183.346',
ENGINE_SHA: '9deeda5758b7f04135441a203092dd0308d90b65d25037b62ef4f9361d3cf806',
ENGINE_SOURCE: 'e250e4a17958216c30c1f5d0d31f7b6418a968d5'}
checked = 0
@@ -1625,7 +1628,7 @@ def test_machine_actual_binary_package_and_retired_boundary_are_verified(self):
def test_current_readme_status_specific_prose_does_not_block_publication_transition(self):
common = GATE.split('for current_readme_marker in ', 1)[1].split('; do', 1)[0]
self.assertNotIn('candidate', common)
- self.assertNotIn('v1.6.553', common)
+ self.assertNotIn('v1.6.554', common)
self.assertIn("grep -Fqi 'keep all three named volumes' README.md", GATE)
status = GATE.split("if grep -Fxq '## Current source candidate' README.md; then", 1)[1]
candidate, published = status.split("elif grep -Fxq '## Current release' README.md; then", 1)
@@ -1634,7 +1637,7 @@ def test_current_readme_status_specific_prose_does_not_block_publication_transit
self.assertIn('This candidate is not yet available', candidate)
self.assertIn('SERVER_CURRENT_PUBLISHED_README_MISSING', published)
self.assertNotIn('candidate', published)
- self.assertNotIn('v1.6.553', published)
+ self.assertNotIn('v1.6.554', published)
if __name__ == '__main__':
diff --git a/scripts/test-engine-archive-contract.py b/scripts/test-engine-archive-contract.py
index 1e103bb60b..6131af847a 100644
--- a/scripts/test-engine-archive-contract.py
+++ b/scripts/test-engine-archive-contract.py
@@ -14,7 +14,10 @@
HAZELCAST_SHA = '825b5fc9526a10549bcb580f2d6c03bf1f5667625661722c0406ea6f796e83f2'
HAZELCAST_ENTRY = 'WEB-INF/lib/hazelcast-5.7.6.jar'
FREEMARKER_ENTRY = 'WEB-INF/lib/freemarker-2.3.35.jar'
+GUAVA_ENTRY = 'WEB-INF/lib/guava-33.7.2-jre.jar'
+GUAVA_SHA = 'b530942257fb935f8b2cfaa5f8eb5bd59c555fd8e8d01b8ce98912e077ea606c'
PAYLOAD = b'small nested Hazelcast fixture'
+GUAVA_PAYLOAD = b'small nested Guava fixture'
class EngineArchiveContract(unittest.TestCase):
@@ -28,13 +31,16 @@ def setUp(self):
# guard executes unchanged; the unmodified helper is also tested
# separately against the exact published 88 MB Engine archive.
self.helper = self.directory / 'verify.sh'
- self.helper.write_text(source.replace(HAZELCAST_SHA, hashlib.sha256(PAYLOAD).hexdigest()))
+ self.helper.write_text(source.replace(HAZELCAST_SHA, hashlib.sha256(PAYLOAD).hexdigest())
+ .replace(GUAVA_SHA, hashlib.sha256(GUAVA_PAYLOAD).hexdigest()))
- def run_archive(self, extra=(), omitted=(), manifest='0.183.354', payload=PAYLOAD, wrong_hash=False):
+ def run_archive(self, extra=(), omitted=(), manifest='0.183.355', payload=PAYLOAD,
+ guava_payload=GUAVA_PAYLOAD, wrong_hash=False):
entries = [
('META-INF/MANIFEST.MF', ('Manifest-Version: 1.0\r\nImplementation-Version: ' + manifest + '\r\n').encode()),
(FREEMARKER_ENTRY, b'Freemarker fixture'),
(HAZELCAST_ENTRY, payload),
+ (GUAVA_ENTRY, guava_payload),
]
archive = self.directory / 'engine.jar'
with warnings.catch_warnings():
@@ -46,7 +52,7 @@ def run_archive(self, extra=(), omitted=(), manifest='0.183.354', payload=PAYLOA
# Recompute each mutated archive's outer hash, so rejection cannot
# accidentally be attributed to an unrelated whole-file mismatch.
digest = '0' * 64 if wrong_hash else hashlib.sha256(archive.read_bytes()).hexdigest()
- return subprocess.run(['sh', str(self.helper), str(archive), '0.183.354', digest],
+ return subprocess.run(['sh', str(self.helper), str(archive), '0.183.355', digest],
capture_output=True, text=True, timeout=15)
def reject(self, marker, **kwargs):
@@ -93,6 +99,16 @@ def test_unsafe_paths(self):
def test_nested_hazelcast_hash(self):
self.reject('FAILED', payload=b'altered nested bytes')
+ def test_old_extra_or_missing_guava(self):
+ for version in ('33.7.1-jre', '33.7.2-android'):
+ with self.subTest(version=version):
+ self.reject('Guava archive contract mismatch',
+ extra=[('WEB-INF/lib/guava-' + version + '.jar', b'old')])
+ self.reject('Guava archive contract mismatch', omitted=(GUAVA_ENTRY,))
+
+ def test_nested_guava_hash(self):
+ self.reject('FAILED', guava_payload=b'altered nested Guava bytes')
+
def test_shared_wiring_and_no_extra_layer_or_stale_variable(self):
docker = (ROOT / 'server/Dockerfile.web-compose-release').read_text()
build = (ROOT / 'server/build-api-explorer-patch-image.sh').read_text()
diff --git a/scripts/test-web-console-sbom.py b/scripts/test-web-console-sbom.py
index ea320e412d..6c2d9c532f 100644
--- a/scripts/test-web-console-sbom.py
+++ b/scripts/test-web-console-sbom.py
@@ -3,10 +3,12 @@
import copy
import hashlib
import importlib.util
+import io
import json
from pathlib import Path
import subprocess
import sys
+import tarfile
import tempfile
import unittest
@@ -152,6 +154,51 @@ def test_cli_success_and_failure_status(self):
self.assertEqual(result.returncode, 1)
self.assertIn('WEB_CONSOLE_SBOM_REJECTED', result.stderr)
+ def profile_archive(self, payload, *, duplicate=False, linked=False, missing=False):
+ with tarfile.open(self.archive, 'w:gz') as package:
+ info = tarfile.TarInfo(f'{self.version}/' + ('other.json' if missing else 'webmcp-build.json'))
+ info.size = len(payload)
+ if linked:
+ info.type = tarfile.SYMTYPE
+ info.linkname = 'outside'
+ info.size = 0
+ package.addfile(info, None if linked else io.BytesIO(payload))
+ if duplicate:
+ package.addfile(info, io.BytesIO(payload))
+ self.archive_hash = hashlib.sha256(self.archive.read_bytes()).hexdigest()
+ self.data['metadata']['component']['hashes'][0]['content'] = self.archive_hash
+ for item in self.data['metadata']['properties']:
+ if item['name'] == 'pasturestack:archive-sha256':
+ item['value'] = self.archive_hash
+ self.write()
+
+ def test_enabled_profile_keeps_new_pages_off(self):
+ self.profile_archive(b'{"ceiling":"enabled","initialMode":"off"}')
+ self.verify(webmcp_ceiling='enabled')
+
+ def test_wrong_ceiling_or_initial_mode_cannot_ship(self):
+ for profile in ({'ceiling': 'off', 'initialMode': 'off'},
+ {'ceiling': 'enabled', 'initialMode': 'enabled'}):
+ with self.subTest(profile=profile):
+ self.profile_archive(json.dumps(profile).encode())
+ with self.assertRaisesRegex(ValueError, 'PROFILE_MISMATCH'):
+ self.verify(webmcp_ceiling='enabled')
+
+ def test_duplicate_linked_or_oversized_profile_is_rejected(self):
+ for options in ({'duplicate': True}, {'linked': True}, {'missing': True}, {}):
+ with self.subTest(options=options):
+ payload = b' ' * 257 if not options else b'{}'
+ self.profile_archive(payload, **options)
+ with self.assertRaisesRegex(ValueError, 'PROFILE_MISSING_OR_INVALID'):
+ self.verify(webmcp_ceiling='enabled')
+
+ def test_ambiguous_profile_and_absent_archive_are_rejected(self):
+ self.profile_archive(b'{"ceiling":"enabled","ceiling":"off","initialMode":"off"}')
+ with self.assertRaisesRegex(ValueError, 'DUPLICATE_JSON_KEY'):
+ self.verify(webmcp_ceiling='enabled')
+ with self.assertRaisesRegex(ValueError, 'PROFILE_INPUT_INVALID'):
+ self.verify(webmcp_ceiling='enabled', archive=None)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/server/Dockerfile.web-compose-release b/server/Dockerfile.web-compose-release
index 2d3103fe85..c89ba24a0b 100644
--- a/server/Dockerfile.web-compose-release
+++ b/server/Dockerfile.web-compose-release
@@ -90,13 +90,13 @@ ARG PER_HOST_SUBNET_STARTUP_SHA256=f22b41f18d1733c037babed4d2e0474efeedc6017a968
ARG PER_HOST_SUBNET_PROVENANCE_SHA256=afa2db0e0755c6bd3ee28e111937c521a6d84e6d71d563e99202cf4693000eaf
ARG WEB_CONSOLE_RELEASE_BASE_URL=https://github.com/PastureStack/web-console/releases/download
-ARG WEB_CONSOLE_RELEASE_TAG=1.6.195
-ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.195.tar.gz
-ARG WEB_CONSOLE_ARTIFACT_SHA256=26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3
-ARG WEB_CONSOLE_COMMIT=f905bc12daae18d98a6ac65942625562fff2a424
-ARG WEB_CONSOLE_LOCK_SHA256=ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22
-ARG WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT=web-console-1.6.195-runtime.cdx.json
-ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a
+ARG WEB_CONSOLE_RELEASE_TAG=1.6.196
+ARG WEB_CONSOLE_ARTIFACT=web-console-1.6.196.tar.gz
+ARG WEB_CONSOLE_ARTIFACT_SHA256=4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa
+ARG WEB_CONSOLE_COMMIT=4e3e4d51db003ef7b456166084d425bfb06e1518
+ARG WEB_CONSOLE_LOCK_SHA256=4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6
+ARG WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT=web-console-1.6.196-runtime.cdx.json
+ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb
ARG CATALOG_SERVICE_RELEASE_BASE_URL=https://github.com/PastureStack/catalog-service/releases/download
ARG CATALOG_SERVICE_VERSION=0.20.15
ARG CATALOG_SERVICE_COMMIT=6ea547594065f7f95d5877b82f0c1bc553e72640
@@ -118,9 +118,9 @@ ARG WEBHOOK_AUTOMATION_SERVICE_VERSION=0.10.5
ARG WEBHOOK_AUTOMATION_SERVICE_ARCHIVE_SHA256=f2df8874d5a00a2d5713594609063256164ed624d29a733149d00cb271f0f66b
ARG WEBHOOK_AUTOMATION_SERVICE_BINARY_SHA256=b98e55db2739ab45119eacc94c8f65cb44371480ebcebec95e5470fd67c72a4c
ARG ORCHESTRATION_ENGINE_RELEASE_BASE_URL=https://github.com/PastureStack/orchestration-engine/releases/download
-ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.354
+ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.355
ARG ORCHESTRATION_ENGINE_ARTIFACT=cattle.jar
-ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa
+ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f
ARG COMPOSE_EXECUTOR_RELEASE_BASE_URL=https://github.com/PastureStack/compose-cli/releases/download
ARG COMPOSE_EXECUTOR_VERSION=0.14.39
ARG COMPOSE_EXECUTOR_ARCHIVE_SHA256=4ab4685af67a8b8b11f3dd4996de9e087c26391ec401cf02cdcafe4c93094bab
@@ -182,7 +182,8 @@ RUN set -eux; \
--sbom "${web_sbom}" --sbom-sha256 "${WEB_CONSOLE_RUNTIME_SBOM_SHA256}" \
--version "${WEB_CONSOLE_RELEASE_TAG}" --source-commit "${WEB_CONSOLE_COMMIT}" \
--archive-name "${WEB_CONSOLE_ARTIFACT}" --archive-sha256 "${WEB_CONSOLE_ARTIFACT_SHA256}" \
- --lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}"; \
+ --lock-sha256 "${WEB_CONSOLE_LOCK_SHA256}" --archive "${web_archive}" \
+ --webmcp-ceiling enabled; \
mkdir -p /out/runtime-licenses/web-console; \
install -m 0644 "${web_sbom}" /out/runtime-licenses/web-console/runtime.cdx.json; \
tar -tzf "${web_archive}" >"${web_listing}"; \
@@ -695,12 +696,12 @@ ARG PER_HOST_SUBNET_VERSION=0.2.5
ARG PER_HOST_SUBNET_PRODUCER_COMMIT=e102e0bb38d84b0a9cbf87e5458cf1bc74d01904
ARG PER_HOST_SUBNET_ARCHIVE_SHA256=7237e83e96a64e7f3de64eb86bff9721c4e879c35cda9e9de1e4cb1ba225f298
ARG PER_HOST_SUBNET_PROVENANCE_SHA256=afa2db0e0755c6bd3ee28e111937c521a6d84e6d71d563e99202cf4693000eaf
-ARG SERVER_RELEASE_TAG=v1.6.553
+ARG SERVER_RELEASE_TAG=v1.6.554
ARG API_EXPLORER_RELEASE_TAG=v1.1.20
ARG API_EXPLORER_ARTIFACT_SHA256=c989ee12a518f6d9d2b23b1f17f48d781aacf6ec31fb65291dd7de9b1661badb
ARG API_EXPLORER_COMMIT=a586f0dc0aa50df579049a8d4bf26f04400fdf75
-ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.354
-ARG ORCHESTRATION_ENGINE_VERSION=0.183.354
+ARG ORCHESTRATION_ENGINE_RELEASE_TAG=v0.183.355
+ARG ORCHESTRATION_ENGINE_VERSION=0.183.355
ARG ENGINE_READONLY_SCHEMA_SHA256=7f274219e8dd9c6d750a408a6edec1448b564f4b16ba2204c425c5b45cec2233
ARG ENGINE_RESTRICTED_SCHEMA_SHA256=f854ba99260f29e324ab7446a8592996946038920ac9bcf17689d98d1ebc5e51
ARG HOST_API_VERSION=0.38.8
@@ -721,11 +722,11 @@ ARG GLIBC_PACKAGE_VERSION=2.43-2ubuntu2.4
ARG GLIBC_RUNTIME_PACKAGE_VERSION=2.43-2ubuntu2.4+pasturestack.1
ARG PERL_PACKAGE_VERSION=5.40.1-7ubuntu0.3
ARG PCRE2_PACKAGE_VERSION=10.46-1build1+pasturestack.1
-ARG WEB_CONSOLE_RELEASE_TAG=1.6.195
-ARG WEB_CONSOLE_ARTIFACT_SHA256=26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3
-ARG WEB_CONSOLE_COMMIT=f905bc12daae18d98a6ac65942625562fff2a424
-ARG WEB_CONSOLE_LOCK_SHA256=ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22
-ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a
+ARG WEB_CONSOLE_RELEASE_TAG=1.6.196
+ARG WEB_CONSOLE_ARTIFACT_SHA256=4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa
+ARG WEB_CONSOLE_COMMIT=4e3e4d51db003ef7b456166084d425bfb06e1518
+ARG WEB_CONSOLE_LOCK_SHA256=4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6
+ARG WEB_CONSOLE_RUNTIME_SBOM_SHA256=44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb
ARG CATALOG_SERVICE_VERSION=0.20.15
ARG CATALOG_SERVICE_COMMIT=6ea547594065f7f95d5877b82f0c1bc553e72640
ARG CATALOG_SERVICE_ARCHIVE_SHA256=7c059101265d897e4b176f503a9217b112946d37d91daa0c40f421fb16e4bb6b
@@ -744,8 +745,8 @@ ARG WEBHOOK_AUTOMATION_SERVICE_VERSION=0.10.5
ARG WEBHOOK_AUTOMATION_SERVICE_COMMIT=4da77ccac464bc7f7cd2020e0659a666a5d1ff7e
ARG WEBHOOK_AUTOMATION_SERVICE_ARCHIVE_SHA256=f2df8874d5a00a2d5713594609063256164ed624d29a733149d00cb271f0f66b
ARG WEBHOOK_AUTOMATION_SERVICE_BINARY_SHA256=b98e55db2739ab45119eacc94c8f65cb44371480ebcebec95e5470fd67c72a4c
-ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa
-ARG ORCHESTRATION_ENGINE_COMMIT=dc5a8d7bc44131f609f72a6504483b692d47ce16
+ARG ORCHESTRATION_ENGINE_ARTIFACT_SHA256=a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f
+ARG ORCHESTRATION_ENGINE_COMMIT=9ea138d1db1718cf65157c9564c379e62bafe9a0
ARG HOST_PROVISIONER_VERSION=0.39.10
ARG HOST_PROVISIONER_COMMIT=d5f6d5c9f4f0599f4bf1bdc9d5d0cca2e2e0c1ba
ARG HOST_PROVISIONER_ARCHIVE_SHA256=fd8a814c5810746091ba3192a9fd31699e6865a2598654b9bbb55eae90bef661
diff --git a/server/artifacts/verify-engine-archive.sh b/server/artifacts/verify-engine-archive.sh
index ed6242410b..563c0bf9f7 100644
--- a/server/artifacts/verify-engine-archive.sh
+++ b/server/artifacts/verify-engine-archive.sh
@@ -44,4 +44,11 @@ if test "$hazelcast_entries" != 'WEB-INF/lib/hazelcast-5.7.6.jar'; then
fi
unzip -p "$archive" WEB-INF/lib/hazelcast-5.7.6.jar > "$work/hazelcast.jar"
printf '%s %s\n' '825b5fc9526a10549bcb580f2d6c03bf1f5667625661722c0406ea6f796e83f2' "$work/hazelcast.jar" | sha256sum -c -
-printf 'ENGINE_ARCHIVE_CONTRACT_OK version=%s freemarker=2.3.35 hazelcast=5.7.6 sha256=%s\n' "$expected_version" "$expected_sha256"
+guava_entries=$(grep -E '^WEB-INF/lib/guava-[^/]+\.jar$' "$work/listing" || true)
+if test "$guava_entries" != 'WEB-INF/lib/guava-33.7.2-jre.jar'; then
+ echo 'Guava archive contract mismatch: expected exactly 33.7.2-jre' >&2
+ exit 1
+fi
+unzip -p "$archive" WEB-INF/lib/guava-33.7.2-jre.jar > "$work/guava.jar"
+printf '%s %s\n' 'b530942257fb935f8b2cfaa5f8eb5bd59c555fd8e8d01b8ce98912e077ea606c' "$work/guava.jar" | sha256sum -c -
+printf 'ENGINE_ARCHIVE_CONTRACT_OK version=%s freemarker=2.3.35 hazelcast=5.7.6 guava=33.7.2-jre sha256=%s\n' "$expected_version" "$expected_sha256"
diff --git a/server/artifacts/verify-web-console-sbom.py b/server/artifacts/verify-web-console-sbom.py
index 0835b28a65..c4641f723e 100644
--- a/server/artifacts/verify-web-console-sbom.py
+++ b/server/artifacts/verify-web-console-sbom.py
@@ -3,10 +3,12 @@
import argparse
import hashlib
+import io
import json
from pathlib import Path
import re
import sys
+import tarfile
def unique_object(pairs):
@@ -19,7 +21,7 @@ def unique_object(pairs):
def verify(sbom, *, sbom_sha256, version, source_commit, archive_name,
- archive_sha256, lock_sha256, archive=None):
+ archive_sha256, lock_sha256, archive=None, webmcp_ceiling=None):
for value in (sbom_sha256, archive_sha256, lock_sha256):
if not re.fullmatch(r'[0-9a-f]{64}', value):
raise ValueError('WEB_CONSOLE_SBOM_INVALID_SHA256_PIN')
@@ -39,8 +41,24 @@ def verify(sbom, *, sbom_sha256, version, source_commit, archive_name,
archive_path = Path(archive)
if archive_path.is_symlink() or not archive_path.is_file():
raise ValueError('WEB_CONSOLE_SBOM_ARCHIVE_MISSING_OR_LINKED')
- if hashlib.sha256(archive_path.read_bytes()).hexdigest() != archive_sha256:
+ archive_bytes = archive_path.read_bytes()
+ if hashlib.sha256(archive_bytes).hexdigest() != archive_sha256:
raise ValueError('WEB_CONSOLE_SBOM_ARCHIVE_SHA256_MISMATCH')
+ if webmcp_ceiling is not None:
+ if archive is None or webmcp_ceiling not in ('off', 'read-only', 'enabled'):
+ raise ValueError('WEB_CONSOLE_WEBMCP_PROFILE_INPUT_INVALID')
+ # Parse the exact bytes whose checksum was verified, without reopening
+ # an operator-selected path after verification.
+ with tarfile.open(fileobj=io.BytesIO(archive_bytes), mode='r:gz') as package:
+ profiles = [entry for entry in package.getmembers()
+ if entry.name.removeprefix('./') == f'{version}/webmcp-build.json']
+ if len(profiles) != 1 or not profiles[0].isfile() or profiles[0].size > 256:
+ raise ValueError('WEB_CONSOLE_WEBMCP_PROFILE_MISSING_OR_INVALID')
+ with package.extractfile(profiles[0]) as stream:
+ profile = json.loads(stream.read(257).decode('utf-8'),
+ object_pairs_hook=unique_object)
+ if profile != {'ceiling': webmcp_ceiling, 'initialMode': 'off'}:
+ raise ValueError('WEB_CONSOLE_WEBMCP_PROFILE_MISMATCH')
data = json.loads(payload.decode('utf-8'), object_pairs_hook=unique_object)
if (data.get('bomFormat'), data.get('specVersion'), data.get('version')) != (
'CycloneDX', '1.6', 1):
@@ -102,10 +120,11 @@ def main():
parser.add_argument('--archive-sha256', required=True)
parser.add_argument('--lock-sha256', required=True)
parser.add_argument('--archive')
+ parser.add_argument('--webmcp-ceiling', choices=('off', 'read-only', 'enabled'))
args = vars(parser.parse_args())
try:
result = verify(**args)
- except (ValueError, OSError, KeyError, TypeError, AttributeError) as error:
+ except (ValueError, OSError, KeyError, TypeError, AttributeError, tarfile.TarError) as error:
print(f'WEB_CONSOLE_SBOM_REJECTED {error}', file=sys.stderr)
return 1
print('WEB_CONSOLE_RUNTIME_SBOM_OK ' + ' '.join(
diff --git a/server/build-api-explorer-patch-image.sh b/server/build-api-explorer-patch-image.sh
index 55a1b2bf73..96bc5b9ce2 100755
--- a/server/build-api-explorer-patch-image.sh
+++ b/server/build-api-explorer-patch-image.sh
@@ -14,14 +14,14 @@ if [[ -n "$(git status --porcelain --untracked-files=normal)" ]]; then
fi
revision=${PASTURESTACK_SERVER_REVISION:-$(git rev-parse HEAD)}
-server_release_tag=${SERVER_RELEASE_TAG:-v1.6.553}
+server_release_tag=${SERVER_RELEASE_TAG:-v1.6.554}
source_date_epoch=${SOURCE_DATE_EPOCH:-$(git show -s --format=%ct HEAD)}
base_image=${BASE_IMAGE:-ghcr.io/pasturestack/server:v1.6.460@sha256:c855af8aea232dacc5bb6df68e2271d482c68b53c43ab0c108ec19118f5ab403}
orchestration_engine_release_base_url=${ORCHESTRATION_ENGINE_RELEASE_BASE_URL:-https://github.com/PastureStack/orchestration-engine/releases/download}
-orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.354}
+orchestration_engine_release_tag=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.355}
orchestration_engine_artifact=${ORCHESTRATION_ENGINE_ARTIFACT:-cattle.jar}
-orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-f4889bfb8759a7664f07524bb129d0cda19f35604a06c877b6ca7033246df6fa}
-orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-dc5a8d7bc44131f609f72a6504483b692d47ce16}
+orchestration_engine_artifact_sha256=${ORCHESTRATION_ENGINE_ARTIFACT_SHA256:-a7f651f84a9fe6deef2c9f66f50c13c70e450e25f9dfa6fb59129b2681f9f54f}
+orchestration_engine_commit=${ORCHESTRATION_ENGINE_COMMIT:-9ea138d1db1718cf65157c9564c379e62bafe9a0}
orchestration_engine_version=${orchestration_engine_release_tag#v}
engine_readonly_schema_sha256=${ENGINE_READONLY_SCHEMA_SHA256:-7f274219e8dd9c6d750a408a6edec1448b564f4b16ba2204c425c5b45cec2233}
engine_restricted_schema_sha256=${ENGINE_RESTRICTED_SCHEMA_SHA256:-f854ba99260f29e324ab7446a8592996946038920ac9bcf17689d98d1ebc5e51}
@@ -63,13 +63,13 @@ api_explorer_artifact=${API_EXPLORER_ARTIFACT:-api-explorer-1.1.20.tar.gz}
api_explorer_artifact_sha256=${API_EXPLORER_ARTIFACT_SHA256:-c989ee12a518f6d9d2b23b1f17f48d781aacf6ec31fb65291dd7de9b1661badb}
api_explorer_commit=${API_EXPLORER_COMMIT:-a586f0dc0aa50df579049a8d4bf26f04400fdf75}
web_console_release_base_url=${WEB_CONSOLE_RELEASE_BASE_URL:-https://github.com/PastureStack/web-console/releases/download}
-web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.195}
-web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.195.tar.gz}
-web_console_artifact_sha256=${WEB_CONSOLE_ARTIFACT_SHA256:-26cbe1f3c057722c6682c43316021687508b7d9dd58c28c32713512d75238bb3}
-web_console_commit=${WEB_CONSOLE_COMMIT:-f905bc12daae18d98a6ac65942625562fff2a424}
-web_console_lock_sha256=${WEB_CONSOLE_LOCK_SHA256:-ae862be4519751895f74e552f036906d64be8371fdbbc6379c16e52518f9ee22}
+web_console_release_tag=${WEB_CONSOLE_RELEASE_TAG:-1.6.196}
+web_console_artifact=${WEB_CONSOLE_ARTIFACT:-web-console-1.6.196.tar.gz}
+web_console_artifact_sha256=${WEB_CONSOLE_ARTIFACT_SHA256:-4f0a550c2754e244cbf77f380c5c138150687988791e063bbf39ce0150d880fa}
+web_console_commit=${WEB_CONSOLE_COMMIT:-4e3e4d51db003ef7b456166084d425bfb06e1518}
+web_console_lock_sha256=${WEB_CONSOLE_LOCK_SHA256:-4b9fb2e87352ec76c242b06af3974556b4d7c9f430adb0baccea3f624d3605e6}
web_console_runtime_sbom_artifact=${WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT:-web-console-${web_console_release_tag}-runtime.cdx.json}
-web_console_runtime_sbom_sha256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256:-ca31669f58aa342cd346e1d33ec7061d65f9b84d6a3afcbd132e09361425a88a}
+web_console_runtime_sbom_sha256=${WEB_CONSOLE_RUNTIME_SBOM_SHA256:-44b6c1940e4fd364e7a634ac2a6168bc2a0ac3d84b07b9adbc4f6fa2d7e743eb}
catalog_service_release_base_url=${CATALOG_SERVICE_RELEASE_BASE_URL:-https://github.com/PastureStack/catalog-service/releases/download}
catalog_service_version=${CATALOG_SERVICE_VERSION:-0.20.15}
catalog_service_commit=${CATALOG_SERVICE_COMMIT:-6ea547594065f7f95d5877b82f0c1bc553e72640}
@@ -130,7 +130,7 @@ usage_telemetry_agent_archive_sha256=${USAGE_TELEMETRY_AGENT_ARCHIVE_SHA256:-6d4
usage_telemetry_agent_binary_sha256=${USAGE_TELEMETRY_AGENT_BINARY_SHA256:-90ed65f689c91801abd0cf482211c96a01e7c63ef695e4faecdf4713eff730be}
supported_docker_range='~v1.12.3 || ~v1.13.0 || ~v17.03.0 || ~v17.06.0 || ~v17.09.0 || ~v17.12.0 || ~v18.03.0 || ~v18.06.0 || ~v18.09.0 || ~v19.03.2 || v24.0.9 || >=v29.4.1 <=v29.7.2 || v29.8.0'
newest_docker_version=v29.8.0
-image=${IMAGE:-pasturestack-validation/server:v1.6.553}
+image=${IMAGE:-pasturestack-validation/server:v1.6.554}
build_options=()
[[ "$revision" =~ ^[0-9a-f]{40}$ ]]
@@ -649,6 +649,7 @@ docker run --rm --env "WEB_CONSOLE_RELEASE_TAG=${web_console_release_tag}" --ent
set -euo pipefail
web_root=$(readlink -f /usr/share/cattle/war)
test "$(cat "${web_root}/VERSION.txt")" = "${WEB_CONSOLE_RELEASE_TAG}"
+ grep -Fx "{\"ceiling\":\"enabled\",\"initialMode\":\"off\"}" "${web_root}/webmcp-build.json"
echo "${PASTURESTACK_WEB_CONSOLE_RUNTIME_SBOM_SHA256} /usr/share/licenses/pasturestack/web-console/runtime.cdx.json" | sha256sum -c -
test "$(find "${web_root}/translations" -maxdepth 1 -type f -name "*.json" | wc -l)" -eq 13
test ! -e "${web_root}/translations/none.json"
diff --git a/server/build-component-candidate.sh b/server/build-component-candidate.sh
index f2c2f03dcd..73010beae4 100755
--- a/server/build-component-candidate.sh
+++ b/server/build-component-candidate.sh
@@ -18,10 +18,10 @@ for name in "${required[@]}"; do
case "$name" in *_COMMIT) [[ ${!name} =~ ^[0-9a-f]{40}$ ]] ;; *) [[ ${!name} =~ ^[0-9a-f]{64}$ ]] ;; esac
export "$name"
done
-export SERVER_RELEASE_TAG=${SERVER_RELEASE_TAG:-v1.6.553}
-export ORCHESTRATION_ENGINE_RELEASE_TAG=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.354}
+export SERVER_RELEASE_TAG=${SERVER_RELEASE_TAG:-v1.6.554}
+export ORCHESTRATION_ENGINE_RELEASE_TAG=${ORCHESTRATION_ENGINE_RELEASE_TAG:-v0.183.355}
export ORCHESTRATION_ENGINE_ARTIFACT=cattle.jar
-export WEB_CONSOLE_RELEASE_TAG=${WEB_CONSOLE_RELEASE_TAG:-1.6.195}
+export WEB_CONSOLE_RELEASE_TAG=${WEB_CONSOLE_RELEASE_TAG:-1.6.196}
export WEB_CONSOLE_ARTIFACT="web-console-${WEB_CONSOLE_RELEASE_TAG}.tar.gz"
export WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT=${WEB_CONSOLE_RUNTIME_SBOM_ARTIFACT:-web-console-${WEB_CONSOLE_RELEASE_TAG}-runtime.cdx.json}
export WEBSOCKET_PROXY_VERSION=${WEBSOCKET_PROXY_VERSION:-0.23.18}
diff --git a/server/security/openvex.json b/server/security/openvex.json
index 5b76737c8c..9ca77b9ff6 100644
--- a/server/security/openvex.json
+++ b/server/security/openvex.json
@@ -1,9 +1,9 @@
{
"@context": "https://openvex.dev/ns/v0.2.0",
- "@id": "https://github.com/PastureStack/server/security/openvex/v1.6.553",
+ "@id": "https://github.com/PastureStack/server/security/openvex/v1.6.554",
"author": "PastureStack Security",
- "timestamp": "2026-10-25T00:00:00Z",
- "version": 65,
+ "timestamp": "2026-10-29T09:57:33.809116+00:00",
+ "version": 66,
"statements": [
{
"vulnerability": {
diff --git a/server/security/vendor-pending.json b/server/security/vendor-pending.json
index ee28181f01..e36267215b 100644
--- a/server/security/vendor-pending.json
+++ b/server/security/vendor-pending.json
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
- "release": "v1.6.553",
- "generatedAt": "2026-10-25T00:00:00Z",
+ "release": "v1.6.554",
+ "generatedAt": "2026-10-29T09:57:33.809116+00:00",
"policy": {
"allowedSeverities": [
"LOW",
@@ -18,38 +18,42 @@
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-102473",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "dash",
"installedVersion": "0.5.12-12ubuntu3"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-102473.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-102473.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "ee3ce5fd986db42e62aff6989b4957bac30ce2a4667d035f1333f5bef171a3f0"
},
{
"vulnerabilityId": "CVE-2026-102474",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-102474",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "dash",
"installedVersion": "0.5.12-12ubuntu3"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-102474.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-102474.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "269f45920d570ea9a9c7f4c192082dcb19123b019b1459fe8569b57862c6f86e"
},
{
"vulnerabilityId": "CVE-2026-107161",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
- "upstreamVendorStatus": "Needs evaluation",
+ "upstreamVendorStatus": "deferred",
"source": "https://ubuntu.com/security/CVE-2026-107161",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"severityBasis": "Ubuntu priority Medium and the scanner's vendor severity; upstream CVSS v3 7.5 High is not a fix or a not-affected decision.",
"packages": [
{
@@ -61,38 +65,43 @@
"installedVersion": "2.1.28+dfsg1-9ubuntu3"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-107161.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-107161.json",
+ "vendorEvidenceSha256": "93e0ff1c051b635fd12e912a2a14f23da82369b10d8c3a7f68dc672b3a3cb3d8"
},
{
"vulnerabilityId": "CVE-2026-67693",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-67693",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-67693.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-67693.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "cc2f1e0582680b8ce803cb5fe23f4ad52076be85d15346041d9c6ff0eb46bf14"
},
{
"vulnerabilityId": "CVE-2026-88647",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-88647",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-88647.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-88647.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "691d53b0422bb396bd5c5318c5e767a58fb7ef0787bb17d237c36c0a975a9f07"
},
{
"vulnerabilityId": "CVE-2026-88648",
@@ -100,61 +109,68 @@
"vendorStatus": "needs-evaluation",
"upstreamVendorStatus": "deferred",
"source": "https://ubuntu.com/security/CVE-2026-88648",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-23T03:05:10Z",
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
"vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-88648.json",
- "notes": "Canonical lists Ubuntu 26.04 gnutls28 as vulnerable with its fix deferred. The official OSV range has no fixed event and the current runtime scan has an empty FixedVersion. Retain the raw finding; do not claim remediation or not_affected."
+ "notes": "Canonical lists Ubuntu 26.04 gnutls28 as vulnerable with its fix deferred. The official OSV range has no fixed event and the current runtime scan has an empty FixedVersion. Retain the raw finding; do not claim remediation or not_affected.",
+ "vendorEvidenceSha256": "cd2e721b40d5fe5b37e69de24ff3b3bb4c6aef20cbe8b643778f2413112e2846"
},
{
"vulnerabilityId": "CVE-2026-95184",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-95184",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95184.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95184.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "0d460c6b3ead6382852ad4413413c1adffaf9680c1aaf3abf2b727344b1a4ac0"
},
{
"vulnerabilityId": "CVE-2026-95209",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-95209",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95209.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95209.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "e5e4a6178158471a4c281aa36d4ce2de1b43728ddba6d1470fb78cf0c66e6749"
},
{
"vulnerabilityId": "CVE-2026-95210",
"severity": "MEDIUM",
"vendorStatus": "needs-evaluation",
"source": "https://ubuntu.com/security/CVE-2026-95210",
- "reviewAfter": "2026-10-27",
+ "reviewAfter": "2026-11-05",
"packages": [
{
"name": "libgnutls30t64",
"installedVersion": "3.8.12-2ubuntu1.1"
}
],
- "verifiedAt": "2026-10-20T01:35:18Z",
- "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95210.json"
+ "verifiedAt": "2026-10-29T09:57:07.268087+00:00",
+ "vendorEvidence": "https://raw.githubusercontent.com/canonical/ubuntu-security-notices/main/osv/cve/2026/UBUNTU-CVE-2026-95210.json",
+ "upstreamVendorStatus": "deferred",
+ "vendorEvidenceSha256": "3346e54d52e88e9521aa60871c147ffca621436244694f13a9f4646ebf50a711"
}
]
}