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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ RUN set -eu; \
echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin" >&2; exit 1; \
fi; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; fi; \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ] || [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; fi; \
MCPORTER_EXPECTED_INTEGRITY=""; \
MCPORTER_EXPECTED_TARBALL=""; \
if [ "$MCPORTER_VERSION" = "0.7.3" ]; then MCPORTER_EXPECTED_INTEGRITY="$MCPORTER_0_7_3_INTEGRITY"; MCPORTER_EXPECTED_TARBALL="$MCPORTER_0_7_3_TARBALL"; fi; \
Expand Down Expand Up @@ -353,7 +353,7 @@ RUN set -eu; \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_SOURCE_PACK_PATH")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ] || [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \
--archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \
--working-directory "$OPENCLAW_PACK_DIR")"; \
Expand All @@ -370,9 +370,10 @@ RUN set -eu; \
esac; \
rm -rf "$OPENCLAW_PACK_DIR"; \
fi; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null; \
fi; \
case "$OPENCLAW_VERSION" in \
2026.3.11) npm ls -g --depth=1 openclaw tar >/dev/null ;; \
2026.6.10) npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null ;; \
esac; \
if [ "$USE_REVIEWED_BASE_RUNTIME" = "1" ]; then \
echo "INFO: Reusing reviewed base mcporter $CUR_MCPORTER_VER with exact lock provenance"; \
else \
Expand Down
9 changes: 5 additions & 4 deletions Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_SOURCE_PACK_PATH")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \
if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ] || [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
OPENCLAW_PACK_PATH="$(node --experimental-strip-types /scripts/lib/openclaw-npm-remediation.mts \
--archive "$OPENCLAW_SOURCE_PACK_PATH" --package-spec "openclaw@${OPENCLAW_VERSION}" \
--working-directory "$OPENCLAW_PACK_DIR")"; \
Expand All @@ -299,9 +299,10 @@ RUN --mount=type=bind,source=nemoclaw-blueprint/blueprint.yaml,target=/tmp/bluep
&& if [ "$OPENCLAW_INSTALLED_VERSION" != "$OPENCLAW_VERSION" ]; then \
echo "Error: Installed OpenClaw ${OPENCLAW_INSTALLED_VERSION:-unknown} does not match reviewed target ${OPENCLAW_VERSION}"; exit 1; \
fi \
&& if [ "$OPENCLAW_VERSION" = "2026.6.10" ]; then \
npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null; \
fi \
&& case "$OPENCLAW_VERSION" in \
2026.3.11) npm ls -g --depth=1 openclaw tar >/dev/null ;; \
2026.6.10) npm ls -g --depth=1 openclaw @openclaw/fs-safe tar jszip >/dev/null ;; \
esac \
&& MCPORTER_EXPECTED_INTEGRITY="" \
&& MCPORTER_EXPECTED_TARBALL="" \
&& if [ "$MCPORTER_VERSION" = "0.7.3" ]; then MCPORTER_EXPECTED_INTEGRITY="$MCPORTER_0_7_3_INTEGRITY"; MCPORTER_EXPECTED_TARBALL="$MCPORTER_0_7_3_TARBALL"; fi \
Expand Down
11 changes: 6 additions & 5 deletions agents/langchain-deepagents-code/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ FROM node:22-trixie-slim@sha256:2d9f5c76c8f4dd36e8f253bee5d828a83a6c09f36188f0b0
COPY scripts/lib/reviewed-npm-archive.mts /scripts/lib/reviewed-npm-archive.mts
COPY scripts/patch-bundled-npm-tar.mts /scripts/patch-bundled-npm-tar.mts

# Node remains available to the managed terminal at runtime, so remediate
# npm's private node-tar copy even though Deep Agents Code itself is Python.
RUN node --experimental-strip-types /scripts/patch-bundled-npm-tar.mts \
--npm-root /usr/local/lib/node_modules/npm

ENV DEBIAN_FRONTEND=noninteractive \
VIRTUAL_ENV=/opt/venv \
PATH="/usr/local/bin:/opt/venv/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"
Expand All @@ -40,6 +35,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
vim-tiny=2:9.1.1230-2 \
&& rm -rf /var/lib/apt/lists/*

# Node remains available to the managed terminal at runtime, so remediate
# npm's private node-tar copy even though Deep Agents Code itself is Python.
# The verified archive downloader is installed in the preceding layer.
RUN node --experimental-strip-types /scripts/patch-bundled-npm-tar.mts \
--npm-root /usr/local/lib/node_modules/npm

RUN groupadd -r sandbox \
&& useradd -r -g sandbox -d /sandbox -s /bin/bash sandbox \
&& usermod -a -G sandbox root \
Expand Down
14 changes: 13 additions & 1 deletion docs/security/openclaw-2026.6.10-dependency-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The OpenClaw 2026.6.10 bump does not newly introduce an unfrozen OpenClaw transi

### Transitive Remediation Boundary

`scripts/lib/openclaw-npm-remediation.mts` remediates only four exact reviewed identities.
`scripts/lib/openclaw-npm-remediation.mts` remediates only five exact reviewed identities.
It rejects an unexpected source dependency shape before it changes or installs an archive.
The helper verifies every replacement package by exact registry SRI and tarball URL.
It also rejects unsafe archive members before extraction and after repacking.
Expand All @@ -89,6 +89,9 @@ For `openclaw@2026.6.10`, the helper makes these changes:
- Bundles the reviewed `@openclaw/fs-safe@0.3.0` package and removes its duplicate optional `tar` and `jszip` declarations. The bundled package resolves OpenClaw's reviewed direct `tar@7.5.19` and `jszip@3.10.1` dependencies instead, including during a global npm install.
- Verifies the installed global dependency tree before either the reviewed base image or production image can complete.

For the E2E-only `openclaw@2026.3.11` identity, the helper replaces the exact `tar@7.5.11` declaration with reviewed `tar@7.5.19`.
It rejects a source archive that has different dependency metadata or an unexpected npm shrinkwrap.
Comment on lines +92 to +93

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Narrow the claimed source-shape validation.

The helper does not reject arbitrary dependency-metadata changes before rebuilding; it validates only tar@7.5.11, no bundledDependencies, and no shrinkwrap. Other manifest drift fails later through the metadata hash. Update this wording or enforce an exact dependency shape. As per coding guidelines, “Verify documented behavior against source code, tests, scripts, or existing documentation before publishing it.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/security/openclaw-2026.6.10-dependency-review.md` around lines 92 - 93,
The documentation overstates the helper’s pre-rebuild source-shape validation.
Update the text describing the E2E-only openclaw@2026.3.11 handling to state
only the checks actually enforced—tar@7.5.11, absence of bundledDependencies,
and absence of an npm shrinkwrap—while noting that other manifest drift is
detected later by the metadata hash.

Source: Coding guidelines


For `@openclaw/slack@2026.6.10` and `@openclaw/msteams@2026.6.10`, the helper makes these changes:

- Replaces bundled `axios@1.16.0` with `axios@1.18.0`.
Expand Down Expand Up @@ -123,6 +126,7 @@ It binds each patched package manifest and shrinkwrap to a committed SHA-512 met
The core value also covers the bundled `@openclaw/fs-safe` package manifest.
The diagnostics value also covers the bundled SDK, Jaeger propagator, and nested core package manifests.
The expected values are `sha512-B5O6Gu3YGY52w+Px8diL5zBtk8mj0u7E1ZvVK7KOLWX9H+S3B7kYUxnGfyB239mVYSluecfiWGvFFMk5eFhwKg==` for OpenClaw core, `sha512-ByLYBs3KXz3u0mPuj9DcP/xPTJNgQaLTPxazybhyIC1VjyftEmKQuoZufPZ8z8CjwBsOPm6NbjMQB2BfX36TTg==` for diagnostics OTEL, `sha512-AXllGzI+m33jUq3w1nCVXngLA1m9kH8c9XryHSoPzuVhGP6xwWpzgKl3yyfOMoIykN0GKcka59ZZbjEwkxFudQ==` for Slack, and `sha512-eTTIpA8HzcBwXBLt6UZDoFgOUmkRgIhcZFBOwg+5Jfgt8HDwtfPnqKo6vm2DdDdPMPhu08FbEzU5Gt3RoL5fIw==` for Microsoft Teams.
The E2E-only `openclaw@2026.3.11` value is `sha512-c+3QxBJidAFb8xZSmz4azC7KHFvXUAY9vN1AlXJ243LwMCFN5it5MW0r6FBuxIFvlBCnGlzcqRCvU5ghUec/ng==`.
Both the library and command-line entry points enforce the same committed values.
`Dockerfile.base` records `ignore-scripts+reviewed-lifecycle+transitive-remediation-v1` in its protected provenance marker.
The production Dockerfile rejects stale base provenance and repeats the remediation when the marker does not match.
Expand Down Expand Up @@ -251,6 +255,14 @@ The reviewed `@openclaw/diagnostics-otel@2026.6.10` package dist imports `OTLPTr

The legacy `2026.3.11` and `2026.4.24` OpenClaw pins are retained only for stale-upgrade fixture builds. Production Dockerfile install blocks now reject those versions unless `NEMOCLAW_E2E_FIXTURE_LEGACY_OPENCLAW=1` is set explicitly. The E2E-scoped name is intentionally noisy so production build workflows do not treat it as a general override. Production image workflows run `scripts/check-production-build-args.sh` before production Docker builds so the fixture flag, both legacy version values, and every integrity/tarball Docker ARG declared by a production Dockerfile cannot be overridden through production build args or their corresponding environment variables. The guard also rejects future positional `*_INTEGRITY` and `*_TARBALL` names, keeping reviewed pin values repository-controlled even before the Dockerfile's registry and downloaded-archive checks run. The stale-upgrade E2E build contexts pass their fixture values only on fixture-specific build paths, and the integrity-pin contract suite verifies the default rejection, the explicit fixture opt-in, and the production workflow guard.

Frozen OpenShell gateway-upgrade fixtures select only the SRI-pinned OpenClaw `2026.4.24`, `2026.5.22`, or `2026.5.27` archive.
The live test uses `packReviewedNpmArchive` to verify exact registry metadata, the reviewed tarball URL, and the downloaded SRI.
The adapter copies only that verified local archive into the historical build context.
It installs the archive with lifecycle scripts disabled and invokes `postinstall-bundled-plugins.mjs` directly.
The fixtures retain npm registry signature verification for the historical mcporter lock.
The adapter requires exactly one advisory audit statement before it replaces that statement with a test-only skip.
`test/e2e/support/openshell-gateway-upgrade-old-installer.test.ts` verifies these constraints.

Invalid state: a production image build overriding `OPENCLAW_VERSION` to an old fixture pin or replacing any repository-reviewed integrity/tarball value while still passing the workflow boundary. Source boundary: Dockerfile and Dockerfile.base install blocks plus the guard that precedes every production image build. Source-fix constraint: keep stale-upgrade E2Es able to build old images without normalizing those pins or accepting caller-controlled production package identity. Regression tests: the integrity-pin contract suite rejects the flag, both legacy versions, all declared integrity/tarball ARG overrides through direct, `--build-arg`, and environment paths, and a future-shaped positional pin name; `test/openclaw-dependency-review.test.ts` proves all seven production image builds are guard-protected and carry no literal fixture selectors. Removal condition: issue #5896 section 9 retires the old-base fixture strategy and fixture flag; the general repository-owned production pin guard remains until production builds no longer expose package identity as Docker ARGs.

### OpenClaw Device Approval Convergence Boundary
Expand Down
32 changes: 30 additions & 2 deletions scripts/lib/openclaw-npm-remediation.mts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { packReviewedNpmArchive } from "./reviewed-npm-archive.mts";
type JsonObject = Record<string, any>;

type Remediation = Readonly<{
kind: "core" | "diagnostics-otel" | "plugin";
kind: "core" | "diagnostics-otel" | "legacy-core" | "plugin";
expectedPatchedMetadataIntegrity: string;
}>;

Expand Down Expand Up @@ -105,6 +105,11 @@ const REMEDIATIONS: Readonly<Record<string, Remediation>> = Object.freeze({
expectedPatchedMetadataIntegrity:
"sha512-B5O6Gu3YGY52w+Px8diL5zBtk8mj0u7E1ZvVK7KOLWX9H+S3B7kYUxnGfyB239mVYSluecfiWGvFFMk5eFhwKg==",
},
"openclaw@2026.3.11": {
kind: "legacy-core",
expectedPatchedMetadataIntegrity:
"sha512-c+3QxBJidAFb8xZSmz4azC7KHFvXUAY9vN1AlXJ243LwMCFN5it5MW0r6FBuxIFvlBCnGlzcqRCvU5ghUec/ng==",
},
});

function run(command: string, args: readonly string[], cwd: string, env: NodeJS.ProcessEnv) {
Expand Down Expand Up @@ -183,7 +188,10 @@ function writeJson(path: string, value: JsonObject): void {

function hashPatchedMetadata(packageDirectory: string): string {
const hash = createHash("sha512");
const names = ["package.json", "npm-shrinkwrap.json"];
const names = ["package.json"];
if (existsSync(join(packageDirectory, "npm-shrinkwrap.json"))) {
names.push("npm-shrinkwrap.json");
}
const bundledFsSafePackageJson = "node_modules/@openclaw/fs-safe/package.json";
if (existsSync(join(packageDirectory, bundledFsSafePackageJson))) {
names.push(bundledFsSafePackageJson);
Expand Down Expand Up @@ -384,6 +392,24 @@ export function patchOpenClawCorePackageGraph(packageDirectory: string): void {
writeJson(shrinkwrapPath, shrinkwrap);
}

export function patchLegacyOpenClawCorePackageGraph(packageDirectory: string): void {
const packageJsonPath = join(packageDirectory, "package.json");
const packageJson = readJson(packageJsonPath);
requirePackageIdentity(packageJson, "openclaw", "2026.3.11", "Legacy OpenClaw core");
if (packageJson.dependencies?.tar !== "7.5.11") {
throw new Error("openclaw@2026.3.11 must declare reviewed tar@7.5.11 before remediation");
}
if (packageJson.bundledDependencies !== undefined) {
throw new Error("openclaw@2026.3.11 unexpectedly declares bundled dependencies");
}
if (existsSync(join(packageDirectory, "npm-shrinkwrap.json"))) {
throw new Error("openclaw@2026.3.11 unexpectedly ships an npm shrinkwrap");
}

packageJson.dependencies.tar = TAR_VERSION;
writeJson(packageJsonPath, packageJson);
}

export function patchOpenClawDiagnosticsPackageGraph(packageDirectory: string): void {
const packageJsonPath = join(packageDirectory, "package.json");
const shrinkwrapPath = join(packageDirectory, "npm-shrinkwrap.json");
Expand Down Expand Up @@ -565,6 +591,8 @@ export function buildRemediatedOpenClawArchive(request: BuildRequest): Remediate
join(sourcePackage, "node_modules", "@openclaw", "fs-safe"),
);
patchOpenClawCorePackageGraph(sourcePackage);
} else if (remediation.kind === "legacy-core") {
patchLegacyOpenClawCorePackageGraph(sourcePackage);
} else if (remediation.kind === "diagnostics-otel") {
const jaegerArchive = packReplacement(
`@opentelemetry/propagator-jaeger@${OTEL_PROPAGATOR_JAEGER_VERSION}`,
Expand Down
135 changes: 135 additions & 0 deletions test/e2e/live/openshell-gateway-upgrade-old-installer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import fs from "node:fs";

export type ReviewedOldOpenClawArchive = Readonly<{
expectedIntegrity: string;
label: string;
packageSpec: string;
tarballUrl: string;
}>;

const REVIEWED_OLD_OPENCLAW_ARCHIVES: Readonly<Record<string, ReviewedOldOpenClawArchive>> =
Object.freeze({
"2026.4.24": {
expectedIntegrity:
"sha512-W6u4XeIIP4+uG4DYV9G3JeS6QNuKwfhQIej1GIoL4BdcnUFgrnB8kHYNXL3MxiHRKuhZB9OYwUMGs8jKFZR/Vg==",
label: "historical fixture OpenClaw 2026.4.24",
packageSpec: "openclaw@2026.4.24",
tarballUrl: "https://registry.npmjs.org/openclaw/-/openclaw-2026.4.24.tgz",
},
"2026.5.22": {
expectedIntegrity:
"sha512-m+zgBELGbCHjWB1IWF5WSWNPr480cMKOMff2OF72c8A0AMD4hC/9+qwYtzjYmGkETcffnB711JymlVsQnh2Tow==",
label: "historical fixture OpenClaw 2026.5.22",
packageSpec: "openclaw@2026.5.22",
tarballUrl: "https://registry.npmjs.org/openclaw/-/openclaw-2026.5.22.tgz",
},
"2026.5.27": {
expectedIntegrity:
"sha512-2N93zhdAo88KAbHt6T7KvYXf4s7XIkYXBgv1npYpn7e1Y9FvrtgtpsA38my9rtFW+70uXEojRPX5/OqnuDqJPw==",
label: "historical fixture OpenClaw 2026.5.27",
packageSpec: "openclaw@2026.5.27",
tarballUrl: "https://registry.npmjs.org/openclaw/-/openclaw-2026.5.27.tgz",
},
});

export function reviewedOldOpenClawArchive(version: string): ReviewedOldOpenClawArchive {
const reviewedArchive = REVIEWED_OLD_OPENCLAW_ARCHIVES[version];
if (!reviewedArchive) {
throw new Error(`Historical gateway upgrade OpenClaw ${version} has no reviewed archive pin`);
}
return reviewedArchive;
}

// The frozen release installers are the source of truth, but their embedded
// Dockerfiles predate the fixture pins needed for a deterministic upgrade test.
// Keep this adapter scoped to the frozen historical lanes and retire it with
// them; changing the tagged release payloads is not viable.
export function patchOldInstallerFixture(installer: string): void {
const needle = ' legacy_script="${source_root}/install.sh"\n';
const hook =
String.raw` if [[ -n "\${NEMOCLAW_OLD_OPENCLAW_VERSION:-}" && -f "$payload_script" ]]; then
python3 - "$payload_script" <<'NEMOCLAW_OLD_PAYLOAD_PIN_PY'
from pathlib import Path
import sys

path = Path(sys.argv[1])
text = path.read_text(encoding="utf-8")
needle = ' spin "Cloning \${_CLI_DISPLAY} source" clone_nemoclaw_ref "$release_ref" "$nemoclaw_src"\n'
hook = r''' if [[ -n "\${NEMOCLAW_OLD_OPENCLAW_VERSION:-}" ]]; then
if [[ -z "\${NEMOCLAW_OLD_OPENCLAW_ARCHIVE:-}" || ! -f "$NEMOCLAW_OLD_OPENCLAW_ARCHIVE" ]]; then
echo "ERROR: reviewed historical OpenClaw archive is missing" >&2
exit 1
fi
cp -- "$NEMOCLAW_OLD_OPENCLAW_ARCHIVE" "$nemoclaw_src/.nemoclaw-e2e-old-openclaw.tgz"
python3 - "$nemoclaw_src/Dockerfile" "$NEMOCLAW_OLD_OPENCLAW_VERSION" <<'NEMOCLAW_OLD_DOCKERFILE_PIN_PY'
from pathlib import Path
import sys

path = Path(sys.argv[1])
version = sys.argv[2]
text = path.read_text(encoding="utf-8")
injection = (
"# E2E old-upgrade fixture: force the historical OpenClaw before the old Dockerfile's version gate.\n"
"COPY .nemoclaw-e2e-old-openclaw.tgz /tmp/nemoclaw-e2e-old-openclaw.tgz\n"
"RUN rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw \\\n"
" && npm install -g --ignore-scripts --no-audit --no-fund --no-progress /tmp/nemoclaw-e2e-old-openclaw.tgz \\\n"
" && node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs \\\n"
f" && test \"$(openclaw --version | awk '{{print $2}}')\" = \"{version}\" \\\n"
" && rm -f /tmp/nemoclaw-e2e-old-openclaw.tgz\n\n"
)
if injection not in text:
arg_markers = [
line for line in text.splitlines(keepends=True)
if line.startswith("ARG OPENCLAW_VERSION=")
]
if len(arg_markers) == 1:
marker = arg_markers[0]
text = text.replace(marker, marker + "\n" + injection, 1)
elif len(arg_markers) > 1:
raise SystemExit(
f"{path}: found {len(arg_markers)} OpenClaw version ARGs; expected exactly one"
)
else:
marker = "RUN set -eu; \\\n MIN_VER=$(grep -m 1 'min_openclaw_version'"
if marker not in text:
raise SystemExit(f"{path}: old OpenClaw version gate not found")
text = text.replace(marker, injection + marker, 1)

advisory_audit = ' npm --prefix /usr/local/lib/nemoclaw/mcporter-runtime audit --omit=dev --audit-level=low; \\\n'
advisory_audit_count = text.count(advisory_audit)
if advisory_audit_count != 1:
raise SystemExit(
f"{path}: found {advisory_audit_count} historical mcporter advisory audits; expected exactly one"
)
audit_fixture_note = (
' echo "INFO: Skipping current advisory audit for the immutable historical mcporter lock"; \\\n'
)
text = text.replace(advisory_audit, audit_fixture_note, 1)

path.write_text(text, encoding="utf-8")
print(f"INFO: Forced OpenClaw {version} in old upgrade fixture Dockerfile", flush=True)
NEMOCLAW_OLD_DOCKERFILE_PIN_PY
fi
'''
if hook not in text:
if needle not in text:
raise SystemExit(f"{path}: old source clone hook not found")
text = text.replace(needle, needle + hook, 1)
path.write_text(text, encoding="utf-8")
NEMOCLAW_OLD_PAYLOAD_PIN_PY
fi
`.replaceAll("\\${", "${");

const text = fs.readFileSync(installer, "utf8");
const patchedText = text.includes(hook)
? text
: text.includes(needle)
? text.replace(needle, needle + hook)
: (() => {
throw new Error(`${installer}: old bootstrap payload hook not found`);
})();
fs.writeFileSync(installer, patchedText, "utf8");
}
Loading
Loading