feat: add authenticated iOS background presence beacon#73330
Conversation
PR SummaryMedium Risk Overview Gateway handling for
Reviewed by Cursor Bugbot for commit d5c1900. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR adds an authenticated Confidence Score: 4/5Safe to merge; all findings are P2 quality/edge-case concerns with no correctness impact under normal load. No P0 or P1 issues found. Three P2 concerns: a benign race condition between throttle check and map update, a duplicate enum definition that could drift, and a pruning path that could evict still-fresh throttle entries under extreme scale. None affect correctness in normal deployments. src/gateway/server-node-events.ts (throttle race + pruning logic), src/gateway/protocol/schema/nodes.ts (duplicate enum)
|
| export const NodePresenceAliveReasonSchema = Type.String({ | ||
| enum: [ | ||
| "background", | ||
| "silent_push", | ||
| "bg_app_refresh", | ||
| "significant_location", | ||
| "manual", | ||
| "connect", | ||
| ], | ||
| }); |
There was a problem hiding this comment.
Presence-reason enum duplicated across two modules
NodePresenceAliveReasonSchema here defines the closed set of six reasons inline, and NODE_PRESENCE_ALIVE_REASONS in src/shared/node-presence.ts defines the same list independently. Adding a new trigger (e.g. "foreground_shortcut") to node-presence.ts but forgetting this schema (or vice-versa) will silently create a split: the runtime normalizer in normalizeNodePresenceAliveReason will accept it, but validateNodePresenceAlivePayload will reject it. Deriving one from the other (e.g. enum: [...NODE_PRESENCE_ALIVE_REASONS]) would keep them in sync.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/gateway/protocol/schema/nodes.ts
Line: 12-21
Comment:
**Presence-reason enum duplicated across two modules**
`NodePresenceAliveReasonSchema` here defines the closed set of six reasons inline, and `NODE_PRESENCE_ALIVE_REASONS` in `src/shared/node-presence.ts` defines the same list independently. Adding a new trigger (e.g. `"foreground_shortcut"`) to `node-presence.ts` but forgetting this schema (or vice-versa) will silently create a split: the runtime normalizer in `normalizeNodePresenceAliveReason` will accept it, but `validateNodePresenceAlivePayload` will reject it. Deriving one from the other (e.g. `enum: [...NODE_PRESENCE_ALIVE_REASONS]`) would keep them in sync.
How can I resolve this? If you propose a fix, please make it concise.Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com>
aeb3437 to
23ef816
Compare
🔒 Aisle Security AnalysisWe found 1 potential security issue(s) in this PR:
1. 🟡 Potential DoS via oversized JSON payload in node presence alive event
DescriptionThe
Vulnerable code: case NODE_PRESENCE_ALIVE_EVENT: {
const obj = parsePayloadObject(evt.payloadJSON);
...
}
function parsePayloadObject(payloadJSON?: string | null): Record<string, unknown> | null {
...
payload = JSON.parse(payloadJSON) as unknown;
...
}RecommendationAdd a strict maximum size for presence payloads before attempting to parse, and/or enforce it at schema validation. Example (handler-level guard): const MAX_PRESENCE_PAYLOAD_BYTES = 4 * 1024; // presence should be tiny
case NODE_PRESENCE_ALIVE_EVENT: {
if (!evt.payloadJSON || Buffer.byteLength(evt.payloadJSON, "utf8") > MAX_PRESENCE_PAYLOAD_BYTES) {
return { ok: true, event: evt.event, handled: false, reason: "payload_too_large" };
}
const obj = parsePayloadObject(evt.payloadJSON);
...
}Optionally also constrain Analyzed PR: #73330 at commit Last updated on: 2026-04-28T06:48:13Z |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d5c1900. Configure here.
| updatePairedDeviceMetadataMock.mockResolvedValue(true); | ||
| updatePairedNodeMetadataMock.mockClear(); | ||
| updatePairedNodeMetadataMock.mockResolvedValue(true); | ||
| }); |
There was a problem hiding this comment.
Duplicate beforeEach block in same describe scope
Low Severity
A second beforeEach block at line 1018 duplicates a strict subset of the first beforeEach at line 186 within the same describe scope. In vitest, both hooks run before every test regardless of declaration order, so resetNodeEventDeduplicationForTests, updatePairedDeviceMetadataMock, and updatePairedNodeMetadataMock are cleared and re-initialized twice per test. The second block is entirely redundant.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d5c1900. Configure here.
|
Addressed the background wake review finding.
Verification:
Current exact-SHA CI: |
* feat: add iOS background presence beacon Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com> * fix: keep iOS background reconnects ahead of beacon throttle * build: refresh gateway protocol swift models * fix: emit swift protocol string enums --------- Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com>
* feat: add iOS background presence beacon Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com> * fix: keep iOS background reconnects ahead of beacon throttle * build: refresh gateway protocol swift models * fix: emit swift protocol string enums --------- Co-authored-by: ngutman <1540134+ngutman@users.noreply.github.com>


Summary
node.presence.aliveover existingnode.eventfor iOS background wakeslastSeenAtMs/lastSeenReasoninnode.list, update protocol docs, and add regression coverageCarries forward the useful behavior from #63123 with a tighter protocol/persistence boundary. Thanks @ngutman.
Tests
pnpm test src/gateway/server-node-events.test.ts src/gateway/node-catalog.test.ts src/infra/node-pairing.test.ts src/infra/device-pairing.test.ts src/gateway/protocol/index.test.tspnpm lint:swiftxcodebuild build -project apps/ios/OpenClaw.xcodeproj -scheme OpenClaw -destination 'platform=iOS Simulator,name=iPhone 17'xcodebuild test -project apps/ios/OpenClaw.xcodeproj -scheme OpenClaw -destination 'platform=iOS Simulator,name=iPhone 17' -only-testing:OpenClawTests/BackgroundAliveBeaconTestsran the focused Swift Testing suite: 5/5 passed; local xcodebuild hung during simulator teardown after reporting success, so it was killedpnpm tsgo:core && pnpm tsgo:core:test && pnpm lint:core && pnpm check:runtime-sidecar-loaders && pnpm check:import-cycles && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scopeNote: Testbox
pnpm check:changedreachedlint:appsand stopped because the Linux image has noswiftlint; localpnpm lint:swiftcovered the app lint lane.