Authorization evaluated on a partial request body
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ả
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 currentmain(code path unchanged). - Not affected:
v0.4.0— the identical first request returns200.
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:
createCatalog→ 200createSchema→ 200createTable(EXTERNAL, withstorage_location) as the first table op → 500 (the NPE above)- 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 emptyresourceIdsmap;UnityAccessEvaluator.evaluate(...)binds no#catalog/#schema/#external_location
SpEL variables, so they evaluate tonull;- the expression calls
#authorizeAny(#principal, #catalog, ...)(or#external_location)
with anullUUID; JCasbinAuthorizer.authorizeAny(...)callsresource.toString()on the null → NPE,
surfaced asINTERNAL/ SpELEL1023E.
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
INTERNAL500 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
- Có Dockerfile hoặc tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của unitycatalog/unitycatalog
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
unitycatalog/unitycatalog#2025 ·
Maintainer thường phản hồi trong vòng 4 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
unitycatalog/unitycatalog#1744 ·
Maintainer thường phản hồi trong vòng 4 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
unitycatalog/unitycatalog#1679 ·
Maintainer thường phản hồi trong vòng 4 ngày
-
Flaky python-client-tests CI: coursier OverlappingFileLockException during sbt updateCó thể đã có người làm @niruta25 đã nhận 187 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
unitycatalog/unitycatalog#1496 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 4 ngày
-
Remove the duplicate UC properties setting for both `key` and `option.key`.Có thể đã có người làm @niruta25 đã nhận 206 ngày trước. Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
unitycatalog/unitycatalog#1356 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 4 ngày
Tất cả issue của unitycatalog/unitycatalog
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
checkstyle/checkstyle#22153 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Documentation Needs Triage
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 78/100
frankframework/frankframework#11908 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
ome/ome-codecs#48 ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 90/100
tonihele/OpenKeeper#672 ·
Maintainer thường phản hồi trong vòng 4 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
eclipse-ee4j/angus-mail#224 ·