Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Authorization evaluated on a partial request body

Đang mở
#1,678 1 bình luận 0 reaction 1 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 4 ngày

@yili-db đang làm issue này rồi.

Từ ngày 18/7/2026.

Đánh giá

Issue này chưa được đánh giá.

Mô tả

bug

Authorization evaluated on a partial request body → authorizeAny NPE (HTTP 500) on the first table/function create after server start

Summary

With server.authorization=enable, the first createTable (EXTERNAL) or createFunction
call after the server starts intermittently fails with an internal NullPointerException
in the authorization layer, then "self-heals" — every subsequent identical call succeeds.

POST /api/2.1/unity-catalog/tables
→ 500 INTERNAL
{"error_code":"INTERNAL",
 "message":"EL1023E: A problem occurred whilst attempting to invoke the function
  'authorizeAny': 'Cannot invoke \"java.util.UUID.toString()\" because \"resource\" is null'"}

The trigger looks like "cold start," but the real cause is that authorization is
evaluated against a partially-received request body
. It is timing/size dependent, which
is why it presents as flaky and self-healing.

Affected versions

  • Reproduced: v0.5.0 (release image) and current main (code path unchanged).
  • Not affected: v0.4.0 — the identical first request returns 200.

Environment

  • Unity Catalog server, server.authorization=enable, valid admin token.
  • Reproduced on Docker (unitycatalog/unitycatalog:v0.5.0), macOS aarch64 / Colima.

Steps to reproduce

On a freshly started auth-enabled server:

  1. createCatalog → 200
  2. createSchema → 200
  3. createTable (EXTERNAL, with storage_location) as the first table op → 500 (the NPE above)
  4. Repeat the exact same createTable → 200 (self-heals)

createFunction behaves the same on its own cold path. Small payloads
(MANAGED table, EXTERNAL volume) tend to pass even cold because they arrive in a single
network read.

A self-contained repro + fix-verification harness (Docker + curl only, no build required)
is available and can be attached to this issue.

Root cause

In UnityAccessDecorator.authorizeByRequest(...), for endpoints whose resources come from
the request body (PAYLOAD locators), authorization is wired into an Armeria
req.peekData(...) callback:

// server/.../auth/decorator/UnityAccessDecorator.java
req = req.peekData(data -> {
  peekDataHandler.processPeekData(data);                       // return value discarded
  Map<SecurableType, UUID> resourceIds = mapResourceKeys(resourceKeys, nonResourceValues);
  evaluateAction.beforeRequest(principal, expression, resourceIds, nonResourceValues);
});

PeekDataHandler.processPeekData(HttpData) accumulates chunks and returns true only once
the complete JSON body has been buffered and parsed (i.e. once resourceKeys /
nonResourceValues are populated). The caller ignores that return value and runs
mapResourceKeys(...) + beforeRequest(...) on every data chunk.

When the request body is delivered in more than one peekData invocation, the first
(partial) chunk leaves resourceKeys empty, so:

  • KeyMapper.mapResourceKeys({}) returns an empty resourceIds map;
  • UnityAccessEvaluator.evaluate(...) binds no #catalog / #schema / #external_location
    SpEL variables, so they evaluate to null;
  • the expression calls #authorizeAny(#principal, #catalog, ...) (or #external_location)
    with a null UUID;
  • JCasbinAuthorizer.authorizeAny(...) calls resource.toString() on the null → NPE,
    surfaced as INTERNAL / SpEL EL1023E.
Evidence

With DEBUG logging on io.unitycatalog.server.auth, the failing first call shows the
buffered payload cut off mid-value and an empty resource map:

DEBUG UnityAccessDecorator - Authorization peekData invoked.
DEBUG UnityAccessDecorator - Payload: {  "name": "t_first", ... "storage_location": "s3://lakehouse-data/ware
DEBUG UnityAccessDecorator - resourceIds = {}

The real storage_location is s3://lakehouse-data/warehouse/t_first; the buffer ends at
...ware, i.e. authorization ran on an incomplete body. This is why it is cold- and
size-dependent (a cold JVM / larger body is more likely to be split across reads) and why
it self-heals once warm.

This also means it is not a persistence/cold-cache issue and not payload-, path-,
or table-type-content dependent.

Impact

  • First privileged create after startup can fail with a confusing INTERNAL 500 under
    normal, authorized usage (breaks provisioning scripts whose first call is a table create).
  • More broadly, the request body being parsed on each chunk means an authorization decision
    can be made against an incomplete view of the request — a correctness/security smell
    that should evaluate exactly once, on the complete payload, and fail closed.

Proposed fix

Only run mapResourceKeys(...) + beforeRequest(...) once the full payload has been
received and parsed (gate on processPeekData(...)), and make completion detection robust
by parsing the accumulated buffer rather than relying on the last byte being }. Parsing
to a generic Map is strictly more permissive than the service's own typed
deserialization, so any body the decorator skips (never a valid JSON object) is also
rejected by the service — no request can bypass authorization as a result. Add a regression
test that drives the handler with a body split across multiple chunks and asserts a partial
chunk is not treated as complete.

I have a fix + regression test ready and am happy to open a PR.

Ngôn ngữ chính
Java
Star
3.6k
Fork
687
Merge trung bình
4 ngày 21 giờ
Pull request đã merge (30 ngày)
48

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của unitycatalog/unitycatalog

Tất cả issue của unitycatalog/unitycatalog

Issue tương tự

Thêm issue về Java

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.