Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
db85e27
fix(images): lock OpenClaw production graph
HOYALIM Jul 14, 2026
2c3e151
fix(ci): trust reviewed npm audit boundary
HOYALIM Jul 14, 2026
e975d24
fix(ci): harden reviewed npm audit boundaries
HOYALIM Jul 14, 2026
b86d283
test(ci): keep audit path cases linear
HOYALIM Jul 14, 2026
cba0615
fix(images): close reviewed manifest races
HOYALIM Jul 14, 2026
fe37cce
test(images): align locked OpenClaw contracts
HOYALIM Jul 15, 2026
0a0115d
Merge branch 'main' into codex/issue-5896-openclaw-lock
cv Jul 18, 2026
57c97bf
fix(ci): keep reviewed npm policy trusted
HOYALIM Jul 18, 2026
ef99fd8
ci: pin trusted audit policy bootstrap
HOYALIM Jul 18, 2026
5ac30d1
test(ci): cover unavailable trusted audit
HOYALIM Jul 18, 2026
f18b6f9
merge: refresh #6830 with current main
apurvvkumaria Jul 20, 2026
b6ed20c
Merge branch 'main' into codex/issue-5896-openclaw-lock
cv Jul 20, 2026
86c904a
merge: sync #6830 with current main
apurvvkumaria Jul 25, 2026
0c7dd29
fix(security): lock remediated OpenClaw runtime graph
apurvvkumaria Jul 25, 2026
5a73c02
docs(security): record OpenClaw graph remediation
apurvvkumaria Jul 25, 2026
d3a3109
Merge remote-tracking branch 'origin/main' into codex/pr6830-current-…
apurvvkumaria Jul 25, 2026
6211fb3
fix(ci): refresh reviewed audit bootstrap
apurvvkumaria Jul 25, 2026
ec2574b
fix(ci): include reviewed audit helper closure
apurvvkumaria Jul 25, 2026
d727cca
fix(ci): include reviewed audit policy
apurvvkumaria Jul 25, 2026
b53750b
fix(ci): align locked OpenClaw test contracts
apurvvkumaria Jul 25, 2026
b909b5d
fix(ci): remove stale reviewed metadata import
HOYALIM Jul 25, 2026
791cd0b
Merge branch 'main' into codex/issue-5896-openclaw-lock
apurvvkumaria Jul 25, 2026
487c527
Merge branch 'main' into codex/issue-5896-openclaw-lock
cv Jul 25, 2026
cf8c889
Merge branch 'main' into codex/issue-5896-openclaw-lock
cv Jul 25, 2026
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
28 changes: 25 additions & 3 deletions .github/actions/ci-reviewed-npm-audit/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@
# SPDX-License-Identifier: Apache-2.0

name: ci-reviewed-npm-audit
description: Materialize and audit the reviewed production npm graphs.
description: Audit a target tree's reviewed production npm graphs from trusted CI code.

inputs:
target-root:
description: Absolute path to the checked-out target tree whose graph inputs are audited.
required: true
report-dir:
description: Report directory relative to target-root.
required: true

runs:
using: composite
Expand All @@ -12,14 +20,28 @@ runs:
with:
node-version: "22.23.1"

- name: Pin production npm
shell: bash
run: >-
cd "$RUNNER_TEMP" &&
npm install --global npm@10.9.4
--userconfig /dev/null
--registry https://registry.npmjs.org/
--ignore-scripts --no-audit --no-fund

- name: Materialize and audit reviewed npm graphs
shell: bash
run: node --experimental-strip-types scripts/audit-reviewed-npm-graph.mts
env:
NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT: ${{ inputs.target-root }}
NEMOCLAW_REVIEWED_NPM_AUDIT_REPORT_DIR: ${{ inputs.report-dir }}
NPM_CONFIG_REGISTRY: https://registry.npmjs.org/
NPM_CONFIG_USERCONFIG: /dev/null
run: node --experimental-strip-types "$GITHUB_ACTION_PATH/../../../scripts/audit-reviewed-npm-graph.mts"

- name: Upload reviewed npm audit reports
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: reviewed-npm-audit
path: coverage/reviewed-npm-audit/*.json
path: ${{ inputs.target-root }}/${{ inputs.report-dir }}/*.json
if-no-files-found: error
8 changes: 7 additions & 1 deletion .github/actions/resolve-sandbox-base-image/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ runs:

image="ghcr.io/nvidia/nemoclaw/sandbox-base"
min_glibc="2.39"
base_inputs=(Dockerfile.base nemoclaw-blueprint/blueprint.yaml)
base_inputs=(
Dockerfile.base
agents/openclaw/openclaw-runtime/package.json
agents/openclaw/openclaw-runtime/package-lock.json
nemoclaw-blueprint/blueprint.yaml
scripts/lib/reviewed-npm-archive.mts
)
source "${GITHUB_ACTION_PATH}/../base-image-resolver.sh"

normalize_version_tag() {
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/base-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ on:
# takes effect immediately after merge instead of waiting for another tag.
- ".github/workflows/base-image.yaml"
- "Dockerfile.base"
- "agents/openclaw/openclaw-runtime/package.json"
- "agents/openclaw/openclaw-runtime/package-lock.json"
- "scripts/lib/reviewed-npm-archive.mts"
- "agents/hermes/Dockerfile.base"
- "agents/langchain-deepagents-code/Dockerfile.base"
- "agents/langchain-deepagents-code/manifest.yaml"
Expand All @@ -34,7 +37,6 @@ on:
- "nemoclaw-blueprint/blueprint.yaml"
- "scripts/lib/openclaw-npm-remediation.mts"
- "scripts/lib/reviewed-npm-audit.mts"
- "scripts/lib/reviewed-npm-archive.mts"
- "scripts/checks/node-tar-image-scan.mts"
- "scripts/patch-bundled-npm-tar.mts"
- "scripts/upgrade-bundled-npm.mts"
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ jobs:

- name: Audit reviewed production npm graphs
uses: ./.github/actions/ci-reviewed-npm-audit
with:
target-root: ${{ github.workspace }}
report-dir: artifacts/reviewed-npm-audit

real-openclaw-dist-harness:
runs-on: ubuntu-latest
Expand Down
73 changes: 72 additions & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,79 @@ jobs:
with:
persist-credentials: false

- name: Checkout trusted reviewed npm audit
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ github.event.pull_request.base.sha }}
path: .trusted-reviewed-npm-audit
persist-credentials: false
sparse-checkout: |
.github/actions/ci-reviewed-npm-audit
ci/npm-audit-exceptions.json
ci/reviewed-npm-audit.json
scripts/audit-reviewed-npm-graph.mts
scripts/lib/openclaw-npm-remediation.mts
scripts/lib/reviewed-npm-archive.mts
scripts/lib/reviewed-npm-audit.mts
sparse-checkout-cone-mode: false

- name: Detect trusted reviewed npm audit schema
id: trusted-reviewed-npm-audit
shell: bash
run: |
if grep -Fq 'NEMOCLAW_REVIEWED_NPM_AUDIT_TARGET_ROOT' \
.trusted-reviewed-npm-audit/.github/actions/ci-reviewed-npm-audit/action.yaml \
&& grep -Fq 'resolveTrustedAuditConfigPath(TRUSTED_REPO_ROOT)' \
.trusted-reviewed-npm-audit/scripts/audit-reviewed-npm-graph.mts \
&& [ -f .trusted-reviewed-npm-audit/ci/npm-audit-exceptions.json ] \
&& [ -f .trusted-reviewed-npm-audit/ci/reviewed-npm-audit.json ] \
&& [ -f .trusted-reviewed-npm-audit/scripts/lib/openclaw-npm-remediation.mts ] \
&& [ -f .trusted-reviewed-npm-audit/scripts/lib/reviewed-npm-audit.mts ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi

# One-time bootstrap for this PR only. The action, driver, and helper are
# pinned to its signed first commit; every later PR must use its base SHA.
- name: Checkout pinned bootstrap reviewed npm audit
if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && github.event.pull_request.number == 6830 && github.event.pull_request.head.repo.full_name == 'HOYALIM/NemoClaw' }}
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
repository: HOYALIM/NemoClaw
ref: 0c7dd29394d2c4db660c4d09f3654c0789e200d0
path: .trusted-reviewed-npm-audit-bootstrap
persist-credentials: false
sparse-checkout: |
.github/actions/ci-reviewed-npm-audit
ci/npm-audit-exceptions.json
ci/reviewed-npm-audit.json
scripts/audit-reviewed-npm-graph.mts
scripts/lib/openclaw-npm-remediation.mts
scripts/lib/reviewed-npm-archive.mts
scripts/lib/reviewed-npm-audit.mts
sparse-checkout-cone-mode: false

- name: Reject unavailable trusted reviewed npm audit
if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && (github.event.pull_request.number != 6830 || github.event.pull_request.head.repo.full_name != 'HOYALIM/NemoClaw') }}
shell: bash
run: |
echo "::error::Trusted base SHA does not contain the schema-2 reviewed npm audit"
exit 1

- name: Audit reviewed production npm graphs
uses: ./.github/actions/ci-reviewed-npm-audit
if: ${{ steps.trusted-reviewed-npm-audit.outputs.available == 'true' }}
uses: ./.trusted-reviewed-npm-audit/.github/actions/ci-reviewed-npm-audit
with:
target-root: ${{ github.workspace }}
report-dir: artifacts/reviewed-npm-audit

- name: Audit reviewed production npm graphs (pinned bootstrap)
if: ${{ steps.trusted-reviewed-npm-audit.outputs.available != 'true' && github.event.pull_request.number == 6830 && github.event.pull_request.head.repo.full_name == 'HOYALIM/NemoClaw' }}
uses: ./.trusted-reviewed-npm-audit-bootstrap/.github/actions/ci-reviewed-npm-audit
with:
target-root: ${{ github.workspace }}
report-dir: artifacts/reviewed-npm-audit

cli-test-shards:
needs: changes
Expand Down
71 changes: 52 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ RUN ln -s /opt/nemoclaw/node_modules /opt/nemoclaw-root/node_modules \
# related payloads without invalidating earlier final-image work.
FROM scratch AS openclaw-dependency-payload

COPY agents/openclaw/openclaw-runtime/package.json /usr/local/lib/nemoclaw/openclaw-runtime/package.json
COPY agents/openclaw/openclaw-runtime/package-lock.json /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json
COPY agents/openclaw/mcporter-runtime/package.json /usr/local/lib/nemoclaw/mcporter-runtime/package.json
COPY agents/openclaw/mcporter-runtime/package-lock.json /usr/local/lib/nemoclaw/mcporter-runtime/package-lock.json
COPY agents/openclaw/wechat-runtime/package.json /usr/local/lib/nemoclaw/wechat-runtime/package.json
Expand Down Expand Up @@ -375,8 +377,17 @@ RUN set -eu; \
if [ -z "$EXPECTED_INTEGRITY" ]; then \
echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no committed npm integrity pin" >&2; exit 1; \
fi; \
OPENCLAW_LOCK_SHA256=none-legacy-fixture; \
OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle-v1'; \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then OPENCLAW_RECIPE='ignore-scripts+reviewed-lifecycle+transitive-remediation-v1'; fi; \
if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \
OPENCLAW_LOCK_SHA256=82489f62febb12da52833c0b1f7f6969f7e21a098c565ef1f91342b1e5e32d88; \
ACTUAL_OPENCLAW_LOCK_SHA256="$(sha256sum /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json | awk '{print $1}')"; \
[ "$ACTUAL_OPENCLAW_LOCK_SHA256" = "$OPENCLAW_LOCK_SHA256" ] \
|| { echo "ERROR: OpenClaw lock SHA-256 mismatch (expected $OPENCLAW_LOCK_SHA256, found $ACTUAL_OPENCLAW_LOCK_SHA256)" >&2; exit 1; }; \
OPENCLAW_RECIPE='locked-ci+reviewed-lifecycle-v2'; \
elif [ "$OPENCLAW_VERSION" = "2026.3.11" ]; 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 @@ -404,6 +415,7 @@ RUN set -eu; \
"package=openclaw@${OPENCLAW_VERSION}" \
"integrity=${EXPECTED_INTEGRITY}" \
"tarball=${EXPECTED_TARBALL}" \
"lock-sha256=${OPENCLAW_LOCK_SHA256}" \
"recipe=${OPENCLAW_RECIPE}" \
"mcporter-package=mcporter@${MCPORTER_VERSION}" \
"mcporter-integrity=${MCPORTER_EXPECTED_INTEGRITY}" \
Expand Down Expand Up @@ -437,28 +449,49 @@ RUN set -eu; \
echo "ERROR: Base image has OpenClaw $CUR_VER, which is newer than reviewed target $OPENCLAW_VERSION" >&2; exit 1; \
else \
echo "INFO: Base image OpenClaw $CUR_VER lacks exact reviewed provenance; installing $OPENCLAW_VERSION"; \
OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
--package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \
OPENCLAW_REMEDIATION_JSON="$(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")"; \
OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \
fi; \
# npm 10's atomic-move install can hit EROFS on overlayfs when the prior
# install spans image layers. Removing it first also prevents unreviewed
# files from surviving a same-version reinstall.
rm -rf /usr/local/lib/node_modules/openclaw /usr/local/bin/openclaw; \
npm install -g --no-audit --no-fund --no-progress --ignore-scripts "$OPENCLAW_PACK_PATH"; \
case "$OPENCLAW_VERSION" in \
2026.4.24|2026.7.1) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \
2026.3.11) ;; \
*) echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy" >&2; exit 1 ;; \
esac; \
rm -rf "$OPENCLAW_PACK_DIR"; \
if [ "$OPENCLAW_VERSION" = "2026.7.1" ]; then \
node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts --verify-lock \
--lock-sha256 "$OPENCLAW_LOCK_SHA256" \
--lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \
--registry-origin https://registry.npmjs.org/ \
--package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}"; \
npm --prefix /usr/local/lib/nemoclaw/openclaw-runtime ci \
--ignore-scripts --omit=dev --no-audit --no-fund --no-progress \
--userconfig /dev/null --registry https://registry.npmjs.org/; \
node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
--verify-installed-lock --lock-sha256 "$OPENCLAW_LOCK_SHA256" \
--lockfile /usr/local/lib/nemoclaw/openclaw-runtime/package-lock.json \
--install-root /usr/local/lib/nemoclaw/openclaw-runtime \
--label "OpenClaw ${OPENCLAW_VERSION}"; \
node /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs; \
mkdir -p /usr/local/lib/node_modules; \
ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/openclaw /usr/local/lib/node_modules/openclaw; \
ln -s /usr/local/lib/nemoclaw/openclaw-runtime/node_modules/.bin/openclaw /usr/local/bin/openclaw; \
else \
OPENCLAW_SOURCE_PACK_PATH="$(node --experimental-strip-types /scripts/lib/reviewed-npm-archive.mts \
--package-spec "openclaw@${OPENCLAW_VERSION}" --integrity "$EXPECTED_INTEGRITY" \
--tarball-url "$EXPECTED_TARBALL" --label "OpenClaw ${OPENCLAW_VERSION}")"; \
OPENCLAW_PACK_PATH="$OPENCLAW_SOURCE_PACK_PATH"; \
OPENCLAW_PACK_DIR="$(dirname "$OPENCLAW_PACK_PATH")"; \
if [ "$OPENCLAW_VERSION" = "2026.3.11" ]; then \
OPENCLAW_REMEDIATION_JSON="$(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")"; \
OPENCLAW_PACK_PATH="$(node -e 'const value = JSON.parse(process.argv[1]); if (!value.remediated || typeof value.archivePath !== "string") process.exit(1); process.stdout.write(value.archivePath)' "$OPENCLAW_REMEDIATION_JSON")"; \
fi; \
npm install -g --no-audit --no-fund --no-progress --ignore-scripts "$OPENCLAW_PACK_PATH"; \
case "$OPENCLAW_VERSION" in \
2026.4.24) node /usr/local/lib/node_modules/openclaw/scripts/postinstall-bundled-plugins.mjs ;; \
2026.3.11) ;; \
*) echo "ERROR: OpenClaw ${OPENCLAW_VERSION} has no reviewed lifecycle policy" >&2; exit 1 ;; \
esac; \
rm -rf "$OPENCLAW_PACK_DIR"; \
fi; \
fi; \
case "$OPENCLAW_VERSION" in \
2026.3.11) npm ls -g --depth=1 openclaw tar >/dev/null ;; \
Expand Down
Loading
Loading