mirror of
https://github.com/openclaw/openclaw.git
synced 2026-09-02 12:36:51 +00:00
* fix(ci): accept plugin fixture capabilities * fix(ci): detect fixture capability consent support Probe candidate command help through the existing bounded E2E runners so positive fixtures accept capabilities only when that command supports it. Preserve historical packages, probe failures, argv, and child exit status; keep negative, no-op, and integrity-bound update coverage unapproved. Replace duplicated probes and source-string checks with executable compatibility regressions. Synchronize the RPC timeout test with real process readiness before advancing its unchanged 100 ms deadline, while retaining real process-group termination and cleanup. Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * fix(ci): run Docker lanes from trusted harness Resolve package scripts and packaging tools from the trusted harness while keeping candidate source, artifacts, registry identity, and preflight on the frozen target. Remove candidate-script lane filtering and the duplicate live command wrapper; preserve source inputs in source-built Docker lanes. Prove the public scheduler boundary with distinct harness/target roots and poisoned candidate scripts. Synchronize scheduler timeout fixtures with real child readiness, retain real signals and kill grace, and handle empty QR build arguments on Bash 3. Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * test(ci): retire superseded harness source assertions Keep the workflow trust and pnpm PATH guards, but remove checks for the old inline replacement and deleted duplicate live-script wrapper. The public scheduler regressions now prove those behaviors through distinct candidate/harness roots and quoted pinned pnpm commands. Co-authored-by: Vincent Koc <vincentkoc@ieee.org> * test(ci): cover multi-build source roots * fix(ci): select the harness toolchain before Docker lanes Start prepared pnpm commands in the trusted harness before Corepack resolves its package-manager pin. Preserve frozen candidate source and artifact identity, and resolve relative executable and cache paths before changing cwd. Keep quoted rerun environment values out of executable substitution. Cover different toolchain pins, shell-sensitive paths, and generated reruns through the public scheduler boundary. Co-authored-by: Vincent Koc <vincentkoc@ieee.org> --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
161 lines
5.1 KiB
Bash
161 lines
5.1 KiB
Bash
#!/usr/bin/env bash
|
|
# Verifies the plugin-owned conversation binding command escape regression in
|
|
# Docker. The focused Vitest cases assert that real authorized commands escape,
|
|
# while unknown or unauthorized slash text stays with the bound plugin.
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
SOURCE_ROOT="${OPENCLAW_DOCKER_E2E_REPO_ROOT:-$ROOT_DIR}"
|
|
source "$ROOT_DIR/scripts/lib/docker-e2e-image.sh"
|
|
|
|
IMAGE_NAME="${OPENCLAW_PLUGIN_BINDING_COMMAND_ESCAPE_E2E_IMAGE:-openclaw-plugin-binding-command-escape-e2e}"
|
|
CONTAINER_NAME="openclaw-plugin-binding-command-escape-e2e-$$"
|
|
DOCKER_RUN_TIMEOUT="${OPENCLAW_PLUGIN_BINDING_COMMAND_ESCAPE_DOCKER_RUN_TIMEOUT:-900s}"
|
|
RUN_LOG="$(mktemp -t openclaw-plugin-binding-command-escape-log.XXXXXX)"
|
|
FOCUSED_TEST_REGEX="lets authorized gateway-style plugin commands escape plugin-owned bindings|keeps authorized unknown slash text in a plugin-owned binding routed to the bound plugin|keeps unauthorized plugin-owned binding slash replies suppressed while routed to the bound plugin"
|
|
|
|
cleanup() {
|
|
docker_e2e_docker_cmd rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
rm -f "$RUN_LOG"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
docker_e2e_build_or_reuse \
|
|
"$IMAGE_NAME" \
|
|
plugin-binding-command-escape \
|
|
"$ROOT_DIR/scripts/e2e/plugin-binding-command-escape.Dockerfile" \
|
|
"$SOURCE_ROOT"
|
|
|
|
echo "Running plugin binding command escape Docker E2E..."
|
|
set +e
|
|
DOCKER_COMMAND_TIMEOUT="$DOCKER_RUN_TIMEOUT" docker_e2e_docker_run_cmd run --rm \
|
|
--name "$CONTAINER_NAME" \
|
|
-e COREPACK_ENABLE_DOWNLOAD_PROMPT=0 \
|
|
-e "FOCUSED_TEST_REGEX=$FOCUSED_TEST_REGEX" \
|
|
-e OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/tmp/openclaw-vitest-cache \
|
|
"$IMAGE_NAME" \
|
|
bash -lc '
|
|
set -euo pipefail
|
|
# Main has one aggregate entry; frozen candidates may split binding cases into a second file.
|
|
test_files=(src/auto-reply/reply/dispatch-from-config.test.ts)
|
|
if [[ -f src/auto-reply/reply/dispatch-from-config.lifecycle.test.ts ]]; then
|
|
test_files=(
|
|
src/auto-reply/reply/dispatch-from-config.delivery.test.ts
|
|
src/auto-reply/reply/dispatch-from-config.lifecycle.test.ts
|
|
src/auto-reply/reply/dispatch-from-config.test.ts
|
|
)
|
|
fi
|
|
corepack enable
|
|
node scripts/run-vitest.mjs "${test_files[@]}" --reporter=verbose -t "$FOCUSED_TEST_REGEX"
|
|
' \
|
|
>"$RUN_LOG" 2>&1
|
|
status=$?
|
|
set -e
|
|
|
|
if [ "$status" -ne 0 ]; then
|
|
echo "Docker plugin binding command escape smoke failed"
|
|
docker_e2e_print_log "$RUN_LOG"
|
|
exit "$status"
|
|
fi
|
|
|
|
if ! node - "$RUN_LOG" <<'NODE'
|
|
const fs = require("node:fs");
|
|
const { StringDecoder } = require("node:string_decoder");
|
|
const logPath = process.argv[2];
|
|
const scanBytes = 65536;
|
|
const maxLineChars = 4096;
|
|
const stat = fs.statSync(logPath);
|
|
const length = Math.min(stat.size, scanBytes);
|
|
const buffer = Buffer.alloc(scanBytes);
|
|
const fd = fs.openSync(logPath, "r");
|
|
const decoder = new StringDecoder("utf8");
|
|
let diagnosticTail = "";
|
|
let carry = "";
|
|
let discardingLongLine = false;
|
|
let invalidSummary = false;
|
|
let summaryCount = 0;
|
|
let totalPassed = 0;
|
|
let offset = 0;
|
|
|
|
function scanLine(line) {
|
|
const normalized = line.replace(/\x1B\[[0-?]*[ -/]*[@-~]/gu, "");
|
|
const match = normalized.match(/^\s*Tests\s+(\d+) passed\b/u);
|
|
if (!match) {
|
|
return;
|
|
}
|
|
const count = Number.parseInt(match[1], 10);
|
|
summaryCount += 1;
|
|
if (!Number.isSafeInteger(count) || count <= 0 || summaryCount > 2) {
|
|
invalidSummary = true;
|
|
return;
|
|
}
|
|
totalPassed += count;
|
|
}
|
|
|
|
function appendLineSegment(segment, ended) {
|
|
if (!discardingLongLine) {
|
|
if (carry.length + segment.length <= maxLineChars) {
|
|
carry += segment;
|
|
} else {
|
|
carry = "";
|
|
discardingLongLine = true;
|
|
}
|
|
}
|
|
if (!ended) {
|
|
return;
|
|
}
|
|
if (!discardingLongLine) {
|
|
scanLine(carry.endsWith("\r") ? carry.slice(0, -1) : carry);
|
|
}
|
|
carry = "";
|
|
discardingLongLine = false;
|
|
}
|
|
|
|
function scanText(text) {
|
|
let start = 0;
|
|
for (let newline = text.indexOf("\n"); newline !== -1; newline = text.indexOf("\n", start)) {
|
|
appendLineSegment(text.slice(start, newline), true);
|
|
start = newline + 1;
|
|
}
|
|
appendLineSegment(text.slice(start), false);
|
|
}
|
|
|
|
function isInvalidProof() {
|
|
return invalidSummary || summaryCount < 1 || summaryCount > 2 || totalPassed !== 3;
|
|
}
|
|
|
|
try {
|
|
while (true) {
|
|
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, offset);
|
|
if (bytesRead === 0) {
|
|
break;
|
|
}
|
|
offset += bytesRead;
|
|
scanText(decoder.write(buffer.subarray(0, bytesRead)));
|
|
}
|
|
scanText(decoder.end());
|
|
appendLineSegment("", true);
|
|
if (isInvalidProof() && length > 0) {
|
|
const bytesRead = fs.readSync(fd, buffer, 0, length, stat.size - length);
|
|
diagnosticTail = buffer.subarray(0, bytesRead).toString("utf8");
|
|
}
|
|
} finally {
|
|
fs.closeSync(fd);
|
|
}
|
|
|
|
if (isInvalidProof()) {
|
|
console.error("expected focused Vitest summary for exactly 3 passed tests");
|
|
console.error(
|
|
`saw ${summaryCount} summaries totaling ${totalPassed}; expected one aggregate or two split summaries`,
|
|
);
|
|
console.error(diagnosticTail);
|
|
process.exit(1);
|
|
}
|
|
NODE
|
|
then
|
|
echo "Docker plugin binding command escape smoke did not stay focused"
|
|
docker_e2e_print_log "$RUN_LOG"
|
|
exit 1
|
|
fi
|
|
|
|
echo "OK (3 focused tests)"
|