Skip to content

Commit 20a64a6

Browse files
author
Shaw
committed
Merge branch 'pr-7891' into develop
PR #7891: harden Steward checkout callback Conflict resolutions: - gitleaks.yml: took pr-7891 env-var/RANGE_ARGS pattern, dropped dangling --log-opts - dev-route-catalog.ts: kept HEAD Hidden/requiresAuth=true (newer) - build-llama-cpp-dflash-targets.test.mjs: kept HEAD darwin-arm64-metal-fused assertion - android-bridge.test.ts: kept HEAD test description - _router.generated.ts: regenerated (560 routes) Post-merge fixes: - steward-nonce-exchange/route.ts: removed unconditional token/refreshToken override that bypassed shouldReturnClientToken origin gate (Greptile P1).
2 parents 849b4f1 + 4309790 commit 20a64a6

22 files changed

Lines changed: 208 additions & 57 deletions

File tree

.github/workflows/cloud-e2e.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ jobs:
5252
run: bun run cloud:e2e
5353
env:
5454
CI: "true"
55+
CLOUD_E2E: "1"
5556
# mocks read these
5657
MOCK_REDIS: "1"
5758
MOCK_HETZNER_LATENCY: "0"

.github/workflows/gitleaks.yml

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,30 @@ jobs:
3939
gitleaks version
4040
4141
- name: Run gitleaks
42-
# Scan only the PR/push commit range. The checkout still fetches full
43-
# history above so these ranges resolve, but pre-existing historical
44-
# findings do not block unrelated source-only PRs.
42+
env:
43+
EVENT_NAME: ${{ github.event_name }}
44+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
45+
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
46+
BEFORE_SHA: ${{ github.event.before }}
47+
CURRENT_SHA: ${{ github.sha }}
4548
run: |
4649
set -euo pipefail
47-
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
48-
log_opts="${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}"
49-
else
50-
before="${{ github.event.before }}"
51-
if [[ "$before" =~ ^0+$ ]]; then
52-
log_opts="${{ github.sha }}^..${{ github.sha }}"
53-
else
54-
log_opts="${before}..${{ github.sha }}"
55-
fi
50+
RANGE_ARGS=()
51+
if [[ "$EVENT_NAME" == "pull_request" ]]; then
52+
RANGE_ARGS=(--log-opts "${PR_BASE_SHA}..${PR_HEAD_SHA}")
53+
elif [[ -n "$BEFORE_SHA" && "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]]; then
54+
RANGE_ARGS=(--log-opts "${BEFORE_SHA}..${CURRENT_SHA}")
5655
fi
57-
echo "Scanning gitleaks range: ${log_opts}"
56+
5857
gitleaks detect \
5958
--config .gitleaks.toml \
6059
--source . \
61-
--log-opts "$log_opts" \
6260
--verbose \
6361
--redact \
6462
--report-format sarif \
6563
--report-path gitleaks.sarif \
66-
--no-banner
64+
--no-banner \
65+
"${RANGE_ARGS[@]}"
6766
6867
- name: Upload SARIF
6968
if: always()

packages/app-core/scripts/build-llama-cpp-dflash-targets.test.mjs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ test("unsupported mobile fused targets fail closed with explicit diagnostics", (
3030
];
3131

3232
for (const [target, pattern] of cases) {
33-
assert.throws(() => parseArgs(["--target", target, "--dry-run"]), (err) => {
34-
const message = err instanceof Error ? err.message : String(err);
35-
assert.match(message, pattern, message);
36-
assert.match(message, new RegExp(target), message);
37-
return true;
38-
});
33+
assert.throws(
34+
() => parseArgs(["--target", target, "--dry-run"]),
35+
(err) => {
36+
const message = err instanceof Error ? err.message : String(err);
37+
assert.match(message, pattern, message);
38+
assert.match(message, new RegExp(target), message);
39+
return true;
40+
},
41+
);
3942
}
4043
});

packages/app-core/scripts/pack-upstreams.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ const SEED_TARGETS = [
5555
label: "@elizaos/skills",
5656
dir: path.join(ELIZA_ROOT, "packages", "skills"),
5757
},
58+
{
59+
label: "@elizaos/security",
60+
dir: path.join(ELIZA_ROOT, "packages", "security"),
61+
},
5862
{
5963
label: "@elizaos/app-core",
6064
dir: path.join(ELIZA_ROOT, "packages", "app-core"),

packages/cloud-api/auth/steward-nonce-exchange/route.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99
* 3. This route forwards to Steward `POST /auth/oauth/exchange`, which
1010
* consumes the code and returns `{ token, refreshToken, expiresAt }`.
1111
* 4. We verify the JWT (same path as `/api/auth/steward-session`), sync the
12-
* user, set the HttpOnly cookies, and return `{ ok, userId }`.
12+
* user, set the HttpOnly cookies, and return `{ ok, userId }`. The
13+
* elizaos.ai hardware checkout origin also receives the access token so
14+
* its cross-site Stripe checkout POST can use Bearer auth; refresh stays
15+
* cookie-only.
1316
*
14-
* The access and refresh tokens never enter the browser process at any point.
17+
* The refresh token never enters the browser process; the access token is
18+
* returned only to the hardware checkout origin that still has to authenticate
19+
* a cross-site Stripe checkout request with Bearer auth.
1520
*
1621
* Origin/Referer CSRF check mirrors `/api/auth/steward-session` exactly — the
1722
* route is callable from `*.elizacloud.ai` and `elizaos.ai` only (plus
@@ -99,6 +104,16 @@ function checkOrigin(
99104
};
100105
}
101106

107+
function shouldReturnClientToken(
108+
c: { req: { header: (name: string) => string | undefined } },
109+
isProduction: boolean,
110+
): boolean {
111+
const origin =
112+
originHost(c.req.header("origin")) ?? originHost(c.req.header("referer"));
113+
if (origin === "elizaos.ai" || origin === "www.elizaos.ai") return true;
114+
return !isProduction && origin !== null && LOCAL_DEV_ORIGIN_HOSTS.has(origin);
115+
}
116+
102117
// ─── Helpers ──────────────────────────────────────────────────────────────
103118

104119
function stewardSecretConfigured(env: StewardVerifyEnv): boolean {
@@ -396,8 +411,7 @@ app.post("/", async (c) => {
396411
stewardUserId: claims.userId,
397412
expiresAt: exchange.data.expiresAt,
398413
expiresIn: exchange.data.expiresIn,
399-
token,
400-
refreshToken,
414+
...(shouldReturnClientToken(c, isProduction) ? { token, refreshToken } : {}),
401415
});
402416
});
403417

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Hono } from "hono";
2+
3+
import {
4+
RateLimitPresets,
5+
rateLimit,
6+
} from "@/lib/middleware/rate-limit-hono-cloudflare";
7+
import { directWalletPaymentsService } from "@/lib/services/direct-wallet-payments";
8+
import type { AppEnv } from "@/types/cloud-worker-env";
9+
10+
const app = new Hono<AppEnv>();
11+
12+
app.get("/", rateLimit(RateLimitPresets.STANDARD), (c) => {
13+
return c.json(directWalletPaymentsService.getConfig(c.env));
14+
});
15+
16+
export default app;

packages/cloud-api/src/_router.generated.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* AUTO-GENERATED by src/_generate-router.mjs - do not edit by hand.
33
* Re-run `bun run codegen` after adding or removing a route.ts file.
44
*
5-
* 559 routes mounted, 0 skipped (still Next-shaped).
5+
* 560 routes mounted, 0 skipped (still Next-shaped).
66
*/
77

88
/* eslint-disable */
@@ -77,6 +77,7 @@ import _route_cron_process_stripe_queue_route from "../cron/process-stripe-queue
7777
import _route_cron_release_pending_earnings_route from "../cron/release-pending-earnings/route";
7878
import _route_cron_sample_eliza_price_route from "../cron/sample-eliza-price/route";
7979
import _route_cron_social_automation_route from "../cron/social-automation/route";
80+
import _route_crypto_direct_payments_config_route from "../crypto/direct-payments/config/route";
8081
import _route_crypto_direct_payments_p_id_attach_tx_route from "../crypto/direct-payments/[id]/attach-tx/route";
8182
import _route_crypto_direct_payments_p_id_confirm_route from "../crypto/direct-payments/[id]/confirm/route";
8283
import _route_crypto_direct_payments_p_id_route from "../crypto/direct-payments/[id]/route";
@@ -722,6 +723,10 @@ export function mountRoutes(app: Hono<AppEnv>): void {
722723
_route_cron_sample_eliza_price_route,
723724
);
724725
app.route("/api/cron/social-automation", _route_cron_social_automation_route);
726+
app.route(
727+
"/api/crypto/direct-payments/config",
728+
_route_crypto_direct_payments_config_route,
729+
);
725730
app.route(
726731
"/api/crypto/direct-payments/:id/attach-tx",
727732
_route_crypto_direct_payments_p_id_attach_tx_route,

packages/cloud-frontend/src/dashboard/security/permissions/_components/plugin-permissions-page-client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
useSetPageHeader,
77
} from "@elizaos/ui";
88
import { Puzzle } from "lucide-react";
9-
import { useCallback, useEffect, useState } from "react";
9+
import { useEffect, useState } from "react";
1010
import { toast } from "sonner";
1111
import { ApiError, api, apiFetch } from "@/lib/api-client";
1212
import { emitAuditEvent } from "@/lib/security/audit-client";

packages/cloud-shared/src/lib/services/__tests__/direct-wallet-payments.integration.test.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,13 @@ if (SUPPORTS_VITEST_MOCK_API)
161161
};
162162
});
163163

164-
// Solana — we don't test the Solana confirm path through verify (would need a
165-
// huge mock of getParsedTransaction + ATA owner check). The Solana createPayment
166-
// is exercised separately though, so we still need these imports to resolve.
167-
// Mutable state read by the spl-token / Connection mocks to flip behavior per
168-
// test. Hoisted so `vi.mock(...)` factories — which run before module init —
169-
// can capture a reference.
170-
const solanaTestState = vi.hoisted(() => ({
164+
const createSolanaTestState = () => ({
171165
ataOwnerOverride: null as Uint8Array | null,
172166
parsedTxOverride: null as unknown,
173-
}));
167+
});
168+
169+
const solanaTestState =
170+
typeof vi.hoisted === "function" ? vi.hoisted(createSolanaTestState) : createSolanaTestState();
174171

175172
if (SUPPORTS_VITEST_MOCK_API)
176173
vi.mock("@solana/spl-token", async () => {

packages/core/src/__tests__/service-type-collisions.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@ const serviceTypeValuesByMember = new Map(
5050
);
5151

5252
const duplicateServiceTypeAllowlist = new Map<string, AllowlistEntry>([
53+
[
54+
"capability-router",
55+
{
56+
reason:
57+
"Runtime capability routing keeps local, remote, and E2B implementations in one capability-router discovery slot while the strategy-table migration is in progress.",
58+
classes: new Set([
59+
"packages/core/src/services/runtime-capability-service.ts:RuntimeCapabilityService",
60+
"packages/agent/src/services/e2b-capability-router.ts:E2BRemoteCapabilityRouterService",
61+
"packages/agent/src/services/remote-capability-router.ts:RemoteCapabilityRouterService",
62+
]),
63+
},
64+
],
65+
[
66+
"xr-session",
67+
{
68+
reason:
69+
"Hearwear and XR plugins expose the same XR session service contract for different runtime surfaces; only the enabled plugin registers its implementation.",
70+
classes: new Set([
71+
"plugins/plugin-hearwear/src/services/xr-session-service.ts:XRSessionService",
72+
"plugins/plugin-xr/src/services/xr-session-service.ts:XRSessionService",
73+
]),
74+
},
75+
],
5376
[
5477
"trajectories",
5578
{

0 commit comments

Comments
 (0)