Pin the authorization 403 that precedes the blob-upload dispatcher 405

The dispatcher answers 405 UNSUPPORTED for every verb on both OCI blob-upload routes of a remote repository. The authorization route table is narrower: it maps only the method each upload action serves, so five method-and-route pairs get a bare 403 before resolution — on every repository kind — and never reach the dispatcher wherever the authorization middleware is wired.

Route Method Answered by
.../blobs/uploads/ PATCH, PUT, GET, DELETE authorization middleware, 403
.../blobs/uploads/<id> POST authorization middleware, 403

The precedence is now written down in docs/specs/S16-container-remote.md, under ### Gates ahead of the kind branch, and the blob-upload row in ## Error Cases points at it. Nothing pins it.

What to add

One wrapped-handler test per pair — the authorization middleware composed around the dispatcher — asserting the composed answer is 403, not the 405 the dispatcher writes on its own. Five cases, one per row above.

Why it matters

Documented-but-untested precedence is invisible to the suite. Widening the authorization route table so those pairs map to an action, or a change to how the middleware is wired, would ship the dispatcher's 405 on a deployed instance with every existing test still green — the dispatcher tests exercise the handler directly, so they cannot see what the middleware answers in front of it.

Pointers

  • internal/authz/oci.goblobUploadInitiateAction and blobUploadSessionAction are the two method maps that leave the gap; the comment above them explains why the table is deliberately not widened (the same five pairs already answer 403 on a hosted repository, where the dispatcher 501s them, so widening moves a surface older than the spec).
  • internal/format/oci/upload.go — the dispatcher whose 405 the test composes the middleware around.

Why it was deferred

Raised in review on !1390 (merged). The spec half of the ask landed there; the test needs a cross-package composed harness that does not exist yet, on an MR already past the size gate, so it comes out separately.