Skip to content

Generic subscribe performs zero authorization; §7.6 job.subscribe types are unimplemented (§7.6, §14) #92

@nficano

Description

@nficano

Category: spec-conformance Severity: major
Location: Sources/ARCP/Envelope/MessageType.swift:65-70, Sources/ARCP/Runtime/ARCPRuntime.swift:329-336, Sources/ARCP/Runtime/SubscriptionManager.swift:75-86
Spec: ARCP v1.1 §7.6, §14 ("Subscription scope")

What

The spec's §7.6 subscription is a per-job attach (job.subscribe with job_id, from_event_seq, history; responds job.subscribed; MUST verify the principal may observe the target job, else PERMISSION_DENIED). The SDK has no job.subscribe/job.subscribed/job.unsubscribe types at all (MessageType only has the generic subscribe/subscribe.event). The generic subscribe handler (handleSubscribe) performs zero authorization: any authenticated session can subscribe with an empty filter and receive every envelope of every job of every principal flowing out of the runtime. This is the exact privilege-escalation vector §14 says runtimes MUST default-deny. Existing issue #69 mentions "job.subscribe performs no principal authorization" but the actual code has no job.subscribe path — the live generic subscribe is the unauthorized surface and is broader than #69 describes.

Evidence

case .subscribe(let payload):
    try await handleSubscribe(envelope: envelope, payload: payload, info: info, transport: transport)

handleSubscribe calls subscriptionManager.subscribe directly with the client filter — no principal check, no ownerSessionId-vs-target comparison. SubscriptionRecord.matches filters only on session/trace/job/stream/type/priority, never on the subscriber's authorization to see those sessions.

Proposed fix

  1. In handleSubscribe, enforce default "same principal only": reject (or silently scope) any filter whose sessionIds includes a session not owned by info.principal.subject. Without a session filter, restrict matching to the subscriber's own sessions unless deployment policy explicitly opts into cross-principal observation.
  2. Log every subscribe with subscriber principal, requested scope, and decision (§14 "Cross-session subscription audit").
  3. If §7.6 job.subscribe is in scope, add the message types and a handler that returns PERMISSION_DENIED for unauthorized principals.

Acceptance criteria

  • A session cannot receive envelopes for sessions/jobs owned by another principal by default.
  • Subscribe attempts are audit-logged with the policy decision.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions