Category: spec-conformance Severity: minor
Location: src/Arcp.Runtime/JobManager.cs:444-455, src/Arcp.Runtime/SessionState.Dispatch.cs:85-91
Spec: ARCP v1.1 §7.6, §13.3, §14
What
Spec §7.6/§13.3 state cancellation is reserved for "the session that submitted the job" ("Only sess_A1 may cancel it"), and §14 says "Subscription MUST NOT confer cancel authority." Cancel only compares the principal subject, not the submitting session. A second session of the same principal (e.g. a dashboard that subscribed) can therefore cancel a job it did not submit. Additionally, the guard is skipped entirely when either principal is null (requesterPrincipal is not null && job.SubmitterPrincipal is not null), so a null-subject requester bypasses the check and cancels any job.
Evidence
public bool Cancel(JobId jobId, string? requesterPrincipal, string? reason)
{
if (!_jobs.TryGetValue(jobId, out var job)) return false;
if (requesterPrincipal is not null && job.SubmitterPrincipal is not null &&
!string.Equals(requesterPrincipal, job.SubmitterPrincipal, StringComparison.Ordinal))
{
throw new PermissionDeniedException("Subscribers MUST NOT cancel jobs (spec §7.6)");
}
job.CancellationSource.Cancel();
return true;
}
The dispatcher passes Principal?.Subject (a principal), never the submitting SessionId (SessionState.Dispatch.cs:89).
Proposed fix
- Pass the requesting
SessionId into Cancel and compare it to job.SessionId (the submitting session) — reject with PERMISSION_DENIED when they differ, regardless of principal.
- Treat a null/unknown requester as unauthorized (fail closed) rather than skipping the check.
Acceptance criteria
Category: spec-conformance Severity: minor
Location:
src/Arcp.Runtime/JobManager.cs:444-455,src/Arcp.Runtime/SessionState.Dispatch.cs:85-91Spec: ARCP v1.1 §7.6, §13.3, §14
What
Spec §7.6/§13.3 state cancellation is reserved for "the session that submitted the job" ("Only
sess_A1may cancel it"), and §14 says "Subscription MUST NOT confer cancel authority."Cancelonly compares the principal subject, not the submitting session. A second session of the same principal (e.g. a dashboard that subscribed) can therefore cancel a job it did not submit. Additionally, the guard is skipped entirely when either principal is null (requesterPrincipal is not null && job.SubmitterPrincipal is not null), so a null-subject requester bypasses the check and cancels any job.Evidence
The dispatcher passes
Principal?.Subject(a principal), never the submittingSessionId(SessionState.Dispatch.cs:89).Proposed fix
SessionIdintoCanceland compare it tojob.SessionId(the submitting session) — reject withPERMISSION_DENIEDwhen they differ, regardless of principal.Acceptance criteria
PERMISSION_DENIED.