Repository navigation
Conversation
bb224c9 to
6d43ff6
Compare
ehsavoie
left a comment
There was a problem hiding this comment.
Medium Severity
-
AgentCardOperation.java:12-16 — Latent NPE from nullable capabilityFailure
capabilityFailure is @nullable but AgentCardRequestPolicy.validateCapability calls Objects.requireNonNull() on it. Any caller constructing new AgentCardOperation(predicate, null) will NPE at runtime. Only safe today because AgentCardOperation.none() pairs null with an always-true predicate. Add constructor validation or a typed factory. -
DefaultRequestHandler.java:1273 — isBlank() vs isNullOrEmpty() inconsistency
validateRequiredId uses id.isBlank() while authorizeTaskAccess uses Assert.isNullOrEmpty() (which accepts whitespace). A whitespace-only ID passes one guard but fails the other — inconsistent behavior within the same class. Unify on one check. -
RestHandler_v0_3.java (8+ sites) — Redundant isNullOrEmpty + checkNotNullParam pattern
Since isNullOrEmpty returning false already guarantees non-null, the follow-up checkNotNullParam call is only there for NullAway. Extract a requireNonEmpty helper or use @SuppressWarnings. -
JSONRPCHandler.java:729 — Dead TenantNotFoundException catch block
preflight() at line 745-751 already catches TenantNotFoundException and wraps it as InvalidParamsError. resolveExtendedDiscoveryCard returns null, never throws it. Branch is unreachable and misleading.
Low Severity
-
RestHandler_v0_3.java:228-237 — Negative historyLength returns 500 instead of 422
Integer.parseInt("-1") succeeds with no bounds check — negative historyLength escapes format parsing and causes a 500 InternalError downstream instead of a 422 InvalidParamsError. Add historyLength < 0 → InvalidParamsError_v0_3. -
GrpcHandler.java:743,750 — Per-request allocation of policy objects
AgentCardResolver and AgentCardRequestPolicy are allocated on every request via createAgentCardResolver(). JSONRPCHandler and RestHandler create them once in @PostConstruct. Cache in a field or use lazy init. -
AgentCardResolver.java:31-32 — Tenant normalized twice
AgentCardRequestPolicy.normalizeTenant() normalizes before calling resolveEffectiveCard(), which internally calls normalizeTenantForResolution() again. Idempotent today, maintenance trap tomorrow. -
JSONRPCRequestDecoder_v0_3.java:109-111 — Null body relies on undocumented Gson behavior
Null body rejection relies on JsonParser.parseString(null) throwing rather than an explicit null check. Fragile across Gson version upgrades. -
A2AServerRoutesTest.java — Missing blank line before @test
Missing blank line before testEmptyMessageIdDecodeErrorRetainsId — formatting inconsistency with the rest of the file.
Unify tenant/card resolution, transport validation, and effective-card context across REST, JSON-RPC, and gRPC while preserving transport-specific error ordering. Add the targeted compat-0.3 REST push-config canonicalization and JSON-RPC ID correctness fixes. This fixes a2aproject#1093
23dd672 to
072e123
Compare
ehsavoie
left a comment
There was a problem hiding this comment.
Breaking API change: Confirmed. RestHandler.getTask, listTasks, and listTaskPushNotificationConfigurations all changed from typed params (Integer, Boolean, int) to raw String. These are public methods, so any external subclass or caller breaks silently. The commit is labeled feat: instead of feat!: — that's a real oversight.
taskId validation repeated 7× in RestHandler and GrpcHandler canonicalTenant repeated ~10×
| * @param validatedCards set tracking which cards have already been validated | ||
| * @return the resolved agent card | ||
| */ | ||
| public static AgentCard resolveAndValidateOnce(Instance<AgentCard> agentCardInstance, |
| * @param validator validation logic to apply on first access | ||
| * @return the resolved agent card | ||
| */ | ||
| public static AgentCard resolveAndValidateOnce(Supplier<AgentCard> agentCardSupplier, |
| * @return the resolved agent card | ||
| * @throws IllegalStateException if neither card is available | ||
| */ | ||
| public static AgentCard resolveWithFallback(Instance<AgentCard> publicCard, |
| * | ||
| * @see #resolveWithFallback(Instance, Instance, AgentCardRouter, String, Set, Consumer) | ||
| */ | ||
| public static AgentCard resolveWithFallback(Instance<AgentCard> publicCard, |
|
|
||
| private static final Logger LOGGER = Logger.getLogger(JSONRPCHandler.class.getName()); | ||
|
|
||
| private static final AgentCardOperation STREAMING = new AgentCardOperation( |
There was a problem hiding this comment.
Duplicated in RestHandler and GrpcHandler
| private static final AgentCardOperation STREAMING = new AgentCardOperation( | ||
| card -> card.capabilities().streaming(), | ||
| () -> new UnsupportedOperationError(null, "Streaming is not supported by the agent", null)); | ||
| private static final AgentCardOperation PUSH_NOTIFICATIONS = new AgentCardOperation( |
There was a problem hiding this comment.
Duplicated in RestHandler and GrpcHandler with PUSH
| * @return {@code true} if the value is {@code null} or empty | ||
| */ | ||
| public static boolean isNullOrEmpty(@Nullable String value) { | ||
| return value == null || value.isEmpty(); |
There was a problem hiding this comment.
A taskId of " " passes all validation gates and hits the backend as a nonsense ID
return value == null || value.isBlank();
| } catch (JsonSyntaxException e) { | ||
| return new DecodeFailure(requestId, methodName, new JSONParseError_v0_3(e.getMessage())); | ||
| } catch (Throwable t) { | ||
| return new DecodeFailure(requestId, methodName, new InternalError_v0_3(t.getMessage())); |
There was a problem hiding this comment.
Should n't this be sanitized ?
|
|
||
| // Validate method field | ||
| com.google.gson.JsonElement methodElement = node.get("method"); | ||
| if (methodElement == null || !methodElement.isJsonPrimitive()) { |
There was a problem hiding this comment.
isJsonPrimitive() returns true for any JSON primitive — strings, booleans, and numbers alike. So "method": true passes the guard, getAsString() coerces it to the string "true", and the switch falls to default → MethodNotFoundError_v0_3 (-32601) instead of InvalidRequestError_v0_3 (-3260)
It should be:
if (methodElement == null || !methodElement.isJsonPrimitive()
|| !methodElement.getAsJsonPrimitive().isString()) {
throw new InvalidRequestError_v0_3("...");
}
| * <li>{@link IllegalStateException} if nothing resolves</li> | ||
| * </ol> | ||
| */ | ||
| private static AgentCard resolveDefaultWithRouterFallback(Instance<AgentCard> publicCard, |

This fixes #1093
BREAKING CHANGE
It changes how integrations interact with the transport/* handlers.
The public user API remains the same