name: "Authorized: mock jev-use" on: workflow_dispatch: inputs: commit_sha: description: "Full 40-character canonical repository commit SHA" required: true trusted_branch: description: "Canonical main or release branch that must contain the SHA" required: true default: "main" permissions: contents: read env: # Cua programs and packages run here never send usage telemetry. CUA_TELEMETRY: "0" DO_NOT_TRACK: "1" jobs: source: name: Validate canonical source runs-on: ubuntu-latest outputs: sha: ${{ steps.validate.outputs.sha }} steps: - id: validate name: Require a canonical trusted-branch commit env: GH_TOKEN: ${{ github.token }} REQUESTED_SHA: ${{ inputs.commit_sha }} TRUSTED_BRANCH: ${{ inputs.trusted_branch }} run: | set -euo pipefail if [[ "$GITHUB_REPOSITORY" != "trycua/cua" ]]; then echo "candidate validation runs only in trycua/cua" >&2 exit 2 fi if [[ ! "$REQUESTED_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then echo "commit_sha must be a full 40-character SHA" >&2 exit 2 fi if [[ ! "$TRUSTED_BRANCH" =~ ^(main|release/[A-Za-z0-9._/-]+)$ ]]; then echo "trusted_branch must be main or a canonical release/* branch" >&2 exit 2 fi branch_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/$TRUSTED_BRANCH" --jq .object.sha)" status="$(gh api "repos/$GITHUB_REPOSITORY/compare/${REQUESTED_SHA,,}...$branch_sha" --jq .status)" if [[ "$status" != "ahead" && "$status" != "identical" ]]; then echo "$REQUESTED_SHA is not contained in $TRUSTED_BRANCH" >&2 exit 2 fi gh api "repos/$GITHUB_REPOSITORY/commits/${REQUESTED_SHA,,}" --jq .sha echo "sha=${REQUESTED_SHA,,}" >> "$GITHUB_OUTPUT" mock: name: Deterministic mock Jev needs: source runs-on: ubuntu-latest timeout-minutes: 45 env: CUA_DRIVER_BIN: ${{ github.workspace }}/libs/cua-driver/rust/target/release/cua-driver CUA_DRIVER_PERMISSION_MODE: unrestricted CUA_DRIVER_DANGEROUSLY_BYPASS_APPROVALS: "1" CUA_E2E_BROWSER_NO_SANDBOX: "1" defaults: run: working-directory: libs/cua-driver/examples/jev-use steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: ref: ${{ needs.source.outputs.sha }} - uses: dtolnay/rust-toolchain@4716b85f2fac3e324e64fa2810f6b5c3905760a5 # 1.97.1 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" - uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 with: version: "0.8.15" - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: "22" - name: Install Linux desktop dependencies working-directory: ${{ github.workspace }} run: | sudo apt-get update sudo apt-get install -y --no-install-recommends \ xvfb dbus-x11 at-spi2-core openbox picom clang pkg-config \ libdbus-1-dev libpipewire-0.3-dev libspa-0.2-dev libei-dev \ libx11-dev libxi-dev libxtst-dev libxext-dev libssl-dev \ libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev chrome_payload="$(readlink -f "$(command -v google-chrome)")" test "$chrome_payload" = /opt/google/chrome/google-chrome test "$(stat -c %u "$chrome_payload")" = 0 test $((8#$(stat -c %a "$chrome_payload") & 0022)) = 0 - name: Build candidate and install locked clients working-directory: ${{ github.workspace }} run: | cargo build --locked --release -p cua-driver --features portal-input --manifest-path libs/cua-driver/rust/Cargo.toml cd libs/cua-driver/examples/jev-use uv sync --frozen npm ci --ignore-scripts echo "Candidate SHA: $(git rev-parse HEAD)" "$CUA_DRIVER_BIN" --version - name: Run bounded mock checks working-directory: ${{ github.workspace }} run: | mock="$RUNNER_TEMP/jev-use-mock-proof" python_bin="$GITHUB_WORKSPACE/libs/cua-driver/examples/jev-use/.venv/bin/python" test -x "$python_bin" # Match the repository's supported Linux X11 desktop stack while # keeping every provider row on the deterministic mock chooser. xvfb-run -a --server-args="-screen 0 1920x1080x24" \ dbus-run-session -- bash -lc \ 'openbox >/tmp/jev-use-openbox.log 2>&1 & picom --backend xrender --config /dev/null >/tmp/jev-use-picom.log 2>&1 & sleep 2; cd libs/cua-driver/examples/jev-use; "'$python_bin'" verify_setup.py --typescript --max-steps 2 --output-dir "'$mock'"' - name: Verify bounded mock chooser CLI run: .venv/bin/python verify_choice_cli.py - name: Audit mock action bound and redact evidence run: | uv run python - "$RUNNER_TEMP/jev-use-mock-proof" <<'PY' import json, pathlib, sys root = pathlib.Path(sys.argv[1]) summary = json.loads((root / "summary.json").read_text()) assert summary["complete"] is True assert summary["live_requested"] is False assert summary["typescript_requested"] is True assert len(summary["checks"]) == 2 mock = [item for item in summary["checks"] if item["provider"] == "mock"] assert len(mock) == 2 and all(item["outcome"] == "verified" for item in mock) mock_steps = 0 for path in root.glob("*-mock.jsonl"): mock_steps += sum(json.loads(line).get("event") == "step" for line in path.read_text().splitlines()) assert mock_steps <= 4, mock_steps forbidden = ("typesafe_api_key", "authorization", "environment", "image_base64", "screenshot") for path in root.iterdir(): if path.name == "summary.json" or path.suffix == ".jsonl": text = path.read_text().lower() assert not any(value in text for value in forbidden), path PY - name: Upload redacted mock metadata if: always() uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4 with: name: jev-use-mock-${{ needs.source.outputs.sha }} path: | ${{ runner.temp }}/jev-use-mock-proof/summary.json ${{ runner.temp }}/jev-use-mock-proof/*.jsonl if-no-files-found: error retention-days: 14