feat(java): add resource proxies with signed references#349
Conversation
Pass non-serializable resources by reference: deconstruct a value into a HMAC-signed, serializable ProxyRef on the producer and reconstruct it in the handler. ProxyHandler SPI + a FileProxyHandler with a path allowlist; explicit (no implicit graph-walking) to fit typed payloads.
File round-trip across the wire, tamper + allowlist rejection, unknown handler.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR adds a proxy system for non-serializable resources: a handler contract, signed proxy references, a registry for deconstruction/reconstruction, a file-based handler with path allowlisting, a proxy exception type, package documentation, and tests. ChangesProxy referencing system
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdks/java/src/main/java/org/byteveda/taskito/proxies/FileProxyHandler.java`:
- Around line 23-28: The allowlist check in FileProxyHandler can be bypassed via
symlinked ancestors because the constructor only stores normalized paths and the
containment check later relies on lexical startsWith behavior. Update
FileProxyHandler so the allowed roots and the candidate path’s nearest existing
ancestor are resolved to real filesystem paths before the access check, and make
the check use those resolved values instead of only normalize().startsWith().
Add a regression test around the existing allowlist logic to cover a symlink
escape case where a path under an allowed root resolves outside it.
- Around line 47-50: Reject a null reference map at the start of
FileProxyHandler.reconstruct before accessing "path". Add an explicit null check
for the reference argument and throw ProxyException with the same malformed-ref
handling used for the existing path validation, so a null ProxyRef reference
fails through the intended error path instead of triggering
NullPointerException.
In `@sdks/java/src/main/java/org/byteveda/taskito/proxies/Proxies.java`:
- Around line 41-48: Update Proxies.deconstruct(Object value) to handle null
explicitly before iterating handlers or calling value.getClass(). If value is
null, throw a ProxyException with a clear message instead of letting a
NullPointerException escape; keep the existing ProxyRef construction path
unchanged for non-null values. Use the deconstruct method in Proxies as the fix
location.
- Around line 33-36: The Proxies.register(ProxyHandler<?> handler) method
currently accepts null ids and silently overwrites existing handlers in
handlers.put, which can break the ProxyRef/handler contract. Update register to
fail fast by validating handler.id() is non-null and rejecting duplicate ids
before mutating the handlers map, using the existing Proxies and ProxyHandler
symbols so callers get an immediate error instead of silently changing
semantics.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 23b77562-ae80-401a-986d-ff717d613c22
📒 Files selected for processing (7)
sdks/java/src/main/java/org/byteveda/taskito/errors/ProxyException.javasdks/java/src/main/java/org/byteveda/taskito/proxies/FileProxyHandler.javasdks/java/src/main/java/org/byteveda/taskito/proxies/Proxies.javasdks/java/src/main/java/org/byteveda/taskito/proxies/ProxyHandler.javasdks/java/src/main/java/org/byteveda/taskito/proxies/ProxyRef.javasdks/java/src/main/java/org/byteveda/taskito/proxies/package-info.javasdks/java/src/test/java/org/byteveda/taskito/ProxyTest.java
What
Adds
org.byteveda.taskito.proxies— deconstruct a non-serializable resource into a compact, HMAC-signed reference on the producer, then verify and reconstruct it on the worker. Lets a task "carry" a file handle, client, etc. by reference without serializing the object.Proxies— registry built with a shared HMAC key.register(handler)— oneProxyHandlerper resource type (unique id).deconstruct(value)— first handler thathandles(value)produces a reference map; returns aProxyRef{handler, reference, signature}. HMAC-SHA256 overid \n canonical-json(reference)(map keys sorted → stable signature).reconstruct(ref)/resolve(ref)— constant-time signature check (MessageDigest.isEqual) before rebuilding; rejects unknown handler or signature mismatch withProxyException.ProxyHandler<T>SPI,ProxyRefrecord,FileProxyHandler(path reference),ProxyException.Signing prevents a worker from reconstructing a tampered/forged reference. Node's proxy equivalent, adapted to Java.
Test
ProxyTest— round-trip viaFileProxyHandler, signature-mismatch rejection, unknown-handler rejection, no-handler rejection../gradlew buildgreen (JDK 17 build leg + 21/25 test legs).Summary by CodeRabbit
ProxyRefobjects.ProxyHandlersupport plus aProxiesregistry for deconstructing and reconstructing references.FileProxyHandlerwith optional allowlist enforcement and symlink-aware path validation.